import * as ts from 'typescript' ; import * as tsutils from 'tsutils/typeguard/3.0' ; import { VisitorContext } from './visitor-context' ; import * as VisitorUtils from './visitor-utils' ; import { setIntersection , setUnion } from './utils' ; function visitRegularObjectType ( type , visitorContext ) { const stringIndexType = visitorContext . checker . getIndexTypeOfType ( type , ts . IndexKind . String ) ; if ( stringIndexType ) { return true ; } const properties = visitorContext . checker . getPropertiesOfType ( type ) ; const propertiesInfo = properties . map ( ( property ) => VisitorUtils . getPropertyInfo ( type , property , visitorContext ) ) ; const propertiesName = propertiesInfo . map ( ( propertyInfo ) => propertyInfo . name ) ; return new Set ( propertiesName ) ; } function visitTupleObjectType ( ) { return false ; } function visitArrayObjectType ( ) { return false ; } function visitObjectType ( type , visitorContext ) { if ( tsutils . isTupleType ( type ) ) { return visitTupleObjectType ( ) ; } else if ( visitorContext . checker . getIndexTypeOfType ( type , ts . IndexKind . Number ) ) { return visitArrayObjectType ( ) ; } else { return visitRegularObjectType ( type , visitorContext ) ; } } function visitUnionOrIntersectionType ( type , visitorContext ) { const stringTypes = type . types . map ( ( type ) => visitType ( type , visitorContext ) ) ; if ( tsutils . isUnionType ( type ) ) { if ( stringTypes . some ( ( stringType ) => stringType === false ) ) { return false ; } if ( stringTypes . some ( ( stringType ) => stringType !== true ) ) { const stringSets = stringTypes . filter ( ( stringType ) => stringType !== true ) as Set < string > [ ] ; let strings = stringSets [ 0 ] ; for ( let i = 1 ; i < stringSets . length ; i ++ ) { strings = setIntersection ( strings , stringSets [ i ] ) ; } return strings ; } else { return true ; } } else { if ( stringTypes . some ( ( stringType ) => stringType === true ) ) { return true ; } if ( stringTypes . some ( ( stringType ) => stringType !== false ) ) { const stringSets = stringTypes . filter ( ( stringType ) => stringType !== false ) as Set < string > [ ] ; let strings = stringSets [ 0 ] ; for ( let i = 1 ; i < stringSets . length ; i ++ ) { strings = setUnion ( strings , stringSets [ i ] ) ; } return strings ; } else { return false ; } } } function visitIndexType ( ) { throw new Error ( 'Not yet implemented.' ) ; } function visitNonPrimitiveType ( ) { return false ; } function visitLiteralType ( type ) { if ( typeof type . value === 'string' ) { return false ; } else if ( typeof type . value === 'number' ) { return false ; } else { throw new Error ( 'Type value is expected to be a string or number.' ) ; } } function visitTypeReference ( type , visitorContext ) { const mapping = VisitorUtils . getTypeReferenceMapping ( type , visitorContext ) ; const previousTypeReference = visitorContext . previousTypeReference ; visitorContext . typeMapperStack . push ( mapping ) ; visitorContext . previousTypeReference = type ; const result = visitType ( type . target , visitorContext ) ; visitorContext . previousTypeReference = previousTypeReference ; visitorContext . typeMapperStack . pop ( ) ; return result ; } function visitTypeParameter ( type , visitorContext ) { const mappedType = VisitorUtils . getResolvedTypeParameter ( type , visitorContext ) ; if ( mappedType === undefined ) { throw new Error ( 'Unbound type parameter, missing type node.' ) ; } return visitType ( mappedType , visitorContext ) ; } function visitBigInt ( ) { return false ; } function visitBoolean ( ) { return false ; } function visitString ( ) { return false ; } function visitBooleanLiteral ( ) { return false ; } function visitNumber ( ) { return false ; } function visitUndefined ( ) { return false ; } function visitNull ( ) { return false ; } function visitNever ( ) { return false ; } function visitUnknown ( ) { return false ; } function visitAny ( ) { return true ; } export function visitType ( type , visitorContext ) { if ( ( ts . TypeFlags . Any & type . flags ) !== 0 ) { return visitAny ( ) ; } else if ( ( ts . TypeFlags . Unknown & type . flags ) !== 0 ) { return visitUnknown ( ) ; } else if ( ( ts . TypeFlags . Never & type . flags ) !== 0 ) { return visitNever ( ) ; } else if ( ( ts . TypeFlags . Null & type . flags ) !== 0 ) { return visitNull ( ) ; } else if ( ( ts . TypeFlags . Undefined & type . flags ) !== 0 ) { return visitUndefined ( ) ; } else if ( ( ts . TypeFlags . Number & type . flags ) !== 0 ) { return visitNumber ( ) ; } else if ( VisitorUtils . isBigIntType ( type ) ) { return visitBigInt ( ) ; } else if ( ( ts . TypeFlags . Boolean & type . flags ) !== 0 ) { return visitBoolean ( ) ; } else if ( ( ts . TypeFlags . String & type . flags ) !== 0 ) { return visitString ( ) ; } else if ( ( ts . TypeFlags . BooleanLiteral & type . flags ) !== 0 ) { return visitBooleanLiteral ( ) ; } else if ( tsutils . isTypeReference ( type ) && visitorContext . previousTypeReference !== type ) { return visitTypeReference ( type , visitorContext ) ; } else if ( ( ts . TypeFlags . TypeParameter & type . flags ) !== 0 ) { return visitTypeParameter ( type , visitorContext ) ; } else if ( tsutils . isObjectType ( type ) ) { return visitObjectType ( type , visitorContext ) ; } else if ( tsutils . isLiteralType ( type ) ) { return visitLiteralType ( type ) ; } else if ( tsutils . isUnionOrIntersectionType ( type ) ) { return visitUnionOrIntersectionType ( type , visitorContext ) ; } else if ( ( ts . TypeFlags . NonPrimitive & type . flags ) !== 0 ) { return visitNonPrimitiveType ( ) ; } else if ( ( ts . TypeFlags . Index & type . flags ) !== 0 ) { return visitIndexType ( ) ; } else if ( tsutils . isIndexedAccessType ( type ) ) { throw new Error ( 'Not yet implemented.' ) ; } else { throw new Error ( 'Could not generate type-check; unsupported type with flags: ' + type . flags ) ; } }