import ts from 'typescript' ; import * as guards from './guards' ; import * as symbol_ from './symbol' ; import * as utils from './utils' ; type TypedNode = ts . Node & { readonly type ? : ts . TypeNode ; } ; type MaybeTypedNode = ts . Node & { readonly type ? : ts . TypeNode ; } ; function getIntrinsicName ( type ) { return ( type as any ) . intrinsicName ; } export function getTypeNode ( node ) ; export function getTypeNode ( node ) ; export function getTypeNode ( node ) { return utils . getValueOrUndefined ( node . type ) ; } export function getTypeNodeOrThrow ( node ) { return utils . throwIfNullOrUndefined ( getTypeNode ( node ) , 'type node' ) ; } export function getContextualType ( typeChecker , node ) { return utils . getValueOrUndefined ( typeChecker . getContextualType ( node ) ) ; } export function getTypeFromTypeNode ( typeChecker , typeNode ) { return typeChecker . getTypeFromTypeNode ( typeNode ) ; } export function getType ( typeChecker , node ) { const typeNode = ts . isFunctionLike ( node ) ? undefined : ( getTypeNode ( node as any ) as ts . TypeNode | undefined ) ; if ( typeNode !== undefined ) { return typeChecker . getTypeFromTypeNode ( typeNode ) ; } const type = typeChecker . getTypeAtLocation ( node ) ; if ( isAny ( type ) && guards . isExpression ( node ) ) { const contextualType = getContextualType ( typeChecker , node ) ; if ( contextualType !== undefined && ! isAny ( contextualType ) ) { return contextualType ; } } return type ; } export function getConstraint ( type ) { return utils . getValueOrUndefined ( type . getConstraint ( ) ) ; } export function getTypeAtLocation ( typeChecker , symbol , node ) { return typeChecker . getTypeOfSymbolAtLocation ( symbol , node ) ; } export function typeToTypeNode ( typeChecker , type , node ? ) { return typeChecker . typeToTypeNode ( type , node ) ; } export function typeToTypeNodeOrThrow ( typeChecker , type , node ? ) { return utils . throwIfNullOrUndefined ( typeToTypeNode ( typeChecker , type , node ) , 'type node' ) ; } export function getSymbol ( type ) { return utils . getValueOrUndefined ( type . getSymbol ( ) ) ; } export function getAliasSymbol ( type ) { return utils . getValueOrUndefined ( type . aliasSymbol ) ; } export function getAliasTypeArguments ( type ) { return utils . getValueOrUndefined ( type . aliasTypeArguments ) ; } export function getAliasTypeArgumentsArray ( type ) { return utils . getArray ( getAliasTypeArguments ( type ) ) ; } export function getSymbolOrThrow ( type ) { return utils . throwIfNullOrUndefined ( getSymbol ( type ) , 'symbol' ) ; } function getDefaultTypeFormatFlags ( node ? ) { let formatFlags = ts . TypeFormatFlags . UseTypeOfFunction | ts . TypeFormatFlags . NoTruncation | ts . TypeFormatFlags . UseFullyQualifiedType | ts . TypeFormatFlags . WriteTypeArgumentsOfSignature ; if ( node !== undefined && node . kind === ts . SyntaxKind . TypeAliasDeclaration ) { formatFlags |= ts . TypeFormatFlags . InTypeAlias ; } return formatFlags ; } export function getProperties ( type ) { return type . getProperties ( ) ; } export function getConstructSignatures ( type ) { return type . getConstructSignatures ( ) ; } export function getProperty ( type , name ) { return utils . getValueOrUndefined ( type . getProperty ( name ) ) ; } export function getText ( typeChecker , type , node ? , flags = getDefaultTypeFormatFlags ( node ) ) { return typeChecker . typeToString ( type , node , flags ) ; } export function getBaseTypes ( type ) { return utils . getValueOrUndefined ( type . getBaseTypes ( ) ) ; } export function getBaseTypesArray ( type ) { return utils . getArray ( getBaseTypes ( type ) ) ; } function isTypeFlag ( type , flag ) { return ( type . flags & flag ) === flag ; } function isObjectFlag ( type , flag ) { return isObjectType ( type ) && ( type . objectFlags & flag ) === flag ; } function hasTypeFlag ( type , flag ) { return ( type . flags & flag ) !== 0 ; } export function getAllTypes ( type ) { const unionTypes = getUnionTypes ( type ) ; if ( unionTypes !== undefined ) { return unionTypes . reduce < readonly ts . Type [ ] > ( ( acc , unionType ) => acc . concat ( getAllTypes ( unionType ) ) , [ ] ) ; } const intersectionTypes = getIntersectionTypes ( type ) ; if ( intersectionTypes !== undefined ) { return intersectionTypes . reduce < readonly ts . Type [ ] > ( ( acc , unionType ) => acc . concat ( getAllTypes ( unionType ) ) , [ ] ) ; } return [ type ] ; } export function getTypes ( type , isType ) { if ( isType ( type ) ) { return [ type ] ; } const unionTypes = getUnionTypes ( type ) ; if ( unionTypes !== undefined ) { return unionTypes . reduce < readonly ts . Type [ ] > ( ( acc , unionType ) => acc . concat ( getTypes ( unionType , isType ) ) , [ ] ) ; } const intersectionTypes = getIntersectionTypes ( type ) ; if ( intersectionTypes !== undefined ) { return intersectionTypes . reduce < readonly ts . Type [ ] > ( ( acc , unionType ) => acc . concat ( getTypes ( unionType , isType ) ) , [ ] ) ; } return [ ] ; } export function isSymbolic ( type ) { return ! ( isPrimitiveish ( type ) || isIntersection ( type ) || isUnion ( type ) || isTuple ( type ) ) ; } export function isObjectType ( type ) { return isTypeFlag ( type , ts . TypeFlags . Object ) ; } export function isTypeReference ( type ) { return isObjectFlag ( type , ts . ObjectFlags . Reference ) ; } export function isTupleType ( type ) { return isObjectFlag ( type , ts . ObjectFlags . Tuple ) ; } export function isTuple ( type ) { return isTypeReference ( type ) && isTupleType ( type . target ) ; } export function hasTuple ( type ) { return hasType ( type , isTuple ) ; } export function getTupleTypes ( type ) { return getTypes ( type , isTuple ) ; } export function getTupleElements ( type ) { return isTuple ( type ) ? utils . getArray ( type . typeArguments ) : undefined ; } export function getTypeArguments ( type ) { return isTypeReference ( type ) ? utils . getValueOrUndefined ( type . typeArguments ) : undefined ; } export function getTypeArgumentsArray ( type ) { return utils . getArray ( getTypeArguments ( type ) ) ; } export function getTypeArgumentsOrThrow ( type ) { return utils . throwIfNullOrUndefined ( getTypeArguments ( type ) , 'type arguments' ) ; } export function isAny ( type ) { return hasTypeFlag ( type , ts . TypeFlags . Any ) ; } export function isErrorType ( type ) { return isAny ( type ) && getIntrinsicName ( type ) === 'error' ; } export function isUnion ( type ) { return ( type as any ) . isUnion === undefined ? false : type . isUnion ( ) ; } export function getUnionTypes ( type ) { return isUnion ( type ) ? utils . getArray ( type . types ) : undefined ; } export function getUnionTypesArray ( type ) { return utils . getArray ( getUnionTypes ( type ) ) ; } export function isIntersection ( type ) { return ( type as any ) . isIntersection === undefined ? false : type . isIntersection ( ) ; } export function getIntersectionTypes ( type ) { return isIntersection ( type ) ? utils . getArray ( type . types ) : undefined ; } export function getIntersectionTypesArray ( type ) { return utils . getArray ( getIntersectionTypes ( type ) ) ; } export function hasUnionType ( type , isType ) { const unionTypes = getUnionTypes ( type ) ; return unionTypes !== undefined && unionTypes . some ( isType ) ; } export function hasIntersectionType ( type , isType ) { const types = getIntersectionTypes ( type ) ; return types !== undefined && types . some ( isType ) ; } export function hasType ( type , isType ) { return isType ( type ) || hasUnionType ( type , isType ) || hasIntersectionType ( type , isType ) ; } export function isOnlyType ( type , isType ) { if ( isType ( type ) ) { return true ; } const unionTypes = getUnionTypes ( type ) ; if ( unionTypes !== undefined && unionTypes . every ( ( tpe ) => isOnlyType ( tpe , isType ) ) ) { return true ; } const intersectionTypes = getIntersectionTypes ( type ) ; if ( intersectionTypes !== undefined && intersectionTypes . every ( ( tpe ) => isOnlyType ( tpe , isType ) ) ) { return true ; } return false ; } export function isSame ( a , b ) { return ( a !== undefined && b !== undefined && ( a === b || ( isOnlyBooleanish ( a ) && isOnlyBooleanish ( b ) ) || ( isOnlyStringish ( a ) && isOnlyStringish ( b ) ) || ( isOnlyNumberish ( a ) && isOnlyNumberish ( b ) ) || ( isOnlySymbolish ( a ) && isOnlySymbolish ( b ) ) ) ) ; } export function isOnly ( type ) { return [ ... new Set ( getAllTypes ( type ) ) ] . length === 0 ; } export function isNull ( type ) { return isTypeFlag ( type , ts . TypeFlags . Null ) ; } export function isOnlyNull ( type ) { return isOnlyType ( type , isNull ) ; } export function hasNull ( type ) { return hasType ( type , isNull ) ; } export function isUndefined ( type ) { return isTypeFlag ( type , ts . TypeFlags . Undefined ) ; } export function isOnlyUndefined ( type ) { return isOnlyType ( type , isUndefined ) ; } export function hasUndefined ( type ) { return hasType ( type , isUndefined ) ; } export function isUndefinedish ( type ) { return isUndefined ( type ) || isVoid ( type ) ; } export function isOnlyUndefinedish ( type ) { return isOnlyType ( type , isUndefinedish ) ; } export function hasUndefinedish ( type ) { return hasType ( type , isUndefinedish ) ; } export function isNullable ( type ) { const types = getUnionTypes ( type ) ; return ( isNull ( type ) || isUndefined ( type ) || ( types !== undefined && types . some ( ( tpe ) => isNull ( tpe ) || isUndefined ( tpe ) ) ) ) ; } export function isNumber ( type ) { return isTypeFlag ( type , ts . TypeFlags . Number ) ; } export function isOnlyNumber ( type ) { return isOnlyType ( type , isNumber ) ; } export function hasNumber ( type ) { return hasType ( type , isNumber ) ; } export function isNumberLike ( type ) { return isTypeFlag ( type , ts . TypeFlags . NumberLike ) ; } export function isOnlyNumberLike ( type ) { return isOnlyType ( type , isNumberLike ) ; } export function hasNumberLike ( type ) { return hasType ( type , isNumberLike ) ; } export function isNumberLiteral ( type ) { return isTypeFlag ( type , ts . TypeFlags . NumberLiteral ) ; } export function isOnlyNumberLiteral ( type ) { return isOnlyType ( type , isNumberLiteral ) ; } export function hasNumberLiteral ( type ) { return hasType ( type , isNumberLiteral ) ; } export function isNumberish ( type ) { return hasTypeFlag ( type , ts . TypeFlags . NumberLike ) ; } export function isOnlyNumberish ( type ) { return isOnlyType ( type , isNumberish ) ; } export function hasNumberish ( type ) { return hasType ( type , isNumberish ) ; } export function isString ( type ) { return isTypeFlag ( type , ts . TypeFlags . String ) ; } export function isOnlyString ( type ) { return isOnlyType ( type , isString ) ; } export function hasString ( type ) { return hasType ( type , isString ) ; } export function isStringLike ( type ) { return isTypeFlag ( type , ts . TypeFlags . StringLike ) ; } export function isOnlyStringLike ( type ) { return isOnlyType ( type , isStringLike ) ; } export function hasStringLike ( type ) { return hasType ( type , isStringLike ) ; } export function isStringLiteral ( type ) { return isTypeFlag ( type , ts . TypeFlags . StringLiteral ) ; } export function isOnlyStringLiteral ( type ) { return isOnlyType ( type , isStringLiteral ) ; } export function hasStringLiteral ( type ) { return hasType ( type , isStringLiteral ) ; } export function isStringish ( type ) { return hasTypeFlag ( type , ts . TypeFlags . StringLike ) ; } export function isOnlyStringish ( type ) { return isOnlyType ( type , isStringish ) ; } export function hasStringish ( type ) { return hasType ( type , isStringish ) ; } export function isBoolean ( type ) { return isTypeFlag ( type , ts . TypeFlags . Boolean ) ; } export function isOnlyBoolean ( type ) { return isOnlyType ( type , isBoolean ) ; } export function hasBoolean ( type ) { return hasType ( type , isBoolean ) ; } export function isBooleanLike ( type ) { return isTypeFlag ( type , ts . TypeFlags . BooleanLike ) ; } export function isOnlyBooleanLike ( type ) { return isOnlyType ( type , isBooleanLike ) ; } export function hasBooleanLike ( type ) { return hasType ( type , isBooleanLike ) ; } export function isBooleanLiteral ( type ) { return isTypeFlag ( type , ts . TypeFlags . BooleanLiteral ) ; } export function isOnlyBooleanLiteral ( type ) { return isOnlyType ( type , isBooleanLiteral ) ; } export function hasBooleanLiteral ( type ) { return hasType ( type , isBooleanLiteral ) ; } export function isBooleanFalse ( type ) { return isTypeFlag ( type , ts . TypeFlags . BooleanLiteral ) && getIntrinsicName ( type ) === 'false' ; } export function isOnlyBooleanFalse ( type ) { return isOnlyType ( type , isBooleanFalse ) ; } export function hasBooleanFalse ( type ) { return hasType ( type , isBooleanFalse ) ; } export function isBooleanish ( type ) { return hasTypeFlag ( type , ts . TypeFlags . BooleanLike ) ; } export function isOnlyBooleanish ( type ) { return isOnlyType ( type , isBooleanish ) ; } export function hasBooleanish ( type ) { return hasType ( type , isBooleanish ) ; } export function isSymbol ( type ) { return isTypeFlag ( type , ts . TypeFlags . ESSymbol ) ; } export function isOnlySymbol ( type ) { return isOnlyType ( type , isSymbol ) ; } export function hasSymbol ( type ) { return hasType ( type , isSymbol ) ; } export function isSymbolLike ( type ) { return isTypeFlag ( type , ts . TypeFlags . ESSymbolLike ) ; } export function isOnlySymbolLike ( type ) { return isOnlyType ( type , isSymbolLike ) ; } export function hasSymbolLike ( type ) { return hasType ( type , isSymbolLike ) ; } export function isSymbolish ( type ) { return hasTypeFlag ( type , ts . TypeFlags . ESSymbolLike ) ; } export function isOnlySymbolish ( type ) { return isOnlyType ( type , isSymbolish ) ; } export function hasSymbolish ( type ) { return hasType ( type , isSymbolish ) ; } export function isPrimitive ( type ) { return isUndefined ( type ) || isNull ( type ) || isNumber ( type ) || isBoolean ( type ) || isString ( type ) || isSymbol ( type ) ; } export function isOnlyPrimitive ( type ) { return isOnlyType ( type , isPrimitive ) ; } export function hasPrimitive ( type ) { return hasType ( type , isPrimitive ) ; } export function isPrimitiveLike ( type ) { return ( isUndefined ( type ) || isNull ( type ) || isNumberLike ( type ) || isBooleanLike ( type ) || isStringLike ( type ) || isSymbolLike ( type ) ) ; } export function isOnlyPrimitiveLike ( type ) { return isOnlyType ( type , isPrimitiveLike ) ; } export function hasPrimitiveLike ( type ) { return hasType ( type , isPrimitiveLike ) ; } export function isPrimitiveish ( type ) { return ( isUndefined ( type ) || isNull ( type ) || isNumberish ( type ) || isBooleanish ( type ) || isStringish ( type ) || isSymbolish ( type ) || isVoidish ( type ) ) ; } export function isOnlyPrimitiveish ( type ) { return isOnlyType ( type , isPrimitiveish ) ; } export function hasPrimitiveish ( type ) { return hasType ( type , isPrimitive ) ; } export function isOnlyObject ( type ) { return isOnlyType ( type , ( value ) => ! isPrimitiveish ( value ) ) ; } export function isArray ( type ) { const typeSymbol = getSymbol ( type ) ; const typeArguments = getTypeArguments ( type ) ; if ( typeSymbol === undefined || typeArguments === undefined ) { return false ; } return ( ( symbol_ . getName ( typeSymbol ) === 'Array' || symbol_ . getName ( typeSymbol ) === 'ReadonlyArray' ) && typeArguments . length === 1 ) ; } export function isOnlyArray ( type ) { return isOnlyType ( type , isArray ) ; } export function hasArray ( type ) { return hasType ( type , isArray ) ; } export function isArrayish ( type ) { return isArray ( type ) || isTuple ( type ) ; } export function isOnlyArrayish ( type ) { return isOnlyType ( type , isArrayish ) ; } export function hasArrayish ( type ) { return hasType ( type , isArrayish ) ; } export function getArrayType ( type ) { if ( ! isArray ( type ) ) { return undefined ; } const typeArguments = getTypeArgumentsOrThrow ( type ) ; return typeArguments [ 0 ] ; } export function getArrayTypeOrThrow ( type ) { return utils . throwIfNullOrUndefined ( getArrayType ( type ) , 'array type' ) ; } export function getArrayTypes ( type ) { return getTypes ( type , isArray ) ; } export function isVoid ( type ) { return isTypeFlag ( type , ts . TypeFlags . Void ) ; } export function isOnlyVoid ( type ) { return isOnlyType ( type , isVoid ) ; } export function hasVoid ( type ) { return hasType ( type , isVoid ) ; } export function isVoidish ( type ) { return isVoid ( type ) || isUndefined ( type ) ; } export function isOnlyVoidish ( type ) { return isOnlyType ( type , isVoidish ) ; } export function hasVoidish ( type ) { return hasType ( type , isVoidish ) ; } export function getCallSignatures ( type ) { return type . getCallSignatures ( ) ; } export function getNonNullableType ( type ) { return type . getNonNullableType ( ) ; } export function filterUnion ( checker , type , filter ) { const types = getUnionTypes ( type ) ; if ( types === undefined ) { return type ; } return ( checker as any ) . getUnionType ( types . filter ( filter ) ) ; }