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 * as VisitorTypeName from './visitor-type-name' ; import { getIntrinsicName } from './visitor-utils' ; function visitUnionOrIntersectionType ( type , visitorContext ) { const name = VisitorTypeName . visitType ( type , visitorContext , { type : 'keyof' } ) ; return VisitorUtils . setFunctionIfNotExists ( name , visitorContext , ( ) => { const functionNames = type . types . map ( ( type ) => visitType ( type , visitorContext ) ) ; if ( tsutils . isUnionType ( type ) ) { return VisitorUtils . createConjunctionFunction ( functionNames , name ) ; } else { return VisitorUtils . createDisjunctionFunction ( functionNames , name , visitorContext ) ; } } ) ; } function visitIndexType ( visitorContext ) { return VisitorUtils . getNeverFunction ( visitorContext ) ; } function visitNonPrimitiveType ( type , visitorContext ) { const intrinsicName = getIntrinsicName ( type ) ; if ( intrinsicName === 'object' ) { return VisitorUtils . getNeverFunction ( visitorContext ) ; } else { throw new Error ( `Unsupported non-primitive with intrinsic name: ${ intrinsicName }.` ) ; } } function visitLiteralType ( visitorContext ) { return VisitorUtils . getNeverFunction ( visitorContext ) ; } function visitRegularObjectType ( type , visitorContext ) { const stringIndexType = visitorContext . checker . getIndexTypeOfType ( type , ts . IndexKind . String ) ; if ( stringIndexType ) { return VisitorUtils . getStringFunction ( visitorContext ) ; } else { const name = VisitorTypeName . visitType ( type , visitorContext , { type : 'keyof' } ) ; return VisitorUtils . setFunctionIfNotExists ( name , visitorContext , ( ) => { const properties = visitorContext . checker . getPropertiesOfType ( type ) ; const names = properties . map ( ( property ) => property . name ) ; const condition = VisitorUtils . createBinaries ( names . map ( ( name ) => ts . createStrictInequality ( VisitorUtils . objectIdentifier , ts . createStringLiteral ( name ) ) ) , ts . SyntaxKind . AmpersandAmpersandToken , ts . createTrue ( ) ) ; return VisitorUtils . createAssertionFunction ( condition , { type : 'object-keyof' , properties : names } , name , visitorContext ) ; } ) ; } } function visitTupleObjectType ( visitorContext ) { return VisitorUtils . getNumberFunction ( visitorContext ) ; } function visitArrayObjectType ( visitorContext ) { return VisitorUtils . getNumberFunction ( visitorContext ) ; } function visitObjectType ( type , visitorContext ) { if ( tsutils . isTupleType ( type ) ) { return visitTupleObjectType ( visitorContext ) ; } else if ( visitorContext . checker . getIndexTypeOfType ( type , ts . IndexKind . Number ) ) { return visitArrayObjectType ( visitorContext ) ; } else { return visitRegularObjectType ( type , visitorContext ) ; } } 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 visitBoolean ( visitorContext ) { return VisitorUtils . getNeverFunction ( visitorContext ) ; } function visitString ( visitorContext ) { return VisitorUtils . getNeverFunction ( visitorContext ) ; } function visitBooleanLiteral ( visitorContext ) { return VisitorUtils . getNeverFunction ( visitorContext ) ; } function visitBigInt ( visitorContext ) { return VisitorUtils . getNeverFunction ( visitorContext ) ; } function visitNumber ( visitorContext ) { return VisitorUtils . getNeverFunction ( visitorContext ) ; } function visitUndefined ( visitorContext ) { return VisitorUtils . getNeverFunction ( visitorContext ) ; } function visitNull ( visitorContext ) { return VisitorUtils . getNeverFunction ( visitorContext ) ; } function visitNever ( visitorContext ) { return VisitorUtils . getNeverFunction ( visitorContext ) ; } function visitUnknown ( visitorContext ) { return VisitorUtils . getNeverFunction ( visitorContext ) ; } function visitAny ( visitorContext ) { return VisitorUtils . getStringFunction ( visitorContext ) ; } export function visitType ( type , visitorContext ) { if ( ( ts . TypeFlags . Any & type . flags ) !== 0 ) { return visitAny ( visitorContext ) ; } else if ( ( ts . TypeFlags . Unknown & type . flags ) !== 0 ) { return visitUnknown ( visitorContext ) ; } else if ( ( ts . TypeFlags . Never & type . flags ) !== 0 ) { return visitNever ( visitorContext ) ; } else if ( ( ts . TypeFlags . Null & type . flags ) !== 0 ) { return visitNull ( visitorContext ) ; } else if ( ( ts . TypeFlags . Undefined & type . flags ) !== 0 ) { return visitUndefined ( visitorContext ) ; } else if ( ( ts . TypeFlags . Number & type . flags ) !== 0 ) { return visitNumber ( visitorContext ) ; } else if ( VisitorUtils . isBigIntType ( type ) ) { return visitBigInt ( visitorContext ) ; } else if ( ( ts . TypeFlags . Boolean & type . flags ) !== 0 ) { return visitBoolean ( visitorContext ) ; } else if ( ( ts . TypeFlags . String & type . flags ) !== 0 ) { return visitString ( visitorContext ) ; } else if ( ( ts . TypeFlags . BooleanLiteral & type . flags ) !== 0 ) { return visitBooleanLiteral ( visitorContext ) ; } 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 ( visitorContext ) ; } else if ( tsutils . isUnionOrIntersectionType ( type ) ) { return visitUnionOrIntersectionType ( type , visitorContext ) ; } else if ( ( ts . TypeFlags . NonPrimitive & type . flags ) !== 0 ) { return visitNonPrimitiveType ( type , visitorContext ) ; } else if ( ( ts . TypeFlags . Index & type . flags ) !== 0 ) { return visitIndexType ( visitorContext ) ; } 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 ) ; } }