import { __String, AccessExpression, AccessFlags, AccessorDeclaration, addRange, addRelatedInfo, addSyntheticLeadingComment, addSyntheticTrailingComment, AliasDeclarationNode, AllAccessorDeclarations, AmbientModuleDeclaration, and, AnonymousType, AnyImportOrJsDocImport, AnyImportOrReExport, append, appendIfUnique, ArrayBindingPattern, arrayFrom, arrayIsEqualTo, arrayIsHomogeneous, ArrayLiteralExpression, arrayOf, arrayToMultiMap, ArrayTypeNode, ArrowFunction, AsExpression, AssertionExpression, AssignmentDeclarationKind, AssignmentKind, AssignmentPattern, AwaitExpression, BaseType, BigIntLiteral, BigIntLiteralType, BinaryExpression, BinaryOperator, BinaryOperatorToken, binarySearch, BindableObjectDefinePropertyCall, BindableStaticNameExpression, BindingElement, BindingElementGrandparent, BindingName, BindingPattern, bindSourceFile, Block, BooleanLiteral, BreakOrContinueStatement, CallChain, CallExpression, CallLikeExpression, CallSignatureDeclaration, CancellationToken, canHaveDecorators, canHaveExportModifier, canHaveFlowNode, canHaveIllegalDecorators, canHaveIllegalModifiers, canHaveJSDoc, canHaveLocals, canHaveModifiers, canHaveModuleSpecifier, canHaveSymbol, canIncludeBindAndCheckDiagnostics, canUsePropertyAccess, cartesianProduct, CaseBlock, CaseClause, CaseOrDefaultClause, cast, chainDiagnosticMessages, CharacterCodes, CheckFlags, ClassDeclaration, ClassElement, classElementOrClassElementParameterIsDecorated, ClassExpression, ClassLikeDeclaration, classOrConstructorParameterIsDecorated, ClassStaticBlockDeclaration, clear, compareDiagnostics, comparePaths, compareValues, Comparison, CompilerOptions, ComputedPropertyName, concatenate, concatenateDiagnosticMessageChains, ConditionalExpression, ConditionalRoot, ConditionalType, ConditionalTypeNode, ConstructorDeclaration, ConstructorTypeNode, ConstructSignatureDeclaration, contains, containsParseError, ContextFlags, copyEntries, countWhere, createBinaryExpressionTrampoline, createCompilerDiagnostic, createDetachedDiagnostic, createDiagnosticCollection, createDiagnosticForFileFromMessageChain, createDiagnosticForNode, createDiagnosticForNodeArray, createDiagnosticForNodeArrayFromMessageChain, createDiagnosticForNodeFromMessageChain, createDiagnosticMessageChainFromDiagnostic, createEmptyExports, createEvaluator, createFileDiagnostic, createFlowNode, createGetSymbolWalker, createModeAwareCacheKey, createModeMismatchDetails, createModuleNotFoundChain, createMultiMap, createNameResolver, createPrinterWithDefaults, createPrinterWithRemoveComments, createPrinterWithRemoveCommentsNeverAsciiEscape, createPrinterWithRemoveCommentsOmitTrailingSemicolon, createPropertyNameNodeForIdentifierOrLiteral, createScanner, createSymbolTable, createSyntacticTypeNodeBuilder, createTextWriter, Debug, Declaration, DeclarationName, declarationNameToString, DeclarationStatement, DeclarationWithTypeParameterChildren, DeclarationWithTypeParameters, Decorator, deduplicate, DefaultClause, defaultMaximumTruncationLength, DeferredTypeReference, DeleteExpression, Diagnostic, DiagnosticAndArguments, DiagnosticArguments, DiagnosticCategory, DiagnosticMessage, DiagnosticMessageChain, DiagnosticRelatedInformation, Diagnostics, DiagnosticWithLocation, DoStatement, DynamicNamedDeclaration, ElementAccessChain, ElementAccessExpression, ElementFlags, EmitFlags, EmitHint, emitModuleKindIsNonNodeESM, EmitResolver, EmitTextWriter, emptyArray, endsWith, EntityName, EntityNameExpression, EntityNameOrEntityNameExpression, entityNameToString, EnumDeclaration, EnumMember, EnumType, equateValues, escapeLeadingUnderscores, escapeString, EvaluatorResult, evaluatorResult, every, EvolvingArrayType, ExclamationToken, ExportAssignment, exportAssignmentIsAlias, ExportDeclaration, ExportSpecifier, Expression, expressionResultIsUnused, ExpressionStatement, ExpressionWithTypeArguments, Extension, ExternalEmitHelpers, externalHelpersModuleNameText, factory, fileExtensionIs, fileExtensionIsOneOf, filter, find, findAncestor, findBestPatternMatch, findConstructorDeclaration, findIndex, findLast, findLastIndex, findUseStrictPrologue, first, firstDefined, firstIterator, firstOrUndefined, firstOrUndefinedIterator, flatMap, flatten, FlowArrayMutation, FlowAssignment, FlowCall, FlowCondition, FlowFlags, FlowLabel, FlowNode, FlowReduceLabel, FlowStart, FlowSwitchClause, FlowSwitchClauseData, FlowType, forEach, forEachChild, forEachChildRecursively, forEachEnclosingBlockScopeContainer, forEachEntry, forEachKey, forEachReturnStatement, forEachYieldExpression, ForInOrOfStatement, ForInStatement, formatMessage, ForOfStatement, ForStatement, FreshableIntrinsicType, FreshableType, FreshObjectLiteralType, FunctionDeclaration, FunctionExpression, FunctionFlags, FunctionLikeDeclaration, FunctionOrConstructorTypeNode, FunctionTypeNode, GenericType, GetAccessorDeclaration, getAliasDeclarationFromName, getAllJSDocTags, getAllowSyntheticDefaultImports, getAncestor, getAssignedExpandoInitializer, getAssignmentDeclarationKind, getAssignmentDeclarationPropertyAccessKind, getAssignmentTargetKind, getCanonicalDiagnostic, getCheckFlags, getClassExtendsHeritageElement, getClassLikeDeclarationOfSymbol, getCombinedLocalAndExportSymbolFlags, getCombinedModifierFlags, getCombinedNodeFlags, getContainingClass, getContainingClassExcludingClassDecorators, getContainingClassStaticBlock, getContainingFunction, getContainingFunctionOrClassStaticBlock, getDeclarationModifierFlagsFromSymbol, getDeclarationOfKind, getDeclarationsOfKind, getDeclaredExpandoInitializer, getDecorators, getDirectoryPath, getEffectiveBaseTypeNode, getEffectiveConstraintOfTypeParameter, getEffectiveContainerForJSDocTemplateTag, getEffectiveImplementsTypeNodes, getEffectiveInitializer, getEffectiveJSDocHost, getEffectiveModifierFlags, getEffectiveReturnTypeNode, getEffectiveSetAccessorTypeAnnotationNode, getEffectiveTypeAnnotationNode, getEffectiveTypeParameterDeclarations, getElementOrPropertyAccessName, getEmitDeclarations, getEmitFlags, getEmitModuleKind, getEmitModuleResolutionKind, getEmitScriptTarget, getEmitStandardClassFields, getEnclosingBlockScopeContainer, getEnclosingContainer, getEntityNameFromTypeNode, getErrorSpanForNode, getEscapedTextOfIdentifierOrLiteral, getEscapedTextOfJsxAttributeName, getEscapedTextOfJsxNamespacedName, getESModuleInterop, getExpandoInitializer, getExportAssignmentExpression, getExternalModuleImportEqualsDeclarationExpression, getExternalModuleName, getExternalModuleRequireArgument, getFirstConstructorWithBody, getFirstIdentifier, getFunctionFlags, getHostSignatureFromJSDoc, getIdentifierGeneratedImportReference, getIdentifierTypeArguments, getImmediatelyInvokedFunctionExpression, getInitializerOfBinaryExpression, getInterfaceBaseTypeNodes, getInvokedExpression, getIsolatedModules, getJSDocClassTag, getJSDocDeprecatedTag, getJSDocEnumTag, getJSDocHost, getJSDocOverloadTags, getJSDocParameterTags, getJSDocRoot, getJSDocSatisfiesExpressionType, getJSDocTags, getJSDocThisTag, getJSDocType, getJSDocTypeAssertionType, getJSDocTypeParameterDeclarations, getJSDocTypeTag, getJSXImplicitImportBase, getJSXRuntimeImport, getJSXTransformEnabled, getLeftmostAccessExpression, getLineAndCharacterOfPosition, getMembersOfDeclaration, getModifiers, getModuleInstanceState, getNameFromImportAttribute, getNameFromIndexInfo, getNameOfDeclaration, getNameOfExpando, getNamespaceDeclarationNode, getNewTargetContainer, getNonAugmentationDeclaration, getNonModifierTokenPosOfNode, getNormalizedAbsolutePath, getObjectFlags, getOriginalNode, getOrUpdate, getParameterSymbolFromJSDoc, getParseTreeNode, getPropertyAssignmentAliasLikeExpression, getPropertyNameForPropertyNameNode, getPropertyNameFromType, getResolutionDiagnostic, getResolutionModeOverride, getResolveJsonModule, getRestParameterElementType, getRootDeclaration, getScriptTargetFeatures, getSelectedEffectiveModifierFlags, getSemanticJsxChildren, getSetAccessorValueParameter, getSingleVariableOfVariableStatement, getSourceFileOfModule, getSourceFileOfNode, getSpanOfTokenAtPosition, getSpellingSuggestion, getStrictOptionValue, getSuperContainer, getSymbolNameForPrivateIdentifier, getTextOfIdentifierOrLiteral, getTextOfJSDocComment, getTextOfJsxAttributeName, getTextOfNode, getTextOfPropertyName, getThisContainer, getThisParameter, getTrailingSemicolonDeferringWriter, getTypeParameterFromJsDoc, getUseDefineForClassFields, group, hasAbstractModifier, hasAccessorModifier, hasAmbientModifier, hasContextSensitiveParameters, HasDecorators, hasDecorators, hasDynamicName, hasEffectiveModifier, hasEffectiveModifiers, hasEffectiveReadonlyModifier, HasExpressionInitializer, hasExtension, HasIllegalDecorators, HasIllegalModifiers, hasInferredType, HasInitializer, hasInitializer, hasJSDocNodes, hasJSDocParameterTags, hasJsonModuleEmitEnabled, HasLocals, HasModifiers, hasOnlyExpressionInitializer, hasOverrideModifier, hasPossibleExternalModuleReference, hasQuestionToken, hasResolutionModeOverride, hasRestParameter, hasScopeMarker, hasStaticModifier, hasSyntacticModifier, hasSyntacticModifiers, hasType, HeritageClause, Identifier, identifierToKeywordKind, IdentifierTypePredicate, idText, IfStatement, ImportAttribute, ImportAttributes, ImportCall, ImportClause, ImportDeclaration, ImportEqualsDeclaration, ImportOrExportSpecifier, ImportSpecifier, ImportTypeNode, IndexedAccessType, IndexedAccessTypeNode, IndexFlags, IndexInfo, IndexKind, indexOfNode, IndexSignatureDeclaration, IndexType, indicesOf, InferenceContext, InferenceFlags, InferenceInfo, InferencePriority, InferTypeNode, InstanceofExpression, InstantiableType, InstantiationExpressionType, InterfaceDeclaration, InterfaceType, InterfaceTypeWithDeclaredMembers, InternalNodeBuilderFlags, InternalSymbolName, IntersectionFlags, IntersectionType, IntersectionTypeNode, intrinsicTagNameToString, IntrinsicType, introducesArgumentsExoticObject, isAccessExpression, isAccessor, isAliasableExpression, isAmbientModule, isArray, isArrayBindingPattern, isArrayLiteralExpression, isArrowFunction, isAssertionExpression, isAssignmentDeclaration, isAssignmentExpression, isAssignmentOperator, isAssignmentPattern, isAssignmentTarget, isAutoAccessorPropertyDeclaration, isAwaitExpression, isBinaryExpression, isBinaryLogicalOperator, isBindableObjectDefinePropertyCall, isBindableStaticElementAccessExpression, isBindableStaticNameExpression, isBindingElement, isBindingElementOfBareOrAccessedRequire, isBindingPattern, isBlock, isBlockOrCatchScoped, isBlockScopedContainerTopLevel, isBooleanLiteral, isCallChain, isCallExpression, isCallLikeExpression, isCallLikeOrFunctionLikeExpression, isCallOrNewExpression, isCallSignatureDeclaration, isCatchClause, isCatchClauseVariableDeclaration, isCatchClauseVariableDeclarationOrBindingElement, isCheckJsEnabledForFile, isClassDeclaration, isClassElement, isClassExpression, isClassInstanceProperty, isClassLike, isClassStaticBlockDeclaration, isCommaSequence, isCommonJsExportedExpression, isCommonJsExportPropertyAssignment, isCompoundAssignment, isComputedNonLiteralName, isComputedPropertyName, isConditionalTypeNode, isConstAssertion, isConstructorDeclaration, isConstructorTypeNode, isConstructSignatureDeclaration, isConstTypeReference, isDeclaration, isDeclarationFileName, isDeclarationName, isDeclarationReadonly, isDecorator, isDefaultedExpandoInitializer, isDeleteTarget, isDottedName, isDynamicName, isEffectiveExternalModule, isElementAccessExpression, isEntityName, isEntityNameExpression, isEnumConst, isEnumDeclaration, isEnumMember, isExclusivelyTypeOnlyImportOrExport, isExpandoPropertyDeclaration, isExportAssignment, isExportDeclaration, isExportsIdentifier, isExportSpecifier, isExpression, isExpressionNode, isExpressionOfOptionalChainRoot, isExpressionStatement, isExpressionWithTypeArguments, isExpressionWithTypeArgumentsInClassExtendsClause, isExternalModule, isExternalModuleAugmentation, isExternalModuleImportEqualsDeclaration, isExternalModuleIndicator, isExternalModuleNameRelative, isExternalModuleReference, isExternalModuleSymbol, isExternalOrCommonJsModule, isForInOrOfStatement, isForInStatement, isForOfStatement, isForStatement, isFunctionDeclaration, isFunctionExpression, isFunctionExpressionOrArrowFunction, isFunctionLike, isFunctionLikeDeclaration, isFunctionLikeOrClassStaticBlockDeclaration, isFunctionOrModuleBlock, isFunctionTypeNode, isGeneratedIdentifier, isGetAccessor, isGetAccessorDeclaration, isGetOrSetAccessorDeclaration, isGlobalScopeAugmentation, isGlobalSourceFile, isHeritageClause, isIdentifier, isIdentifierText, isIdentifierTypePredicate, isIdentifierTypeReference, isIfStatement, isImportAttributes, isImportCall, isImportClause, isImportDeclaration, isImportEqualsDeclaration, isImportKeyword, isImportOrExportSpecifier, isImportSpecifier, isImportTypeNode, isInCompoundLikeAssignment, isIndexedAccessTypeNode, isInExpressionContext, isInfinityOrNaNString, isInitializedProperty, isInJSDoc, isInJSFile, isInJsonFile, isInstanceOfExpression, isInterfaceDeclaration, isInternalModuleImportEqualsDeclaration, isInTopLevelContext, isIntrinsicJsxName, isInTypeQuery, isIterationStatement, isJSDocAllType, isJSDocAugmentsTag, isJSDocCallbackTag, isJSDocConstructSignature, isJSDocFunctionType, isJSDocImportTag, isJSDocIndexSignature, isJSDocLinkLike, isJSDocMemberName, isJSDocNameReference, isJSDocNode, isJSDocNonNullableType, isJSDocNullableType, isJSDocOptionalParameter, isJSDocOptionalType, isJSDocOverloadTag, isJSDocParameterTag, isJSDocPropertyLikeTag, isJSDocPropertyTag, isJSDocSatisfiesExpression, isJSDocSatisfiesTag, isJSDocSignature, isJSDocTemplateTag, isJSDocThisTag, isJSDocTypeAlias, isJSDocTypeAssertion, isJSDocTypedefTag, isJSDocTypeExpression, isJSDocTypeLiteral, isJSDocUnknownType, isJSDocVariadicType, isJsonSourceFile, isJsxAttribute, isJsxAttributeLike, isJsxAttributes, isJsxElement, isJsxNamespacedName, isJsxOpeningElement, isJsxOpeningFragment, isJsxOpeningLikeElement, isJsxSelfClosingElement, isJsxSpreadAttribute, isJSXTagName, isKnownSymbol, isLateVisibilityPaintedStatement, isLeftHandSideExpression, isLineBreak, isLiteralComputedPropertyDeclarationName, isLiteralExpression, isLiteralExpressionOfObject, isLiteralImportTypeNode, isLiteralTypeNode, isLogicalOrCoalescingBinaryExpression, isLogicalOrCoalescingBinaryOperator, isMappedTypeNode, isMetaProperty, isMethodDeclaration, isMethodSignature, isModifier, isModuleBlock, isModuleDeclaration, isModuleExportsAccessExpression, isModuleIdentifier, isModuleOrEnumDeclaration, isModuleWithStringLiteralName, isNamedDeclaration, isNamedEvaluationSource, isNamedExports, isNamedTupleMember, isNamespaceExport, isNamespaceExportDeclaration, isNamespaceReexportDeclaration, isNewExpression, isNodeDescendantOf, isNonNullAccess, isNonNullExpression, isNumericLiteral, isNumericLiteralName, isObjectBindingPattern, isObjectLiteralElementLike, isObjectLiteralExpression, isObjectLiteralMethod, isObjectLiteralOrClassExpressionMethodOrAccessor, isOmittedExpression, isOptionalChain, isOptionalChainRoot, isOptionalDeclaration, isOptionalJSDocPropertyLikeTag, isOptionalTypeNode, isOutermostOptionalChain, isParameter, isParameterPropertyDeclaration, isParenthesizedExpression, isParenthesizedTypeNode, isPartOfParameterDeclaration, isPartOfTypeNode, isPartOfTypeQuery, isPlainJsFile, isPrefixUnaryExpression, isPrivateIdentifier, isPrivateIdentifierClassElementDeclaration, isPrivateIdentifierPropertyAccessExpression, isPropertyAccessEntityNameExpression, isPropertyAccessExpression, isPropertyAccessOrQualifiedName, isPropertyAccessOrQualifiedNameOrImportTypeNode, isPropertyAssignment, isPropertyDeclaration, isPropertyName, isPropertyNameLiteral, isPropertySignature, isPrototypeAccess, isPrototypePropertyAssignment, isPushOrUnshiftIdentifier, isQualifiedName, isRequireCall, isRestParameter, isRestTypeNode, isRightSideOfAccessExpression, isRightSideOfInstanceofExpression, isRightSideOfQualifiedNameOrPropertyAccess, isRightSideOfQualifiedNameOrPropertyAccessOrJSDocMemberName, isSameEntityName, isSatisfiesExpression, isSetAccessor, isSetAccessorDeclaration, isShorthandAmbientModuleSymbol, isShorthandPropertyAssignment, isSideEffectImport, isSingleOrDoubleQuote, isSourceFile, isSourceFileJS, isSpreadAssignment, isSpreadElement, isStatement, isStatementWithLocals, isStatic, isString, isStringANonContextualKeyword, isStringLiteral, isStringLiteralLike, isStringOrNumericLiteralLike, isSuperCall, isSuperProperty, isTaggedTemplateExpression, isTemplateSpan, isThisContainerOrFunctionBlock, isThisIdentifier, isThisInitializedDeclaration, isThisInitializedObjectBindingExpression, isThisInTypeQuery, isThisProperty, isThisTypeNode, isThisTypeParameter, isThisTypePredicate, isTransientSymbol, isTupleTypeNode, isTypeAlias, isTypeAliasDeclaration, isTypeDeclaration, isTypeLiteralNode, isTypeNode, isTypeNodeKind, isTypeOfExpression, isTypeOnlyImportDeclaration, isTypeOnlyImportOrExportDeclaration, isTypeOperatorNode, isTypeParameterDeclaration, isTypePredicateNode, isTypeQueryNode, isTypeReferenceNode, isTypeReferenceType, isTypeUsableAsPropertyName, isUMDExportSymbol, isValidBigIntString, isValidESSymbolDeclaration, isValidTypeOnlyAliasUseSite, isValueSignatureDeclaration, isVariableDeclaration, isVariableDeclarationInitializedToBareOrAccessedRequire, isVariableDeclarationInVariableStatement, isVariableDeclarationList, isVariableLike, isVariableLikeOrAccessor, isVariableStatement, isWriteAccess, isWriteOnlyAccess, IterableOrIteratorType, IterationTypes, JSDoc, JSDocAugmentsTag, JSDocCallbackTag, JSDocComment, JSDocFunctionType, JSDocImplementsTag, JSDocImportTag, JSDocLink, JSDocLinkCode, JSDocLinkPlain, JSDocMemberName, JSDocNullableType, JSDocOptionalType, JSDocOverloadTag, JSDocParameterTag, JSDocPrivateTag, JSDocPropertyLikeTag, JSDocPropertyTag, JSDocProtectedTag, JSDocPublicTag, JSDocSatisfiesTag, JSDocSignature, JSDocTemplateTag, JSDocThisTag, JSDocTypeAssertion, JSDocTypedefTag, JSDocTypeExpression, JSDocTypeLiteral, JSDocTypeReferencingNode, JSDocTypeTag, JSDocVariadicType, JsxAttribute, JsxAttributeLike, JsxAttributeName, JsxAttributes, JsxAttributeValue, JsxChild, JsxClosingElement, JsxElement, JsxEmit, JsxExpression, JsxFlags, JsxFragment, JsxNamespacedName, JsxOpeningElement, JsxOpeningFragment, JsxOpeningLikeElement, JsxReferenceKind, JsxSelfClosingElement, JsxSpreadAttribute, JsxTagNameExpression, KeywordTypeNode, LabeledStatement, LanguageFeatureMinimumTarget, last, lastOrUndefined, LateBoundBinaryExpressionDeclaration, LateBoundDeclaration, LateBoundName, LateVisibilityPaintedStatement, LazyNodeCheckFlags, length, LiteralExpression, LiteralType, LiteralTypeNode, map, mapDefined, MappedSymbol, MappedType, MappedTypeNode, MatchingKeys, maybeBind, MemberOverrideStatus, MetaProperty, MethodDeclaration, MethodSignature, minAndMax, MinusToken, Modifier, ModifierFlags, modifiersToFlags, modifierToFlag, ModuleBlock, ModuleDeclaration, ModuleExportName, moduleExportNameIsDefault, moduleExportNameTextEscaped, moduleExportNameTextUnescaped, ModuleInstanceState, ModuleKind, ModuleResolutionKind, ModuleSpecifierResolutionHost, Mutable, NamedDeclaration, NamedExports, NamedImportsOrExports, NamedTupleMember, NamespaceDeclaration, NamespaceExport, NamespaceExportDeclaration, NamespaceImport, needsScopeMarker, NewExpression, Node, NodeArray, NodeBuilderFlags, nodeCanBeDecorated, NodeCheckFlags, NodeFlags, nodeHasName, nodeIsMissing, nodeIsPresent, nodeIsSynthesized, NodeLinks, nodeStartsNewLexicalEnvironment, NodeWithTypeArguments, NonNullChain, NonNullExpression, NoSubstitutionTemplateLiteral, not, noTruncationMaximumTruncationLength, NumberLiteralType, NumericLiteral, objectAllocator, ObjectBindingPattern, ObjectFlags, ObjectFlagsType, ObjectLiteralElementLike, ObjectLiteralExpression, ObjectType, OptionalChain, OptionalTypeNode, or, orderedRemoveItemAt, OuterExpressionKinds, ParameterDeclaration, parameterIsThisKeyword, ParameterPropertyDeclaration, ParenthesizedExpression, ParenthesizedTypeNode, parseIsolatedEntityName, parseNodeFactory, parsePseudoBigInt, parseValidBigInt, Path, pathIsRelative, PatternAmbientModule, PlusToken, PostfixUnaryExpression, PredicateSemantics, PrefixUnaryExpression, PrivateIdentifier, Program, PromiseOrAwaitableType, PropertyAccessChain, PropertyAccessEntityNameExpression, PropertyAccessExpression, PropertyAssignment, PropertyDeclaration, PropertyName, PropertySignature, PseudoBigInt, pseudoBigIntToString, PunctuationSyntaxKind, pushIfUnique, QualifiedName, QuestionToken, rangeEquals, rangeOfNode, rangeOfTypeParameters, ReadonlyKeyword, reduceLeft, RegularExpressionLiteral, RelationComparisonResult, relativeComplement, removeExtension, removePrefix, replaceElement, resolutionExtensionIsTSOrJson, ResolutionMode, ResolvedModuleFull, ResolvedType, resolvingEmptyArray, RestTypeNode, ReturnStatement, ReverseMappedSymbol, ReverseMappedType, sameMap, SatisfiesExpression, Scanner, scanTokenAtPosition, ScriptKind, ScriptTarget, SetAccessorDeclaration, setCommentRange as setCommentRangeWorker, setEmitFlags, setIdentifierTypeArguments, setNodeFlags, setOriginalNode, setParent, setSyntheticLeadingComments, setTextRange as setTextRangeWorker, setTextRangePosEnd, setValueDeclaration, ShorthandPropertyAssignment, shouldAllowImportingTsExtension, shouldPreserveConstEnums, Signature, SignatureDeclaration, SignatureFlags, SignatureKind, singleElementArray, SingleSignatureType, skipOuterExpressions, skipParentheses, skipTrivia, skipTypeChecking, skipTypeParentheses, some, SourceFile, SpreadAssignment, SpreadElement, startsWith, Statement, StringLiteral, StringLiteralLike, StringLiteralType, StringMappingType, stripQuotes, StructuredType, SubstitutionType, SuperCall, SwitchStatement, Symbol, SymbolAccessibility, SymbolAccessibilityResult, SymbolFlags, SymbolFormatFlags, SymbolId, SymbolLinks, symbolName, SymbolTable, SymbolTracker, SymbolVisibilityResult, SyntaxKind, SyntheticDefaultModuleType, SyntheticExpression, TaggedTemplateExpression, TemplateExpression, TemplateLiteralType, TemplateLiteralTypeNode, Ternary, textRangeContainsPositionInclusive, TextSpan, textSpanContainsPosition, textSpanEnd, ThisExpression, ThisTypeNode, ThrowStatement, TokenFlags, tokenToString, tracing, TracingNode, TrackedSymbol, TransientSymbol, TransientSymbolLinks, tryAddToSet, tryCast, tryExtractTSExtension, tryGetClassImplementingOrExtendingExpressionWithTypeArguments, tryGetExtensionFromPath, tryGetJSDocSatisfiesTypeNode, tryGetModuleSpecifierFromDeclaration, tryGetPropertyAccessOrIdentifierToString, TryStatement, TupleType, TupleTypeNode, TupleTypeReference, Type, TypeAliasDeclaration, TypeAssertion, TypeChecker, TypeCheckerHost, TypeComparer, TypeElement, TypeFlags, TypeFormatFlags, TypeId, TypeLiteralNode, TypeMapKind, TypeMapper, TypeNode, TypeNodeSyntaxKind, TypeOfExpression, TypeOnlyAliasDeclaration, TypeOnlyCompatibleAliasDeclaration, TypeOperatorNode, TypeParameter, TypeParameterDeclaration, TypePredicate, TypePredicateKind, TypePredicateNode, TypeQueryNode, TypeReference, TypeReferenceNode, TypeReferenceSerializationKind, TypeReferenceType, TypeVariable, unescapeLeadingUnderscores, UnionOrIntersectionType, UnionOrIntersectionTypeNode, UnionReduction, UnionType, UnionTypeNode, UniqueESSymbolType, usingSingleLineStringWriter, VariableDeclaration, VariableDeclarationList, VariableLikeDeclaration, VariableStatement, VarianceFlags, visitEachChild as visitEachChildWorker, visitNode, visitNodes, Visitor, VisitResult, VoidExpression, walkUpBindingElementsAndPatterns, walkUpOuterExpressions, walkUpParenthesizedExpressions, walkUpParenthesizedTypes, walkUpParenthesizedTypesAndGetParentAndChild, WhileStatement, WideningContext, WithStatement, YieldExpression, } from "./_namespaces/ts.js"; import * as moduleSpecifiers from "./_namespaces/ts.moduleSpecifiers.js"; import * as performance from "./_namespaces/ts.performance.js"; const ambientModuleSymbolRegex = /^".+"$/; const anon = "(anonymous)" as __String & string; const enum ReferenceHint { Unspecified, Identifier, Property, ExportAssignment, Jsx, AsyncFunction, ExportImportEquals, ExportSpecifier, Decorator, } let nextSymbolId = 1; let nextNodeId = 1; let nextMergeId = 1; let nextFlowId = 1; const enum IterationUse { AllowsSyncIterablesFlag = 1 << 0, AllowsAsyncIterablesFlag = 1 << 1, AllowsStringInputFlag = 1 << 2, ForOfFlag = 1 << 3, YieldStarFlag = 1 << 4, SpreadFlag = 1 << 5, DestructuringFlag = 1 << 6, PossiblyOutOfBounds = 1 << 7, // Spread, Destructuring, Array element assignment Element = AllowsSyncIterablesFlag, Spread = AllowsSyncIterablesFlag | SpreadFlag, Destructuring = AllowsSyncIterablesFlag | DestructuringFlag, ForOf = AllowsSyncIterablesFlag | AllowsStringInputFlag | ForOfFlag, ForAwaitOf = AllowsSyncIterablesFlag | AllowsAsyncIterablesFlag | AllowsStringInputFlag | ForOfFlag, YieldStar = AllowsSyncIterablesFlag | YieldStarFlag, AsyncYieldStar = AllowsSyncIterablesFlag | AllowsAsyncIterablesFlag | YieldStarFlag, GeneratorReturnType = AllowsSyncIterablesFlag, AsyncGeneratorReturnType = AllowsAsyncIterablesFlag, } const enum IterationTypeKind { Yield, Return, Next, } interface IterationTypesResolver { iterableCacheKey: "iterationTypesOfAsyncIterable" | "iterationTypesOfIterable"; iteratorCacheKey: "iterationTypesOfAsyncIterator" | "iterationTypesOfIterator"; iteratorSymbolName: "asyncIterator" | "iterator"; getGlobalIteratorType: (reportErrors: boolean) => GenericType; getGlobalIterableType: (reportErrors: boolean) => GenericType; getGlobalIterableIteratorType: (reportErrors: boolean) => GenericType; getGlobalIteratorObjectType: (reportErrors: boolean) => GenericType; getGlobalGeneratorType: (reportErrors: boolean) => GenericType; getGlobalBuiltinIteratorTypes: () => readonly GenericType[]; resolveIterationType: (type: Type, errorNode: Node | undefined) => Type | undefined; mustHaveANextMethodDiagnostic: DiagnosticMessage; mustBeAMethodDiagnostic: DiagnosticMessage; mustHaveAValueDiagnostic: DiagnosticMessage; } const enum WideningKind { Normal, FunctionReturn, GeneratorNext, GeneratorYield, } // dprint-ignore /** @internal */ export const enum TypeFacts { None = 0, TypeofEQString = 1 << 0, // typeof x === "string" TypeofEQNumber = 1 << 1, // typeof x === "number" TypeofEQBigInt = 1 << 2, // typeof x === "bigint" TypeofEQBoolean = 1 << 3, // typeof x === "boolean" TypeofEQSymbol = 1 << 4, // typeof x === "symbol" TypeofEQObject = 1 << 5, // typeof x === "object" TypeofEQFunction = 1 << 6, // typeof x === "function" TypeofEQHostObject = 1 << 7, // typeof x === "xxx" TypeofNEString = 1 << 8, // typeof x !== "string" TypeofNENumber = 1 << 9, // typeof x !== "number" TypeofNEBigInt = 1 << 10, // typeof x !== "bigint" TypeofNEBoolean = 1 << 11, // typeof x !== "boolean" TypeofNESymbol = 1 << 12, // typeof x !== "symbol" TypeofNEObject = 1 << 13, // typeof x !== "object" TypeofNEFunction = 1 << 14, // typeof x !== "function" TypeofNEHostObject = 1 << 15, // typeof x !== "xxx" EQUndefined = 1 << 16, // x === undefined EQNull = 1 << 17, // x === null EQUndefinedOrNull = 1 << 18, // x === undefined / x === null NEUndefined = 1 << 19, // x !== undefined NENull = 1 << 20, // x !== null NEUndefinedOrNull = 1 << 21, // x != undefined / x != null Truthy = 1 << 22, // x Falsy = 1 << 23, // !x IsUndefined = 1 << 24, // Contains undefined or intersection with undefined IsNull = 1 << 25, // Contains null or intersection with null IsUndefinedOrNull = IsUndefined | IsNull, All = (1 << 27) - 1, // The following members encode facts about particular kinds of types for use in the getTypeFacts function. // The presence of a particular fact means that the given test is true for some (and possibly all) values // of that kind of type. BaseStringStrictFacts = TypeofEQString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull, BaseStringFacts = BaseStringStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, StringStrictFacts = BaseStringStrictFacts | Truthy | Falsy, StringFacts = BaseStringFacts | Truthy, EmptyStringStrictFacts = BaseStringStrictFacts | Falsy, EmptyStringFacts = BaseStringFacts, NonEmptyStringStrictFacts = BaseStringStrictFacts | Truthy, NonEmptyStringFacts = BaseStringFacts | Truthy, BaseNumberStrictFacts = TypeofEQNumber | TypeofNEString | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull, BaseNumberFacts = BaseNumberStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, NumberStrictFacts = BaseNumberStrictFacts | Truthy | Falsy, NumberFacts = BaseNumberFacts | Truthy, ZeroNumberStrictFacts = BaseNumberStrictFacts | Falsy, ZeroNumberFacts = BaseNumberFacts, NonZeroNumberStrictFacts = BaseNumberStrictFacts | Truthy, NonZeroNumberFacts = BaseNumberFacts | Truthy, BaseBigIntStrictFacts = TypeofEQBigInt | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull, BaseBigIntFacts = BaseBigIntStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, BigIntStrictFacts = BaseBigIntStrictFacts | Truthy | Falsy, BigIntFacts = BaseBigIntFacts | Truthy, ZeroBigIntStrictFacts = BaseBigIntStrictFacts | Falsy, ZeroBigIntFacts = BaseBigIntFacts, NonZeroBigIntStrictFacts = BaseBigIntStrictFacts | Truthy, NonZeroBigIntFacts = BaseBigIntFacts | Truthy, BaseBooleanStrictFacts = TypeofEQBoolean | TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull, BaseBooleanFacts = BaseBooleanStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, BooleanStrictFacts = BaseBooleanStrictFacts | Truthy | Falsy, BooleanFacts = BaseBooleanFacts | Truthy, FalseStrictFacts = BaseBooleanStrictFacts | Falsy, FalseFacts = BaseBooleanFacts, TrueStrictFacts = BaseBooleanStrictFacts | Truthy, TrueFacts = BaseBooleanFacts | Truthy, SymbolStrictFacts = TypeofEQSymbol | TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull | Truthy, SymbolFacts = SymbolStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, ObjectStrictFacts = TypeofEQObject | TypeofEQHostObject | TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | NEUndefined | NENull | NEUndefinedOrNull | Truthy, ObjectFacts = ObjectStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, FunctionStrictFacts = TypeofEQFunction | TypeofEQHostObject | TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | NEUndefined | NENull | NEUndefinedOrNull | Truthy, FunctionFacts = FunctionStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, VoidFacts = TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | EQUndefined | EQUndefinedOrNull | NENull | Falsy, UndefinedFacts = TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | EQUndefined | EQUndefinedOrNull | NENull | Falsy | IsUndefined, NullFacts = TypeofEQObject | TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | TypeofNEHostObject | EQNull | EQUndefinedOrNull | NEUndefined | Falsy | IsNull, EmptyObjectStrictFacts = All & ~(EQUndefined | EQNull | EQUndefinedOrNull | IsUndefinedOrNull), EmptyObjectFacts = All & ~IsUndefinedOrNull, UnknownFacts = All & ~IsUndefinedOrNull, AllTypeofNE = TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | NEUndefined, // Masks OrFactsMask = TypeofEQFunction | TypeofNEObject, AndFactsMask = All & ~OrFactsMask, } const typeofNEFacts: ReadonlyMap = new Map(Object.entries({ string: TypeFacts.TypeofNEString, number: TypeFacts.TypeofNENumber, bigint: TypeFacts.TypeofNEBigInt, boolean: TypeFacts.TypeofNEBoolean, symbol: TypeFacts.TypeofNESymbol, undefined: TypeFacts.NEUndefined, object: TypeFacts.TypeofNEObject, function: TypeFacts.TypeofNEFunction, })); type TypeSystemEntity = Node | Symbol | Type | Signature; const enum TypeSystemPropertyName { Type, ResolvedBaseConstructorType, DeclaredType, ResolvedReturnType, ImmediateBaseConstraint, ResolvedTypeArguments, ResolvedBaseTypes, WriteType, ParameterInitializerContainsUndefined, } // dprint-ignore /** @internal */ export const enum CheckMode { Normal = 0, // Normal type checking Contextual = 1 << 0, // Explicitly assigned contextual type, therefore not cacheable Inferential = 1 << 1, // Inferential typing SkipContextSensitive = 1 << 2, // Skip context sensitive function expressions SkipGenericFunctions = 1 << 3, // Skip single signature generic functions IsForSignatureHelp = 1 << 4, // Call resolution for purposes of signature help RestBindingElement = 1 << 5, // Checking a type that is going to be used to determine the type of a rest binding element // e.g. in `const { a, ...rest } = foo`, when checking the type of `foo` to determine the type of `rest`, // we need to preserve generic types instead of substituting them for constraints TypeOnly = 1 << 6, // Called from getTypeOfExpression, diagnostics may be omitted } /** @internal */ export const enum SignatureCheckMode { None = 0, BivariantCallback = 1 << 0, StrictCallback = 1 << 1, IgnoreReturnTypes = 1 << 2, StrictArity = 1 << 3, StrictTopSignature = 1 << 4, Callback = BivariantCallback | StrictCallback, } const enum IntersectionState { None = 0, Source = 1 << 0, // Source type is a constituent of an outer intersection Target = 1 << 1, // Target type is a constituent of an outer intersection } const enum RecursionFlags { None = 0, Source = 1 << 0, Target = 1 << 1, Both = Source | Target, } const enum MappedTypeModifiers { IncludeReadonly = 1 << 0, ExcludeReadonly = 1 << 1, IncludeOptional = 1 << 2, ExcludeOptional = 1 << 3, } const enum MappedTypeNameTypeKind { None, Filtering, Remapping, } const enum ExpandingFlags { None = 0, Source = 1, Target = 1 << 1, Both = Source | Target, } const enum MembersOrExportsResolutionKind { resolvedExports = "resolvedExports", resolvedMembers = "resolvedMembers", } const enum UnusedKind { Local, Parameter, } /** @param containingNode Node to check for parse error */ type AddUnusedDiagnostic = (containingNode: Node, type: UnusedKind, diagnostic: DiagnosticWithLocation) => void; const isNotOverloadAndNotAccessor = and(isNotOverload, isNotAccessor); const enum DeclarationMeaning { GetAccessor = 1, SetAccessor = 2, PropertyAssignment = 4, Method = 8, PrivateStatic = 16, GetOrSetAccessor = GetAccessor | SetAccessor, PropertyAssignmentOrMethod = PropertyAssignment | Method, } const enum DeclarationSpaces { None = 0, ExportValue = 1 << 0, ExportType = 1 << 1, ExportNamespace = 1 << 2, } const enum MinArgumentCountFlags { None = 0, StrongArityForUntypedJS = 1 << 0, VoidIsNonOptional = 1 << 1, } const enum IntrinsicTypeKind { Uppercase, Lowercase, Capitalize, Uncapitalize, NoInfer, } const intrinsicTypeKinds: ReadonlyMap = new Map(Object.entries({ Uppercase: IntrinsicTypeKind.Uppercase, Lowercase: IntrinsicTypeKind.Lowercase, Capitalize: IntrinsicTypeKind.Capitalize, Uncapitalize: IntrinsicTypeKind.Uncapitalize, NoInfer: IntrinsicTypeKind.NoInfer, })); const SymbolLinks = class implements SymbolLinks { declare _symbolLinksBrand: any; }; function NodeLinks(this: NodeLinks) { this.flags = NodeCheckFlags.None; } /** @internal */ export function getNodeId(node: Node): number { if (!node.id) { node.id = nextNodeId; nextNodeId++; } return node.id; } /** @internal */ export function getSymbolId(symbol: Symbol): SymbolId { if (!symbol.id) { symbol.id = nextSymbolId; nextSymbolId++; } return symbol.id; } /** @internal */ export function isInstantiatedModule(node: ModuleDeclaration, preserveConstEnums: boolean): boolean { const moduleState = getModuleInstanceState(node); return moduleState === ModuleInstanceState.Instantiated || (preserveConstEnums && moduleState === ModuleInstanceState.ConstEnumOnly); } /** @internal */ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // Why var? It avoids TDZ checks in the runtime which can be costly. // See: https://github.com/microsoft/TypeScript/issues/52924 /* eslint-disable no-var */ var deferredDiagnosticsCallbacks: (() => void)[] = []; var addLazyDiagnostic = (arg: () => void) => { deferredDiagnosticsCallbacks.push(arg); }; // Cancellation that controls whether or not we can cancel in the middle of type checking. // In general cancelling is *not* safe for the type checker. We might be in the middle of // computing something, and we will leave our internals in an inconsistent state. Callers // who set the cancellation token should catch if a cancellation exception occurs, and // should throw away and create a new TypeChecker. // // Currently we only support setting the cancellation token when getting diagnostics. This // is because diagnostics can be quite expensive, and we want to allow hosts to bail out if // they no longer need the information (for example, if the user started editing again). var cancellationToken: CancellationToken | undefined; var scanner: Scanner | undefined; var Symbol = objectAllocator.getSymbolConstructor(); var Type = objectAllocator.getTypeConstructor(); var Signature = objectAllocator.getSignatureConstructor(); var typeCount = 0; var symbolCount = 0; var totalInstantiationCount = 0; var instantiationCount = 0; var instantiationDepth = 0; var inlineLevel = 0; var currentNode: Node | undefined; var varianceTypeParameter: TypeParameter | undefined; var isInferencePartiallyBlocked = false; var emptySymbols = createSymbolTable(); var arrayVariances = [VarianceFlags.Covariant]; var compilerOptions = host.getCompilerOptions(); var languageVersion = getEmitScriptTarget(compilerOptions); var moduleKind = getEmitModuleKind(compilerOptions); var legacyDecorators = !!compilerOptions.experimentalDecorators; var useDefineForClassFields = getUseDefineForClassFields(compilerOptions); var emitStandardClassFields = getEmitStandardClassFields(compilerOptions); var allowSyntheticDefaultImports = getAllowSyntheticDefaultImports(compilerOptions); var strictNullChecks = getStrictOptionValue(compilerOptions, "strictNullChecks"); var strictFunctionTypes = getStrictOptionValue(compilerOptions, "strictFunctionTypes"); var strictBindCallApply = getStrictOptionValue(compilerOptions, "strictBindCallApply"); var strictPropertyInitialization = getStrictOptionValue(compilerOptions, "strictPropertyInitialization"); var strictBuiltinIteratorReturn = getStrictOptionValue(compilerOptions, "strictBuiltinIteratorReturn"); var noImplicitAny = getStrictOptionValue(compilerOptions, "noImplicitAny"); var noImplicitThis = getStrictOptionValue(compilerOptions, "noImplicitThis"); var useUnknownInCatchVariables = getStrictOptionValue(compilerOptions, "useUnknownInCatchVariables"); var exactOptionalPropertyTypes = compilerOptions.exactOptionalPropertyTypes; var noUncheckedSideEffectImports = !!compilerOptions.noUncheckedSideEffectImports; var checkBinaryExpression = createCheckBinaryExpression(); var emitResolver = createResolver(); var nodeBuilder = createNodeBuilder(); var syntacticNodeBuilder = createSyntacticTypeNodeBuilder(compilerOptions, { isEntityNameVisible, isExpandoFunctionDeclaration, getAllAccessorDeclarations: getAllAccessorDeclarationsForDeclaration, requiresAddingImplicitUndefined, isUndefinedIdentifierExpression(node: Identifier) { Debug.assert(isExpressionNode(node)); return getSymbolAtLocation(node) === undefinedSymbol; }, isDefinitelyReferenceToGlobalSymbolObject, }); var evaluate = createEvaluator({ evaluateElementAccessExpression, evaluateEntityNameExpression, }); var globals = createSymbolTable(); var undefinedSymbol = createSymbol(SymbolFlags.Property, "undefined" as __String); undefinedSymbol.declarations = []; var globalThisSymbol = createSymbol(SymbolFlags.Module, "globalThis" as __String, CheckFlags.Readonly); globalThisSymbol.exports = globals; globalThisSymbol.declarations = []; globals.set(globalThisSymbol.escapedName, globalThisSymbol); var argumentsSymbol = createSymbol(SymbolFlags.Property, "arguments" as __String); var requireSymbol = createSymbol(SymbolFlags.Property, "require" as __String); var isolatedModulesLikeFlagName = compilerOptions.verbatimModuleSyntax ? "verbatimModuleSyntax" : "isolatedModules"; var canCollectSymbolAliasAccessabilityData = !compilerOptions.verbatimModuleSyntax; /** This will be set during calls to `getResolvedSignature` where services determines an apparent number of arguments greater than what is actually provided. */ var apparentArgumentCount: number | undefined; var lastGetCombinedNodeFlagsNode: Node | undefined; var lastGetCombinedNodeFlagsResult = NodeFlags.None; var lastGetCombinedModifierFlagsNode: Declaration | undefined; var lastGetCombinedModifierFlagsResult = ModifierFlags.None; var resolveName = createNameResolver({ compilerOptions, requireSymbol, argumentsSymbol, globals, getSymbolOfDeclaration, error, getRequiresScopeChangeCache, setRequiresScopeChangeCache, lookup: getSymbol, onPropertyWithInvalidInitializer: checkAndReportErrorForInvalidInitializer, onFailedToResolveSymbol, onSuccessfullyResolvedSymbol, }); var resolveNameForSymbolSuggestion = createNameResolver({ compilerOptions, requireSymbol, argumentsSymbol, globals, getSymbolOfDeclaration, error, getRequiresScopeChangeCache, setRequiresScopeChangeCache, lookup: getSuggestionForSymbolNameLookup, }); // for public members that accept a Node or one of its subtypes, we must guard against // synthetic nodes created during transformations by calling `getParseTreeNode`. // for most of these, we perform the guard only on `checker` to avoid any possible // extra cost of calling `getParseTreeNode` when calling these functions from inside the // checker. const checker: TypeChecker = { getNodeCount: () => reduceLeft(host.getSourceFiles(), (n, s) => n + s.nodeCount, 0), getIdentifierCount: () => reduceLeft(host.getSourceFiles(), (n, s) => n + s.identifierCount, 0), getSymbolCount: () => reduceLeft(host.getSourceFiles(), (n, s) => n + s.symbolCount, symbolCount), getTypeCount: () => typeCount, getInstantiationCount: () => totalInstantiationCount, getRelationCacheSizes: () => ({ assignable: assignableRelation.size, identity: identityRelation.size, subtype: subtypeRelation.size, strictSubtype: strictSubtypeRelation.size, }), isUndefinedSymbol: symbol => symbol === undefinedSymbol, isArgumentsSymbol: symbol => symbol === argumentsSymbol, isUnknownSymbol: symbol => symbol === unknownSymbol, getMergedSymbol, symbolIsValue, getDiagnostics, getGlobalDiagnostics, getRecursionIdentity, getUnmatchedProperties, getTypeOfSymbolAtLocation: (symbol, locationIn) => { const location = getParseTreeNode(locationIn); return location ? getTypeOfSymbolAtLocation(symbol, location) : errorType; }, getTypeOfSymbol, getSymbolsOfParameterPropertyDeclaration: (parameterIn, parameterName) => { const parameter = getParseTreeNode(parameterIn, isParameter); if (parameter === undefined) return Debug.fail("Cannot get symbols of a synthetic parameter that cannot be resolved to a parse-tree node."); Debug.assert(isParameterPropertyDeclaration(parameter, parameter.parent)); return getSymbolsOfParameterPropertyDeclaration(parameter, escapeLeadingUnderscores(parameterName)); }, getDeclaredTypeOfSymbol, getPropertiesOfType, getPropertyOfType: (type, name) => getPropertyOfType(type, escapeLeadingUnderscores(name)), getPrivateIdentifierPropertyOfType: (leftType: Type, name: string, location: Node) => { const node = getParseTreeNode(location); if (!node) { return undefined; } const propName = escapeLeadingUnderscores(name); const lexicallyScopedIdentifier = lookupSymbolForPrivateIdentifierDeclaration(propName, node); return lexicallyScopedIdentifier ? getPrivateIdentifierPropertyOfType(leftType, lexicallyScopedIdentifier) : undefined; }, getTypeOfPropertyOfType: (type, name) => getTypeOfPropertyOfType(type, escapeLeadingUnderscores(name)), getIndexInfoOfType: (type, kind) => getIndexInfoOfType(type, kind === IndexKind.String ? stringType : numberType), getIndexInfosOfType, getIndexInfosOfIndexSymbol, getSignaturesOfType, getIndexTypeOfType: (type, kind) => getIndexTypeOfType(type, kind === IndexKind.String ? stringType : numberType), getIndexType: type => getIndexType(type), getBaseTypes, getBaseTypeOfLiteralType, getWidenedType, getWidenedLiteralType, fillMissingTypeArguments, getTypeFromTypeNode: nodeIn => { const node = getParseTreeNode(nodeIn, isTypeNode); return node ? getTypeFromTypeNode(node) : errorType; }, getParameterType: getTypeAtPosition, getParameterIdentifierInfoAtPosition, getPromisedTypeOfPromise, getAwaitedType: type => getAwaitedType(type), getReturnTypeOfSignature, isNullableType, getNullableType, getNonNullableType, getNonOptionalType: removeOptionalTypeMarker, getTypeArguments, typeToTypeNode: nodeBuilder.typeToTypeNode, typePredicateToTypePredicateNode: nodeBuilder.typePredicateToTypePredicateNode, indexInfoToIndexSignatureDeclaration: nodeBuilder.indexInfoToIndexSignatureDeclaration, signatureToSignatureDeclaration: nodeBuilder.signatureToSignatureDeclaration, symbolToEntityName: nodeBuilder.symbolToEntityName, symbolToExpression: nodeBuilder.symbolToExpression, symbolToNode: nodeBuilder.symbolToNode, symbolToTypeParameterDeclarations: nodeBuilder.symbolToTypeParameterDeclarations, symbolToParameterDeclaration: nodeBuilder.symbolToParameterDeclaration, typeParameterToDeclaration: nodeBuilder.typeParameterToDeclaration, getSymbolsInScope: (locationIn, meaning) => { const location = getParseTreeNode(locationIn); return location ? getSymbolsInScope(location, meaning) : []; }, getSymbolAtLocation: nodeIn => { const node = getParseTreeNode(nodeIn); // set ignoreErrors: true because any lookups invoked by the API shouldn't cause any new errors return node ? getSymbolAtLocation(node, /*ignoreErrors*/ true) : undefined; }, getIndexInfosAtLocation: nodeIn => { const node = getParseTreeNode(nodeIn); return node ? getIndexInfosAtLocation(node) : undefined; }, getShorthandAssignmentValueSymbol: nodeIn => { const node = getParseTreeNode(nodeIn); return node ? getShorthandAssignmentValueSymbol(node) : undefined; }, getExportSpecifierLocalTargetSymbol: nodeIn => { const node = getParseTreeNode(nodeIn, isExportSpecifier); return node ? getExportSpecifierLocalTargetSymbol(node) : undefined; }, getExportSymbolOfSymbol(symbol) { return getMergedSymbol(symbol.exportSymbol || symbol); }, getTypeAtLocation: nodeIn => { const node = getParseTreeNode(nodeIn); return node ? getTypeOfNode(node) : errorType; }, getTypeOfAssignmentPattern: nodeIn => { const node = getParseTreeNode(nodeIn, isAssignmentPattern); return node && getTypeOfAssignmentPattern(node) || errorType; }, getPropertySymbolOfDestructuringAssignment: locationIn => { const location = getParseTreeNode(locationIn, isIdentifier); return location ? getPropertySymbolOfDestructuringAssignment(location) : undefined; }, signatureToString: (signature, enclosingDeclaration, flags, kind) => { return signatureToString(signature, getParseTreeNode(enclosingDeclaration), flags, kind); }, typeToString: (type, enclosingDeclaration, flags) => { return typeToString(type, getParseTreeNode(enclosingDeclaration), flags); }, symbolToString: (symbol, enclosingDeclaration, meaning, flags) => { return symbolToString(symbol, getParseTreeNode(enclosingDeclaration), meaning, flags); }, typePredicateToString: (predicate, enclosingDeclaration, flags) => { return typePredicateToString(predicate, getParseTreeNode(enclosingDeclaration), flags); }, writeSignature: (signature, enclosingDeclaration, flags, kind, writer) => { return signatureToString(signature, getParseTreeNode(enclosingDeclaration), flags, kind, writer); }, writeType: (type, enclosingDeclaration, flags, writer) => { return typeToString(type, getParseTreeNode(enclosingDeclaration), flags, writer); }, writeSymbol: (symbol, enclosingDeclaration, meaning, flags, writer) => { return symbolToString(symbol, getParseTreeNode(enclosingDeclaration), meaning, flags, writer); }, writeTypePredicate: (predicate, enclosingDeclaration, flags, writer) => { return typePredicateToString(predicate, getParseTreeNode(enclosingDeclaration), flags, writer); }, getAugmentedPropertiesOfType, getRootSymbols, getSymbolOfExpando, getContextualType: (nodeIn: Expression, contextFlags?: ContextFlags) => { const node = getParseTreeNode(nodeIn, isExpression); if (!node) { return undefined; } if (contextFlags! & ContextFlags.Completions) { return runWithInferenceBlockedFromSourceNode(node, () => getContextualType(node, contextFlags)); } return getContextualType(node, contextFlags); }, getContextualTypeForObjectLiteralElement: nodeIn => { const node = getParseTreeNode(nodeIn, isObjectLiteralElementLike); return node ? getContextualTypeForObjectLiteralElement(node, /*contextFlags*/ undefined) : undefined; }, getContextualTypeForArgumentAtIndex: (nodeIn, argIndex) => { const node = getParseTreeNode(nodeIn, isCallLikeExpression); return node && getContextualTypeForArgumentAtIndex(node, argIndex); }, getContextualTypeForJsxAttribute: nodeIn => { const node = getParseTreeNode(nodeIn, isJsxAttributeLike); return node && getContextualTypeForJsxAttribute(node, /*contextFlags*/ undefined); }, isContextSensitive, getTypeOfPropertyOfContextualType, getFullyQualifiedName, getResolvedSignature: (node, candidatesOutArray, argumentCount) => getResolvedSignatureWorker(node, candidatesOutArray, argumentCount, CheckMode.Normal), getCandidateSignaturesForStringLiteralCompletions, getResolvedSignatureForSignatureHelp: (node, candidatesOutArray, argumentCount) => runWithoutResolvedSignatureCaching(node, () => getResolvedSignatureWorker(node, candidatesOutArray, argumentCount, CheckMode.IsForSignatureHelp)), getExpandedParameters, hasEffectiveRestParameter, containsArgumentsReference, getConstantValue: nodeIn => { const node = getParseTreeNode(nodeIn, canHaveConstantValue); return node ? getConstantValue(node) : undefined; }, isValidPropertyAccess: (nodeIn, propertyName) => { const node = getParseTreeNode(nodeIn, isPropertyAccessOrQualifiedNameOrImportTypeNode); return !!node && isValidPropertyAccess(node, escapeLeadingUnderscores(propertyName)); }, isValidPropertyAccessForCompletions: (nodeIn, type, property) => { const node = getParseTreeNode(nodeIn, isPropertyAccessExpression); return !!node && isValidPropertyAccessForCompletions(node, type, property); }, getSignatureFromDeclaration: declarationIn => { const declaration = getParseTreeNode(declarationIn, isFunctionLike); return declaration ? getSignatureFromDeclaration(declaration) : undefined; }, isImplementationOfOverload: nodeIn => { const node = getParseTreeNode(nodeIn, isFunctionLike); return node ? isImplementationOfOverload(node) : undefined; }, getImmediateAliasedSymbol, getAliasedSymbol: resolveAlias, getEmitResolver, requiresAddingImplicitUndefined, getExportsOfModule: getExportsOfModuleAsArray, getExportsAndPropertiesOfModule, forEachExportAndPropertyOfModule, getSymbolWalker: createGetSymbolWalker( getRestTypeOfSignature, getTypePredicateOfSignature, getReturnTypeOfSignature, getBaseTypes, resolveStructuredTypeMembers, getTypeOfSymbol, getResolvedSymbol, getConstraintOfTypeParameter, getFirstIdentifier, getTypeArguments, ), getAmbientModules, getJsxIntrinsicTagNamesAt, isOptionalParameter: nodeIn => { const node = getParseTreeNode(nodeIn, isParameter); return node ? isOptionalParameter(node) : false; }, tryGetMemberInModuleExports: (name, symbol) => tryGetMemberInModuleExports(escapeLeadingUnderscores(name), symbol), tryGetMemberInModuleExportsAndProperties: (name, symbol) => tryGetMemberInModuleExportsAndProperties(escapeLeadingUnderscores(name), symbol), tryFindAmbientModule: moduleName => tryFindAmbientModule(moduleName, /*withAugmentations*/ true), getApparentType, getUnionType, isTypeAssignableTo, createAnonymousType, createSignature, createSymbol, createIndexInfo, getAnyType: () => anyType, getStringType: () => stringType, getStringLiteralType, getNumberType: () => numberType, getNumberLiteralType, getBigIntType: () => bigintType, getBigIntLiteralType, createPromiseType, createArrayType, getElementTypeOfArrayType, getBooleanType: () => booleanType, getFalseType: (fresh?) => fresh ? falseType : regularFalseType, getTrueType: (fresh?) => fresh ? trueType : regularTrueType, getVoidType: () => voidType, getUndefinedType: () => undefinedType, getNullType: () => nullType, getESSymbolType: () => esSymbolType, getNeverType: () => neverType, getOptionalType: () => optionalType, getPromiseType: () => getGlobalPromiseType(/*reportErrors*/ false), getPromiseLikeType: () => getGlobalPromiseLikeType(/*reportErrors*/ false), getAnyAsyncIterableType: () => { const type = getGlobalAsyncIterableType(/*reportErrors*/ false); if (type === emptyGenericType) return undefined; return createTypeReference(type, [anyType, anyType, anyType]); }, isSymbolAccessible, isArrayType, isTupleType, isArrayLikeType, isEmptyAnonymousObjectType, isTypeInvalidDueToUnionDiscriminant, getExactOptionalProperties, getAllPossiblePropertiesOfTypes, getSuggestedSymbolForNonexistentProperty, getSuggestedSymbolForNonexistentJSXAttribute, getSuggestedSymbolForNonexistentSymbol: (location, name, meaning) => getSuggestedSymbolForNonexistentSymbol(location, escapeLeadingUnderscores(name), meaning), getSuggestedSymbolForNonexistentModule, getSuggestedSymbolForNonexistentClassMember, getBaseConstraintOfType, getDefaultFromTypeParameter: type => type && type.flags & TypeFlags.TypeParameter ? getDefaultFromTypeParameter(type as TypeParameter) : undefined, resolveName(name, location, meaning, excludeGlobals) { return resolveName(location, escapeLeadingUnderscores(name), meaning, /*nameNotFoundMessage*/ undefined, /*isUse*/ false, excludeGlobals); }, getJsxNamespace: n => unescapeLeadingUnderscores(getJsxNamespace(n)), getJsxFragmentFactory: n => { const jsxFragmentFactory = getJsxFragmentFactoryEntity(n); return jsxFragmentFactory && unescapeLeadingUnderscores(getFirstIdentifier(jsxFragmentFactory).escapedText); }, getAccessibleSymbolChain, getTypePredicateOfSignature, resolveExternalModuleName: moduleSpecifierIn => { const moduleSpecifier = getParseTreeNode(moduleSpecifierIn, isExpression); return moduleSpecifier && resolveExternalModuleName(moduleSpecifier, moduleSpecifier, /*ignoreErrors*/ true); }, resolveExternalModuleSymbol, tryGetThisTypeAt: (nodeIn, includeGlobalThis, container) => { const node = getParseTreeNode(nodeIn); return node && tryGetThisTypeAt(node, includeGlobalThis, container); }, getTypeArgumentConstraint: nodeIn => { const node = getParseTreeNode(nodeIn, isTypeNode); return node && getTypeArgumentConstraint(node); }, getSuggestionDiagnostics: (fileIn, ct) => { const file = getParseTreeNode(fileIn, isSourceFile) || Debug.fail("Could not determine parsed source file."); if (skipTypeChecking(file, compilerOptions, host)) { return emptyArray; } let diagnostics: DiagnosticWithLocation[] | undefined; try { // Record the cancellation token so it can be checked later on during checkSourceElement. // Do this in a finally block so we can ensure that it gets reset back to nothing after // this call is done. cancellationToken = ct; // Ensure file is type checked, with _eager_ diagnostic production, so identifiers are registered as potentially unused checkSourceFileWithEagerDiagnostics(file); Debug.assert(!!(getNodeLinks(file).flags & NodeCheckFlags.TypeChecked)); diagnostics = addRange(diagnostics, suggestionDiagnostics.getDiagnostics(file.fileName)); checkUnusedIdentifiers(getPotentiallyUnusedIdentifiers(file), (containingNode, kind, diag) => { if (!containsParseError(containingNode) && !unusedIsError(kind, !!(containingNode.flags & NodeFlags.Ambient))) { (diagnostics || (diagnostics = [])).push({ ...diag, category: DiagnosticCategory.Suggestion }); } }); return diagnostics || emptyArray; } finally { cancellationToken = undefined; } }, runWithCancellationToken: (token, callback) => { try { cancellationToken = token; return callback(checker); } finally { cancellationToken = undefined; } }, getLocalTypeParametersOfClassOrInterfaceOrTypeAlias, isDeclarationVisible, isPropertyAccessible, getTypeOnlyAliasDeclaration, getMemberOverrideModifierStatus, isTypeParameterPossiblyReferenced, typeHasCallOrConstructSignatures, getSymbolFlags, }; function getCandidateSignaturesForStringLiteralCompletions(call: CallLikeExpression, editingArgument: Node) { const candidatesSet = new Set(); const candidates: Signature[] = []; // first, get candidates when inference is blocked from the source node. runWithInferenceBlockedFromSourceNode(editingArgument, () => getResolvedSignatureWorker(call, candidates, /*argumentCount*/ undefined, CheckMode.Normal)); for (const candidate of candidates) { candidatesSet.add(candidate); } // reset candidates for second pass candidates.length = 0; // next, get candidates where the source node is considered for inference. runWithoutResolvedSignatureCaching(editingArgument, () => getResolvedSignatureWorker(call, candidates, /*argumentCount*/ undefined, CheckMode.Normal)); for (const candidate of candidates) { candidatesSet.add(candidate); } return arrayFrom(candidatesSet); } function runWithoutResolvedSignatureCaching(node: Node | undefined, fn: () => T): T { node = findAncestor(node, isCallLikeOrFunctionLikeExpression); if (node) { const cachedResolvedSignatures = []; const cachedTypes = []; while (node) { const nodeLinks = getNodeLinks(node); cachedResolvedSignatures.push([nodeLinks, nodeLinks.resolvedSignature] as const); nodeLinks.resolvedSignature = undefined; if (isFunctionExpressionOrArrowFunction(node)) { const symbolLinks = getSymbolLinks(getSymbolOfDeclaration(node)); const type = symbolLinks.type; cachedTypes.push([symbolLinks, type] as const); symbolLinks.type = undefined; } node = findAncestor(node.parent, isCallLikeOrFunctionLikeExpression); } const result = fn(); for (const [nodeLinks, resolvedSignature] of cachedResolvedSignatures) { nodeLinks.resolvedSignature = resolvedSignature; } for (const [symbolLinks, type] of cachedTypes) { symbolLinks.type = type; } return result; } return fn(); } function runWithInferenceBlockedFromSourceNode(node: Node | undefined, fn: () => T): T { const containingCall = findAncestor(node, isCallLikeExpression); if (containingCall) { let toMarkSkip = node!; do { getNodeLinks(toMarkSkip).skipDirectInference = true; toMarkSkip = toMarkSkip.parent; } while (toMarkSkip && toMarkSkip !== containingCall); } isInferencePartiallyBlocked = true; const result = runWithoutResolvedSignatureCaching(node, fn); isInferencePartiallyBlocked = false; if (containingCall) { let toMarkSkip = node!; do { getNodeLinks(toMarkSkip).skipDirectInference = undefined; toMarkSkip = toMarkSkip.parent; } while (toMarkSkip && toMarkSkip !== containingCall); } return result; } function getResolvedSignatureWorker(nodeIn: CallLikeExpression, candidatesOutArray: Signature[] | undefined, argumentCount: number | undefined, checkMode: CheckMode): Signature | undefined { const node = getParseTreeNode(nodeIn, isCallLikeExpression); apparentArgumentCount = argumentCount; const res = !node ? undefined : getResolvedSignature(node, candidatesOutArray, checkMode); apparentArgumentCount = undefined; return res; } var tupleTypes = new Map(); var unionTypes = new Map(); var unionOfUnionTypes = new Map(); var intersectionTypes = new Map(); var stringLiteralTypes = new Map(); var numberLiteralTypes = new Map(); var bigIntLiteralTypes = new Map(); var enumLiteralTypes = new Map(); var indexedAccessTypes = new Map(); var templateLiteralTypes = new Map(); var stringMappingTypes = new Map(); var substitutionTypes = new Map(); var subtypeReductionCache = new Map(); var decoratorContextOverrideTypeCache = new Map(); var cachedTypes = new Map(); var evolvingArrayTypes: EvolvingArrayType[] = []; var undefinedProperties: SymbolTable = new Map(); var markerTypes = new Set(); var unknownSymbol = createSymbol(SymbolFlags.Property, "unknown" as __String); var resolvingSymbol = createSymbol(0, InternalSymbolName.Resolving); var unresolvedSymbols = new Map(); var errorTypes = new Map(); // We specifically create the `undefined` and `null` types before any other types that can occur in // unions such that they are given low type IDs and occur first in the sorted list of union constituents. // We can then just examine the first constituent(s) of a union to check for their presence. var seenIntrinsicNames = new Set(); var anyType = createIntrinsicType(TypeFlags.Any, "any"); var autoType = createIntrinsicType(TypeFlags.Any, "any", ObjectFlags.NonInferrableType, "auto"); var wildcardType = createIntrinsicType(TypeFlags.Any, "any", /*objectFlags*/ undefined, "wildcard"); var blockedStringType = createIntrinsicType(TypeFlags.Any, "any", /*objectFlags*/ undefined, "blocked string"); var errorType = createIntrinsicType(TypeFlags.Any, "error"); var unresolvedType = createIntrinsicType(TypeFlags.Any, "unresolved"); var nonInferrableAnyType = createIntrinsicType(TypeFlags.Any, "any", ObjectFlags.ContainsWideningType, "non-inferrable"); var intrinsicMarkerType = createIntrinsicType(TypeFlags.Any, "intrinsic"); var unknownType = createIntrinsicType(TypeFlags.Unknown, "unknown"); var undefinedType = createIntrinsicType(TypeFlags.Undefined, "undefined"); var undefinedWideningType = strictNullChecks ? undefinedType : createIntrinsicType(TypeFlags.Undefined, "undefined", ObjectFlags.ContainsWideningType, "widening"); var missingType = createIntrinsicType(TypeFlags.Undefined, "undefined", /*objectFlags*/ undefined, "missing"); var undefinedOrMissingType = exactOptionalPropertyTypes ? missingType : undefinedType; var optionalType = createIntrinsicType(TypeFlags.Undefined, "undefined", /*objectFlags*/ undefined, "optional"); var nullType = createIntrinsicType(TypeFlags.Null, "null"); var nullWideningType = strictNullChecks ? nullType : createIntrinsicType(TypeFlags.Null, "null", ObjectFlags.ContainsWideningType, "widening"); var stringType = createIntrinsicType(TypeFlags.String, "string"); var numberType = createIntrinsicType(TypeFlags.Number, "number"); var bigintType = createIntrinsicType(TypeFlags.BigInt, "bigint"); var falseType = createIntrinsicType(TypeFlags.BooleanLiteral, "false", /*objectFlags*/ undefined, "fresh") as FreshableIntrinsicType; var regularFalseType = createIntrinsicType(TypeFlags.BooleanLiteral, "false") as FreshableIntrinsicType; var trueType = createIntrinsicType(TypeFlags.BooleanLiteral, "true", /*objectFlags*/ undefined, "fresh") as FreshableIntrinsicType; var regularTrueType = createIntrinsicType(TypeFlags.BooleanLiteral, "true") as FreshableIntrinsicType; trueType.regularType = regularTrueType; trueType.freshType = trueType; regularTrueType.regularType = regularTrueType; regularTrueType.freshType = trueType; falseType.regularType = regularFalseType; falseType.freshType = falseType; regularFalseType.regularType = regularFalseType; regularFalseType.freshType = falseType; var booleanType = getUnionType([regularFalseType, regularTrueType]); var esSymbolType = createIntrinsicType(TypeFlags.ESSymbol, "symbol"); var voidType = createIntrinsicType(TypeFlags.Void, "void"); var neverType = createIntrinsicType(TypeFlags.Never, "never"); var silentNeverType = createIntrinsicType(TypeFlags.Never, "never", ObjectFlags.NonInferrableType, "silent"); var implicitNeverType = createIntrinsicType(TypeFlags.Never, "never", /*objectFlags*/ undefined, "implicit"); var unreachableNeverType = createIntrinsicType(TypeFlags.Never, "never", /*objectFlags*/ undefined, "unreachable"); var nonPrimitiveType = createIntrinsicType(TypeFlags.NonPrimitive, "object"); var stringOrNumberType = getUnionType([stringType, numberType]); var stringNumberSymbolType = getUnionType([stringType, numberType, esSymbolType]); var numberOrBigIntType = getUnionType([numberType, bigintType]); var templateConstraintType = getUnionType([stringType, numberType, booleanType, bigintType, nullType, undefinedType]) as UnionType; var numericStringType = getTemplateLiteralType(["", ""], [numberType]); // The `${number}` type var restrictiveMapper: TypeMapper = makeFunctionTypeMapper(t => t.flags & TypeFlags.TypeParameter ? getRestrictiveTypeParameter(t as TypeParameter) : t, () => "(restrictive mapper)"); var permissiveMapper: TypeMapper = makeFunctionTypeMapper(t => t.flags & TypeFlags.TypeParameter ? wildcardType : t, () => "(permissive mapper)"); var uniqueLiteralType = createIntrinsicType(TypeFlags.Never, "never", /*objectFlags*/ undefined, "unique literal"); // `uniqueLiteralType` is a special `never` flagged by union reduction to behave as a literal var uniqueLiteralMapper: TypeMapper = makeFunctionTypeMapper(t => t.flags & TypeFlags.TypeParameter ? uniqueLiteralType : t, () => "(unique literal mapper)"); // replace all type parameters with the unique literal type (disregarding constraints) var outofbandVarianceMarkerHandler: ((onlyUnreliable: boolean) => void) | undefined; var reportUnreliableMapper = makeFunctionTypeMapper(t => { if (outofbandVarianceMarkerHandler && (t === markerSuperType || t === markerSubType || t === markerOtherType)) { outofbandVarianceMarkerHandler(/*onlyUnreliable*/ true); } return t; }, () => "(unmeasurable reporter)"); var reportUnmeasurableMapper = makeFunctionTypeMapper(t => { if (outofbandVarianceMarkerHandler && (t === markerSuperType || t === markerSubType || t === markerOtherType)) { outofbandVarianceMarkerHandler(/*onlyUnreliable*/ false); } return t; }, () => "(unreliable reporter)"); var emptyObjectType = createAnonymousType(/*symbol*/ undefined, emptySymbols, emptyArray, emptyArray, emptyArray); var emptyJsxObjectType = createAnonymousType(/*symbol*/ undefined, emptySymbols, emptyArray, emptyArray, emptyArray); emptyJsxObjectType.objectFlags |= ObjectFlags.JsxAttributes; var emptyTypeLiteralSymbol = createSymbol(SymbolFlags.TypeLiteral, InternalSymbolName.Type); emptyTypeLiteralSymbol.members = createSymbolTable(); var emptyTypeLiteralType = createAnonymousType(emptyTypeLiteralSymbol, emptySymbols, emptyArray, emptyArray, emptyArray); var unknownEmptyObjectType = createAnonymousType(/*symbol*/ undefined, emptySymbols, emptyArray, emptyArray, emptyArray); var unknownUnionType = strictNullChecks ? getUnionType([undefinedType, nullType, unknownEmptyObjectType]) : unknownType; var emptyGenericType = createAnonymousType(/*symbol*/ undefined, emptySymbols, emptyArray, emptyArray, emptyArray) as ObjectType as GenericType; emptyGenericType.instantiations = new Map(); var anyFunctionType = createAnonymousType(/*symbol*/ undefined, emptySymbols, emptyArray, emptyArray, emptyArray); // The anyFunctionType contains the anyFunctionType by definition. The flag is further propagated // in getPropagatingFlagsOfTypes, and it is checked in inferFromTypes. anyFunctionType.objectFlags |= ObjectFlags.NonInferrableType; var noConstraintType = createAnonymousType(/*symbol*/ undefined, emptySymbols, emptyArray, emptyArray, emptyArray); var circularConstraintType = createAnonymousType(/*symbol*/ undefined, emptySymbols, emptyArray, emptyArray, emptyArray); var resolvingDefaultType = createAnonymousType(/*symbol*/ undefined, emptySymbols, emptyArray, emptyArray, emptyArray); var markerSuperType = createTypeParameter(); var markerSubType = createTypeParameter(); markerSubType.constraint = markerSuperType; var markerOtherType = createTypeParameter(); var markerSuperTypeForCheck = createTypeParameter(); var markerSubTypeForCheck = createTypeParameter(); markerSubTypeForCheck.constraint = markerSuperTypeForCheck; var noTypePredicate = createTypePredicate(TypePredicateKind.Identifier, "<>", 0, anyType); var anySignature = createSignature(/*declaration*/ undefined, /*typeParameters*/ undefined, /*thisParameter*/ undefined, emptyArray, anyType, /*resolvedTypePredicate*/ undefined, 0, SignatureFlags.None); var unknownSignature = createSignature(/*declaration*/ undefined, /*typeParameters*/ undefined, /*thisParameter*/ undefined, emptyArray, errorType, /*resolvedTypePredicate*/ undefined, 0, SignatureFlags.None); var resolvingSignature = createSignature(/*declaration*/ undefined, /*typeParameters*/ undefined, /*thisParameter*/ undefined, emptyArray, anyType, /*resolvedTypePredicate*/ undefined, 0, SignatureFlags.None); var silentNeverSignature = createSignature(/*declaration*/ undefined, /*typeParameters*/ undefined, /*thisParameter*/ undefined, emptyArray, silentNeverType, /*resolvedTypePredicate*/ undefined, 0, SignatureFlags.None); var enumNumberIndexInfo = createIndexInfo(numberType, stringType, /*isReadonly*/ true); var iterationTypesCache = new Map(); // cache for common IterationTypes instances var noIterationTypes: IterationTypes = { get yieldType(): Type { return Debug.fail("Not supported"); }, get returnType(): Type { return Debug.fail("Not supported"); }, get nextType(): Type { return Debug.fail("Not supported"); }, }; var anyIterationTypes = createIterationTypes(anyType, anyType, anyType); var asyncIterationTypesResolver: IterationTypesResolver = { iterableCacheKey: "iterationTypesOfAsyncIterable", iteratorCacheKey: "iterationTypesOfAsyncIterator", iteratorSymbolName: "asyncIterator", getGlobalIteratorType: getGlobalAsyncIteratorType, getGlobalIterableType: getGlobalAsyncIterableType, getGlobalIterableIteratorType: getGlobalAsyncIterableIteratorType, getGlobalIteratorObjectType: getGlobalAsyncIteratorObjectType, getGlobalGeneratorType: getGlobalAsyncGeneratorType, getGlobalBuiltinIteratorTypes: getGlobalBuiltinAsyncIteratorTypes, resolveIterationType: (type, errorNode) => getAwaitedType(type, errorNode, Diagnostics.Type_of_await_operand_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member), mustHaveANextMethodDiagnostic: Diagnostics.An_async_iterator_must_have_a_next_method, mustBeAMethodDiagnostic: Diagnostics.The_0_property_of_an_async_iterator_must_be_a_method, mustHaveAValueDiagnostic: Diagnostics.The_type_returned_by_the_0_method_of_an_async_iterator_must_be_a_promise_for_a_type_with_a_value_property, }; var syncIterationTypesResolver: IterationTypesResolver = { iterableCacheKey: "iterationTypesOfIterable", iteratorCacheKey: "iterationTypesOfIterator", iteratorSymbolName: "iterator", getGlobalIteratorType, getGlobalIterableType, getGlobalIterableIteratorType, getGlobalIteratorObjectType, getGlobalGeneratorType, getGlobalBuiltinIteratorTypes, resolveIterationType: (type, _errorNode) => type, mustHaveANextMethodDiagnostic: Diagnostics.An_iterator_must_have_a_next_method, mustBeAMethodDiagnostic: Diagnostics.The_0_property_of_an_iterator_must_be_a_method, mustHaveAValueDiagnostic: Diagnostics.The_type_returned_by_the_0_method_of_an_iterator_must_have_a_value_property, }; interface DuplicateInfoForSymbol { readonly firstFileLocations: Declaration[]; readonly secondFileLocations: Declaration[]; readonly isBlockScoped: boolean; } interface DuplicateInfoForFiles { readonly firstFile: SourceFile; readonly secondFile: SourceFile; /** Key is symbol name. */ readonly conflictingSymbols: Map; } /** Key is "/path/to/a.ts|/path/to/b.ts". */ var amalgamatedDuplicates: Map | undefined; var reverseMappedCache = new Map(); var reverseHomomorphicMappedCache = new Map(); var ambientModulesCache: Symbol[] | undefined; /** * List of every ambient module with a "*" wildcard. * Unlike other ambient modules, these can't be stored in `globals` because symbol tables only deal with exact matches. * This is only used if there is no exact match. */ var patternAmbientModules: PatternAmbientModule[]; var patternAmbientModuleAugmentations: Map | undefined; var globalObjectType: ObjectType; var globalFunctionType: ObjectType; var globalCallableFunctionType: ObjectType; var globalNewableFunctionType: ObjectType; var globalArrayType: GenericType; var globalReadonlyArrayType: GenericType; var globalStringType: ObjectType; var globalNumberType: ObjectType; var globalBooleanType: ObjectType; var globalRegExpType: ObjectType; var globalThisType: GenericType; var anyArrayType: Type; var autoArrayType: Type; var anyReadonlyArrayType: Type; var deferredGlobalNonNullableTypeAlias: Symbol; // The library files are only loaded when the feature is used. // This allows users to just specify library files they want to used through --lib // and they will not get an error from not having unrelated library files var deferredGlobalESSymbolConstructorSymbol: Symbol | undefined; var deferredGlobalESSymbolConstructorTypeSymbol: Symbol | undefined; var deferredGlobalESSymbolType: ObjectType | undefined; var deferredGlobalTypedPropertyDescriptorType: GenericType; var deferredGlobalPromiseType: GenericType | undefined; var deferredGlobalPromiseLikeType: GenericType | undefined; var deferredGlobalPromiseConstructorSymbol: Symbol | undefined; var deferredGlobalPromiseConstructorLikeType: ObjectType | undefined; var deferredGlobalIterableType: GenericType | undefined; var deferredGlobalIteratorType: GenericType | undefined; var deferredGlobalIterableIteratorType: GenericType | undefined; var deferredGlobalIteratorObjectType: GenericType | undefined; var deferredGlobalGeneratorType: GenericType | undefined; var deferredGlobalIteratorYieldResultType: GenericType | undefined; var deferredGlobalIteratorReturnResultType: GenericType | undefined; var deferredGlobalAsyncIterableType: GenericType | undefined; var deferredGlobalAsyncIteratorType: GenericType | undefined; var deferredGlobalAsyncIterableIteratorType: GenericType | undefined; var deferredGlobalBuiltinIteratorTypes: readonly GenericType[] | undefined; var deferredGlobalBuiltinAsyncIteratorTypes: readonly GenericType[] | undefined; var deferredGlobalAsyncIteratorObjectType: GenericType | undefined; var deferredGlobalAsyncGeneratorType: GenericType | undefined; var deferredGlobalTemplateStringsArrayType: ObjectType | undefined; var deferredGlobalImportMetaType: ObjectType; var deferredGlobalImportMetaExpressionType: ObjectType; var deferredGlobalImportCallOptionsType: ObjectType | undefined; var deferredGlobalImportAttributesType: ObjectType | undefined; var deferredGlobalDisposableType: ObjectType | undefined; var deferredGlobalAsyncDisposableType: ObjectType | undefined; var deferredGlobalExtractSymbol: Symbol | undefined; var deferredGlobalOmitSymbol: Symbol | undefined; var deferredGlobalAwaitedSymbol: Symbol | undefined; var deferredGlobalBigIntType: ObjectType | undefined; var deferredGlobalNaNSymbol: Symbol | undefined; var deferredGlobalRecordSymbol: Symbol | undefined; var deferredGlobalClassDecoratorContextType: GenericType | undefined; var deferredGlobalClassMethodDecoratorContextType: GenericType | undefined; var deferredGlobalClassGetterDecoratorContextType: GenericType | undefined; var deferredGlobalClassSetterDecoratorContextType: GenericType | undefined; var deferredGlobalClassAccessorDecoratorContextType: GenericType | undefined; var deferredGlobalClassAccessorDecoratorTargetType: GenericType | undefined; var deferredGlobalClassAccessorDecoratorResultType: GenericType | undefined; var deferredGlobalClassFieldDecoratorContextType: GenericType | undefined; var allPotentiallyUnusedIdentifiers = new Map(); // key is file name var flowLoopStart = 0; var flowLoopCount = 0; var sharedFlowCount = 0; var flowAnalysisDisabled = false; var flowInvocationCount = 0; var lastFlowNode: FlowNode | undefined; var lastFlowNodeReachable: boolean; var flowTypeCache: Type[] | undefined; var contextualTypeNodes: Node[] = []; var contextualTypes: (Type | undefined)[] = []; var contextualIsCache: boolean[] = []; var contextualTypeCount = 0; var contextualBindingPatterns: BindingPattern[] = []; var inferenceContextNodes: Node[] = []; var inferenceContexts: (InferenceContext | undefined)[] = []; var inferenceContextCount = 0; var emptyStringType = getStringLiteralType(""); var zeroType = getNumberLiteralType(0); var zeroBigIntType = getBigIntLiteralType({ negative: false, base10Value: "0" }); var resolutionTargets: TypeSystemEntity[] = []; var resolutionResults: boolean[] = []; var resolutionPropertyNames: TypeSystemPropertyName[] = []; var resolutionStart = 0; var inVarianceComputation = false; var suggestionCount = 0; var maximumSuggestionCount = 10; var mergedSymbols: Symbol[] = []; var symbolLinks: SymbolLinks[] = []; var nodeLinks: NodeLinks[] = []; var flowLoopCaches: Map[] = []; var flowLoopNodes: FlowNode[] = []; var flowLoopKeys: string[] = []; var flowLoopTypes: Type[][] = []; var sharedFlowNodes: FlowNode[] = []; var sharedFlowTypes: FlowType[] = []; var flowNodeReachable: (boolean | undefined)[] = []; var flowNodePostSuper: (boolean | undefined)[] = []; var potentialThisCollisions: Node[] = []; var potentialNewTargetCollisions: Node[] = []; var potentialWeakMapSetCollisions: Node[] = []; var potentialReflectCollisions: Node[] = []; var potentialUnusedRenamedBindingElementsInTypes: BindingElement[] = []; var awaitedTypeStack: number[] = []; var reverseMappedSourceStack: Type[] = []; var reverseMappedTargetStack: Type[] = []; var reverseExpandingFlags = ExpandingFlags.None; var diagnostics = createDiagnosticCollection(); var suggestionDiagnostics = createDiagnosticCollection(); var typeofType = createTypeofType(); var _jsxNamespace: __String; var _jsxFactoryEntity: EntityName | undefined; var subtypeRelation = new Map(); var strictSubtypeRelation = new Map(); var assignableRelation = new Map(); var comparableRelation = new Map(); var identityRelation = new Map(); var enumRelation = new Map(); // Extensions suggested for path imports when module resolution is node16 or higher. // The first element of each tuple is the extension a file has. // The second element of each tuple is the extension that should be used in a path import. // e.g. if we want to import file `foo.mts`, we should write `import {} from "./foo.mjs". var suggestedExtensions: [string, string][] = [ [".mts", ".mjs"], [".ts", ".js"], [".cts", ".cjs"], [".mjs", ".mjs"], [".js", ".js"], [".cjs", ".cjs"], [".tsx", compilerOptions.jsx === JsxEmit.Preserve ? ".jsx" : ".js"], [".jsx", ".jsx"], [".json", ".json"], ]; /* eslint-enable no-var */ initializeTypeChecker(); return checker; function isDefinitelyReferenceToGlobalSymbolObject(node: Node): boolean { if (!isPropertyAccessExpression(node)) return false; if (!isIdentifier(node.name)) return false; if (!isPropertyAccessExpression(node.expression) && !isIdentifier(node.expression)) return false; if (isIdentifier(node.expression)) { // Exactly `Symbol.something` and `Symbol` either does not resolve or definitely resolves to the global Symbol return idText(node.expression) === "Symbol" && getResolvedSymbol(node.expression) === (getGlobalSymbol("Symbol" as __String, SymbolFlags.Value | SymbolFlags.ExportValue, /*diagnostic*/ undefined) || unknownSymbol); } if (!isIdentifier(node.expression.expression)) return false; // Exactly `globalThis.Symbol.something` and `globalThis` resolves to the global `globalThis` return idText(node.expression.name) === "Symbol" && idText(node.expression.expression) === "globalThis" && getResolvedSymbol(node.expression.expression) === globalThisSymbol; } function getCachedType(key: string | undefined) { return key ? cachedTypes.get(key) : undefined; } function setCachedType(key: string | undefined, type: Type) { if (key) cachedTypes.set(key, type); return type; } function getJsxNamespace(location: Node | undefined): __String { if (location) { const file = getSourceFileOfNode(location); if (file) { if (isJsxOpeningFragment(location)) { if (file.localJsxFragmentNamespace) { return file.localJsxFragmentNamespace; } const jsxFragmentPragma = file.pragmas.get("jsxfrag"); if (jsxFragmentPragma) { const chosenPragma = isArray(jsxFragmentPragma) ? jsxFragmentPragma[0] : jsxFragmentPragma; file.localJsxFragmentFactory = parseIsolatedEntityName(chosenPragma.arguments.factory, languageVersion); visitNode(file.localJsxFragmentFactory, markAsSynthetic, isEntityName); if (file.localJsxFragmentFactory) { return file.localJsxFragmentNamespace = getFirstIdentifier(file.localJsxFragmentFactory).escapedText; } } const entity = getJsxFragmentFactoryEntity(location); if (entity) { file.localJsxFragmentFactory = entity; return file.localJsxFragmentNamespace = getFirstIdentifier(entity).escapedText; } } else { const localJsxNamespace = getLocalJsxNamespace(file); if (localJsxNamespace) { return file.localJsxNamespace = localJsxNamespace; } } } } if (!_jsxNamespace) { _jsxNamespace = "React" as __String; if (compilerOptions.jsxFactory) { _jsxFactoryEntity = parseIsolatedEntityName(compilerOptions.jsxFactory, languageVersion); visitNode(_jsxFactoryEntity, markAsSynthetic); if (_jsxFactoryEntity) { _jsxNamespace = getFirstIdentifier(_jsxFactoryEntity).escapedText; } } else if (compilerOptions.reactNamespace) { _jsxNamespace = escapeLeadingUnderscores(compilerOptions.reactNamespace); } } if (!_jsxFactoryEntity) { _jsxFactoryEntity = factory.createQualifiedName(factory.createIdentifier(unescapeLeadingUnderscores(_jsxNamespace)), "createElement"); } return _jsxNamespace; } function getLocalJsxNamespace(file: SourceFile): __String | undefined { if (file.localJsxNamespace) { return file.localJsxNamespace; } const jsxPragma = file.pragmas.get("jsx"); if (jsxPragma) { const chosenPragma = isArray(jsxPragma) ? jsxPragma[0] : jsxPragma; file.localJsxFactory = parseIsolatedEntityName(chosenPragma.arguments.factory, languageVersion); visitNode(file.localJsxFactory, markAsSynthetic, isEntityName); if (file.localJsxFactory) { return file.localJsxNamespace = getFirstIdentifier(file.localJsxFactory).escapedText; } } } function markAsSynthetic(node: T): VisitResult { setTextRangePosEnd(node, -1, -1); return visitEachChildWorker(node, markAsSynthetic, /*context*/ undefined); } function getEmitResolver(sourceFile: SourceFile, cancellationToken: CancellationToken, skipDiagnostics?: boolean) { // Ensure we have all the type information in place for this file so that all the // emitter questions of this resolver will return the right information. if (!skipDiagnostics) getDiagnostics(sourceFile, cancellationToken); return emitResolver; } function lookupOrIssueError(location: Node | undefined, message: DiagnosticMessage, ...args: DiagnosticArguments): Diagnostic { const diagnostic = location ? createDiagnosticForNode(location, message, ...args) : createCompilerDiagnostic(message, ...args); const existing = diagnostics.lookup(diagnostic); if (existing) { return existing; } else { diagnostics.add(diagnostic); return diagnostic; } } function errorSkippedOn(key: keyof CompilerOptions, location: Node | undefined, message: DiagnosticMessage, ...args: DiagnosticArguments): Diagnostic { const diagnostic = error(location, message, ...args); diagnostic.skippedOn = key; return diagnostic; } function createError(location: Node | undefined, message: DiagnosticMessage, ...args: DiagnosticArguments): Diagnostic { return location ? createDiagnosticForNode(location, message, ...args) : createCompilerDiagnostic(message, ...args); } function error(location: Node | undefined, message: DiagnosticMessage, ...args: DiagnosticArguments): Diagnostic { const diagnostic = createError(location, message, ...args); diagnostics.add(diagnostic); return diagnostic; } function addErrorOrSuggestion(isError: boolean, diagnostic: Diagnostic) { if (isError) { diagnostics.add(diagnostic); } else { suggestionDiagnostics.add({ ...diagnostic, category: DiagnosticCategory.Suggestion }); } } function errorOrSuggestion(isError: boolean, location: Node, message: DiagnosticMessage | DiagnosticMessageChain, ...args: DiagnosticArguments): void { // Pseudo-synthesized input node if (location.pos < 0 || location.end < 0) { if (!isError) { return; // Drop suggestions (we have no span to suggest on) } // Issue errors globally const file = getSourceFileOfNode(location); addErrorOrSuggestion(isError, "message" in message ? createFileDiagnostic(file, 0, 0, message, ...args) : createDiagnosticForFileFromMessageChain(file, message)); // eslint-disable-line local/no-in-operator return; } addErrorOrSuggestion(isError, "message" in message ? createDiagnosticForNode(location, message, ...args) : createDiagnosticForNodeFromMessageChain(getSourceFileOfNode(location), location, message)); // eslint-disable-line local/no-in-operator } function errorAndMaybeSuggestAwait( location: Node, maybeMissingAwait: boolean, message: DiagnosticMessage, ...args: DiagnosticArguments ): Diagnostic { const diagnostic = error(location, message, ...args); if (maybeMissingAwait) { const related = createDiagnosticForNode(location, Diagnostics.Did_you_forget_to_use_await); addRelatedInfo(diagnostic, related); } return diagnostic; } function addDeprecatedSuggestionWorker(declarations: Node | Node[], diagnostic: DiagnosticWithLocation) { const deprecatedTag = Array.isArray(declarations) ? forEach(declarations, getJSDocDeprecatedTag) : getJSDocDeprecatedTag(declarations); if (deprecatedTag) { addRelatedInfo( diagnostic, createDiagnosticForNode(deprecatedTag, Diagnostics.The_declaration_was_marked_as_deprecated_here), ); } // We call `addRelatedInfo()` before adding the diagnostic to prevent duplicates. suggestionDiagnostics.add(diagnostic); return diagnostic; } function isDeprecatedSymbol(symbol: Symbol) { const parentSymbol = getParentOfSymbol(symbol); if (parentSymbol && length(symbol.declarations) > 1) { return parentSymbol.flags & SymbolFlags.Interface ? some(symbol.declarations, isDeprecatedDeclaration) : every(symbol.declarations, isDeprecatedDeclaration); } return !!symbol.valueDeclaration && isDeprecatedDeclaration(symbol.valueDeclaration) || length(symbol.declarations) && every(symbol.declarations, isDeprecatedDeclaration); } function isDeprecatedDeclaration(declaration: Declaration) { return !!(getCombinedNodeFlagsCached(declaration) & NodeFlags.Deprecated); } function addDeprecatedSuggestion(location: Node, declarations: Node[], deprecatedEntity: string) { const diagnostic = createDiagnosticForNode(location, Diagnostics._0_is_deprecated, deprecatedEntity); return addDeprecatedSuggestionWorker(declarations, diagnostic); } function addDeprecatedSuggestionWithSignature(location: Node, declaration: Node, deprecatedEntity: string | undefined, signatureString: string) { const diagnostic = deprecatedEntity ? createDiagnosticForNode(location, Diagnostics.The_signature_0_of_1_is_deprecated, signatureString, deprecatedEntity) : createDiagnosticForNode(location, Diagnostics._0_is_deprecated, signatureString); return addDeprecatedSuggestionWorker(declaration, diagnostic); } function createSymbol(flags: SymbolFlags, name: __String, checkFlags?: CheckFlags) { symbolCount++; const symbol = new Symbol(flags | SymbolFlags.Transient, name) as TransientSymbol; symbol.links = new SymbolLinks() as TransientSymbolLinks; symbol.links.checkFlags = checkFlags || CheckFlags.None; return symbol; } function createParameter(name: __String, type: Type) { const symbol = createSymbol(SymbolFlags.FunctionScopedVariable, name); symbol.links.type = type; return symbol; } function createProperty(name: __String, type: Type) { const symbol = createSymbol(SymbolFlags.Property, name); symbol.links.type = type; return symbol; } function getExcludedSymbolFlags(flags: SymbolFlags): SymbolFlags { let result: SymbolFlags = 0; if (flags & SymbolFlags.BlockScopedVariable) result |= SymbolFlags.BlockScopedVariableExcludes; if (flags & SymbolFlags.FunctionScopedVariable) result |= SymbolFlags.FunctionScopedVariableExcludes; if (flags & SymbolFlags.Property) result |= SymbolFlags.PropertyExcludes; if (flags & SymbolFlags.EnumMember) result |= SymbolFlags.EnumMemberExcludes; if (flags & SymbolFlags.Function) result |= SymbolFlags.FunctionExcludes; if (flags & SymbolFlags.Class) result |= SymbolFlags.ClassExcludes; if (flags & SymbolFlags.Interface) result |= SymbolFlags.InterfaceExcludes; if (flags & SymbolFlags.RegularEnum) result |= SymbolFlags.RegularEnumExcludes; if (flags & SymbolFlags.ConstEnum) result |= SymbolFlags.ConstEnumExcludes; if (flags & SymbolFlags.ValueModule) result |= SymbolFlags.ValueModuleExcludes; if (flags & SymbolFlags.Method) result |= SymbolFlags.MethodExcludes; if (flags & SymbolFlags.GetAccessor) result |= SymbolFlags.GetAccessorExcludes; if (flags & SymbolFlags.SetAccessor) result |= SymbolFlags.SetAccessorExcludes; if (flags & SymbolFlags.TypeParameter) result |= SymbolFlags.TypeParameterExcludes; if (flags & SymbolFlags.TypeAlias) result |= SymbolFlags.TypeAliasExcludes; if (flags & SymbolFlags.Alias) result |= SymbolFlags.AliasExcludes; return result; } function recordMergedSymbol(target: Symbol, source: Symbol) { if (!source.mergeId) { source.mergeId = nextMergeId; nextMergeId++; } mergedSymbols[source.mergeId] = target; } function cloneSymbol(symbol: Symbol): TransientSymbol { const result = createSymbol(symbol.flags, symbol.escapedName); result.declarations = symbol.declarations ? symbol.declarations.slice() : []; result.parent = symbol.parent; if (symbol.valueDeclaration) result.valueDeclaration = symbol.valueDeclaration; if (symbol.constEnumOnlyModule) result.constEnumOnlyModule = true; if (symbol.members) result.members = new Map(symbol.members); if (symbol.exports) result.exports = new Map(symbol.exports); recordMergedSymbol(result, symbol); return result; } /** * Note: if target is transient, then it is mutable, and mergeSymbol with both mutate and return it. * If target is not transient, mergeSymbol will produce a transient clone, mutate that and return it. */ function mergeSymbol(target: Symbol, source: Symbol, unidirectional = false): Symbol { if ( !(target.flags & getExcludedSymbolFlags(source.flags)) || (source.flags | target.flags) & SymbolFlags.Assignment ) { if (source === target) { // This can happen when an export assigned namespace exports something also erroneously exported at the top level // See `declarationFileNoCrashOnExtraExportModifier` for an example return target; } if (!(target.flags & SymbolFlags.Transient)) { const resolvedTarget = resolveSymbol(target); if (resolvedTarget === unknownSymbol) { return source; } if ( !(resolvedTarget.flags & getExcludedSymbolFlags(source.flags)) || (source.flags | resolvedTarget.flags) & SymbolFlags.Assignment ) { target = cloneSymbol(resolvedTarget); } else { reportMergeSymbolError(target, source); return source; } } // Javascript static-property-assignment declarations always merge, even though they are also values if (source.flags & SymbolFlags.ValueModule && target.flags & SymbolFlags.ValueModule && target.constEnumOnlyModule && !source.constEnumOnlyModule) { // reset flag when merging instantiated module into value module that has only const enums target.constEnumOnlyModule = false; } target.flags |= source.flags; if (source.valueDeclaration) { setValueDeclaration(target, source.valueDeclaration); } addRange(target.declarations, source.declarations); if (source.members) { if (!target.members) target.members = createSymbolTable(); mergeSymbolTable(target.members, source.members, unidirectional); } if (source.exports) { if (!target.exports) target.exports = createSymbolTable(); mergeSymbolTable(target.exports, source.exports, unidirectional, target); } if (!unidirectional) { recordMergedSymbol(target, source); } } else if (target.flags & SymbolFlags.NamespaceModule) { // Do not report an error when merging `var globalThis` with the built-in `globalThis`, // as we will already report a "Declaration name conflicts..." error, and this error // won't make much sense. if (target !== globalThisSymbol) { error( source.declarations && getNameOfDeclaration(source.declarations[0]), Diagnostics.Cannot_augment_module_0_with_value_exports_because_it_resolves_to_a_non_module_entity, symbolToString(target), ); } } else { reportMergeSymbolError(target, source); } return target; function reportMergeSymbolError(target: Symbol, source: Symbol) { const isEitherEnum = !!(target.flags & SymbolFlags.Enum || source.flags & SymbolFlags.Enum); const isEitherBlockScoped = !!(target.flags & SymbolFlags.BlockScopedVariable || source.flags & SymbolFlags.BlockScopedVariable); const message = isEitherEnum ? Diagnostics.Enum_declarations_can_only_merge_with_namespace_or_other_enum_declarations : isEitherBlockScoped ? Diagnostics.Cannot_redeclare_block_scoped_variable_0 : Diagnostics.Duplicate_identifier_0; const sourceSymbolFile = source.declarations && getSourceFileOfNode(source.declarations[0]); const targetSymbolFile = target.declarations && getSourceFileOfNode(target.declarations[0]); const isSourcePlainJs = isPlainJsFile(sourceSymbolFile, compilerOptions.checkJs); const isTargetPlainJs = isPlainJsFile(targetSymbolFile, compilerOptions.checkJs); const symbolName = symbolToString(source); // Collect top-level duplicate identifier errors into one mapping, so we can then merge their diagnostics if there are a bunch if (sourceSymbolFile && targetSymbolFile && amalgamatedDuplicates && !isEitherEnum && sourceSymbolFile !== targetSymbolFile) { const firstFile = comparePaths(sourceSymbolFile.path, targetSymbolFile.path) === Comparison.LessThan ? sourceSymbolFile : targetSymbolFile; const secondFile = firstFile === sourceSymbolFile ? targetSymbolFile : sourceSymbolFile; const filesDuplicates = getOrUpdate(amalgamatedDuplicates, `${firstFile.path}|${secondFile.path}`, (): DuplicateInfoForFiles => ({ firstFile, secondFile, conflictingSymbols: new Map() })); const conflictingSymbolInfo = getOrUpdate(filesDuplicates.conflictingSymbols, symbolName, (): DuplicateInfoForSymbol => ({ isBlockScoped: isEitherBlockScoped, firstFileLocations: [], secondFileLocations: [] })); if (!isSourcePlainJs) addDuplicateLocations(conflictingSymbolInfo.firstFileLocations, source); if (!isTargetPlainJs) addDuplicateLocations(conflictingSymbolInfo.secondFileLocations, target); } else { if (!isSourcePlainJs) addDuplicateDeclarationErrorsForSymbols(source, message, symbolName, target); if (!isTargetPlainJs) addDuplicateDeclarationErrorsForSymbols(target, message, symbolName, source); } } function addDuplicateLocations(locs: Declaration[], symbol: Symbol): void { if (symbol.declarations) { for (const decl of symbol.declarations) { pushIfUnique(locs, decl); } } } } function addDuplicateDeclarationErrorsForSymbols(target: Symbol, message: DiagnosticMessage, symbolName: string, source: Symbol) { forEach(target.declarations, node => { addDuplicateDeclarationError(node, message, symbolName, source.declarations); }); } function addDuplicateDeclarationError(node: Declaration, message: DiagnosticMessage, symbolName: string, relatedNodes: readonly Declaration[] | undefined) { const errorNode = (getExpandoInitializer(node, /*isPrototypeAssignment*/ false) ? getNameOfExpando(node) : getNameOfDeclaration(node)) || node; const err = lookupOrIssueError(errorNode, message, symbolName); for (const relatedNode of relatedNodes || emptyArray) { const adjustedNode = (getExpandoInitializer(relatedNode, /*isPrototypeAssignment*/ false) ? getNameOfExpando(relatedNode) : getNameOfDeclaration(relatedNode)) || relatedNode; if (adjustedNode === errorNode) continue; err.relatedInformation = err.relatedInformation || []; const leadingMessage = createDiagnosticForNode(adjustedNode, Diagnostics._0_was_also_declared_here, symbolName); const followOnMessage = createDiagnosticForNode(adjustedNode, Diagnostics.and_here); if (length(err.relatedInformation) >= 5 || some(err.relatedInformation, r => compareDiagnostics(r, followOnMessage) === Comparison.EqualTo || compareDiagnostics(r, leadingMessage) === Comparison.EqualTo)) continue; addRelatedInfo(err, !length(err.relatedInformation) ? leadingMessage : followOnMessage); } } function combineSymbolTables(first: SymbolTable | undefined, second: SymbolTable | undefined): SymbolTable | undefined { if (!first?.size) return second; if (!second?.size) return first; const combined = createSymbolTable(); mergeSymbolTable(combined, first); mergeSymbolTable(combined, second); return combined; } function mergeSymbolTable(target: SymbolTable, source: SymbolTable, unidirectional = false, mergedParent?: Symbol) { source.forEach((sourceSymbol, id) => { const targetSymbol = target.get(id); const merged = targetSymbol ? mergeSymbol(targetSymbol, sourceSymbol, unidirectional) : getMergedSymbol(sourceSymbol); if (mergedParent && targetSymbol) { // If a merge was performed on the target symbol, set its parent to the merged parent that initiated the merge // of its exports. Otherwise, `merged` came only from `sourceSymbol` and can keep its parent: // // // a.ts // export interface A { x: number; } // // // b.ts // declare module "./a" { // interface A { y: number; } // interface B {} // } // // When merging the module augmentation into a.ts, the symbol for `A` will itself be merged, so its parent // should be the merged module symbol. But the symbol for `B` has only one declaration, so its parent should // be the module augmentation symbol, which contains its only declaration. merged.parent = mergedParent; } target.set(id, merged); }); } function mergeModuleAugmentation(moduleName: StringLiteral | Identifier): void { const moduleAugmentation = moduleName.parent as ModuleDeclaration; if (moduleAugmentation.symbol.declarations?.[0] !== moduleAugmentation) { // this is a combined symbol for multiple augmentations within the same file. // its symbol already has accumulated information for all declarations // so we need to add it just once - do the work only for first declaration Debug.assert(moduleAugmentation.symbol.declarations!.length > 1); return; } if (isGlobalScopeAugmentation(moduleAugmentation)) { mergeSymbolTable(globals, moduleAugmentation.symbol.exports!); } else { // find a module that about to be augmented // do not validate names of augmentations that are defined in ambient context const moduleNotFoundError = !(moduleName.parent.parent.flags & NodeFlags.Ambient) ? Diagnostics.Invalid_module_name_in_augmentation_module_0_cannot_be_found : undefined; let mainModule = resolveExternalModuleNameWorker(moduleName, moduleName, moduleNotFoundError, /*ignoreErrors*/ false, /*isForAugmentation*/ true); if (!mainModule) { return; } // obtain item referenced by 'export=' mainModule = resolveExternalModuleSymbol(mainModule); if (mainModule.flags & SymbolFlags.Namespace) { // If we're merging an augmentation to a pattern ambient module, we want to // perform the merge unidirectionally from the augmentation ('a.foo') to // the pattern ('*.foo'), so that 'getMergedSymbol()' on a.foo gives you // all the exports both from the pattern and from the augmentation, but // 'getMergedSymbol()' on *.foo only gives you exports from *.foo. if (some(patternAmbientModules, module => mainModule === module.symbol)) { const merged = mergeSymbol(moduleAugmentation.symbol, mainModule, /*unidirectional*/ true); if (!patternAmbientModuleAugmentations) { patternAmbientModuleAugmentations = new Map(); } // moduleName will be a StringLiteral since this is not `declare global`. patternAmbientModuleAugmentations.set((moduleName as StringLiteral).text, merged); } else { if (mainModule.exports?.get(InternalSymbolName.ExportStar) && moduleAugmentation.symbol.exports?.size) { // We may need to merge the module augmentation's exports into the target symbols of the resolved exports const resolvedExports = getResolvedMembersOrExportsOfSymbol(mainModule, MembersOrExportsResolutionKind.resolvedExports); for (const [key, value] of arrayFrom(moduleAugmentation.symbol.exports.entries())) { if (resolvedExports.has(key) && !mainModule.exports.has(key)) { mergeSymbol(resolvedExports.get(key)!, value); } } } mergeSymbol(mainModule, moduleAugmentation.symbol); } } else { // moduleName will be a StringLiteral since this is not `declare global`. error(moduleName, Diagnostics.Cannot_augment_module_0_because_it_resolves_to_a_non_module_entity, (moduleName as StringLiteral).text); } } } function addUndefinedToGlobalsOrErrorOnRedeclaration() { const name = undefinedSymbol.escapedName; const targetSymbol = globals.get(name); if (targetSymbol) { forEach(targetSymbol.declarations, declaration => { // checkTypeNameIsReserved will have added better diagnostics for type declarations. if (!isTypeDeclaration(declaration)) { diagnostics.add(createDiagnosticForNode(declaration, Diagnostics.Declaration_name_conflicts_with_built_in_global_identifier_0, unescapeLeadingUnderscores(name))); } }); } else { globals.set(name, undefinedSymbol); } } function getSymbolLinks(symbol: Symbol): SymbolLinks { if (symbol.flags & SymbolFlags.Transient) return (symbol as TransientSymbol).links; const id = getSymbolId(symbol); return symbolLinks[id] ??= new SymbolLinks(); } function getNodeLinks(node: Node): NodeLinks { const nodeId = getNodeId(node); return nodeLinks[nodeId] || (nodeLinks[nodeId] = new (NodeLinks as any)()); } function getSymbol(symbols: SymbolTable, name: __String, meaning: SymbolFlags): Symbol | undefined { if (meaning) { const symbol = getMergedSymbol(symbols.get(name)); if (symbol) { if (symbol.flags & meaning) { return symbol; } if (symbol.flags & SymbolFlags.Alias) { const targetFlags = getSymbolFlags(symbol); // `targetFlags` will be `SymbolFlags.All` if an error occurred in alias resolution; this avoids cascading errors if (targetFlags & meaning) { return symbol; } } } } // return undefined if we can't find a symbol. } /** * Get symbols that represent parameter-property-declaration as parameter and as property declaration * @param parameter a parameterDeclaration node * @param parameterName a name of the parameter to get the symbols for. * @return a tuple of two symbols */ function getSymbolsOfParameterPropertyDeclaration(parameter: ParameterPropertyDeclaration, parameterName: __String): [Symbol, Symbol] { const constructorDeclaration = parameter.parent; const classDeclaration = parameter.parent.parent; const parameterSymbol = getSymbol(constructorDeclaration.locals!, parameterName, SymbolFlags.Value); const propertySymbol = getSymbol(getMembersOfSymbol(classDeclaration.symbol), parameterName, SymbolFlags.Value); if (parameterSymbol && propertySymbol) { return [parameterSymbol, propertySymbol]; } return Debug.fail("There should exist two symbols, one as property declaration and one as parameter declaration"); } function isBlockScopedNameDeclaredBeforeUse(declaration: Declaration, usage: Node): boolean { const declarationFile = getSourceFileOfNode(declaration); const useFile = getSourceFileOfNode(usage); const declContainer = getEnclosingBlockScopeContainer(declaration); if (declarationFile !== useFile) { if ( (moduleKind && (declarationFile.externalModuleIndicator || useFile.externalModuleIndicator)) || (!compilerOptions.outFile) || isInTypeQuery(usage) || declaration.flags & NodeFlags.Ambient ) { // nodes are in different files and order cannot be determined return true; } // declaration is after usage // can be legal if usage is deferred (i.e. inside function or in initializer of instance property) if (isUsedInFunctionOrInstanceProperty(usage, declaration)) { return true; } const sourceFiles = host.getSourceFiles(); return sourceFiles.indexOf(declarationFile) <= sourceFiles.indexOf(useFile); } // deferred usage in a type context is always OK regardless of the usage position: if (!!(usage.flags & NodeFlags.JSDoc) || isInTypeQuery(usage) || isInAmbientOrTypeNode(usage)) { return true; } if (declaration.pos <= usage.pos && !(isPropertyDeclaration(declaration) && isThisProperty(usage.parent) && !declaration.initializer && !declaration.exclamationToken)) { // declaration is before usage if (declaration.kind === SyntaxKind.BindingElement) { // still might be illegal if declaration and usage are both binding elements (eg var [a = b, b = b] = [1, 2]) const errorBindingElement = getAncestor(usage, SyntaxKind.BindingElement) as BindingElement; if (errorBindingElement) { return findAncestor(errorBindingElement, isBindingElement) !== findAncestor(declaration, isBindingElement) || declaration.pos < errorBindingElement.pos; } // or it might be illegal if usage happens before parent variable is declared (eg var [a] = a) return isBlockScopedNameDeclaredBeforeUse(getAncestor(declaration, SyntaxKind.VariableDeclaration) as Declaration, usage); } else if (declaration.kind === SyntaxKind.VariableDeclaration) { // still might be illegal if usage is in the initializer of the variable declaration (eg var a = a) return !isImmediatelyUsedInInitializerOfBlockScopedVariable(declaration as VariableDeclaration, usage); } else if (isClassLike(declaration)) { // still might be illegal if the usage is within a computed property name in the class (eg class A { static p = "a"; [A.p]() {} }) // or when used within a decorator in the class (e.g. `@dec(A.x) class A { static x = "x" }`), // except when used in a function that is not an IIFE (e.g., `@dec(() => A.x) class A { ... }`) const container = findAncestor(usage, n => n === declaration ? "quit" : isComputedPropertyName(n) ? n.parent.parent === declaration : !legacyDecorators && isDecorator(n) && (n.parent === declaration || isMethodDeclaration(n.parent) && n.parent.parent === declaration || isGetOrSetAccessorDeclaration(n.parent) && n.parent.parent === declaration || isPropertyDeclaration(n.parent) && n.parent.parent === declaration || isParameter(n.parent) && n.parent.parent.parent === declaration)); if (!container) { return true; } if (!legacyDecorators && isDecorator(container)) { return !!findAncestor(usage, n => n === container ? "quit" : isFunctionLike(n) && !getImmediatelyInvokedFunctionExpression(n)); } return false; } else if (isPropertyDeclaration(declaration)) { // still might be illegal if a self-referencing property initializer (eg private x = this.x) return !isPropertyImmediatelyReferencedWithinDeclaration(declaration, usage, /*stopAtAnyPropertyDeclaration*/ false); } else if (isParameterPropertyDeclaration(declaration, declaration.parent)) { // foo = this.bar is illegal in emitStandardClassFields when bar is a parameter property return !(emitStandardClassFields && getContainingClass(declaration) === getContainingClass(usage) && isUsedInFunctionOrInstanceProperty(usage, declaration)); } return true; } // declaration is after usage, but it can still be legal if usage is deferred: // 1. inside an export specifier // 2. inside a function // 3. inside an instance property initializer, a reference to a non-instance property // (except when emitStandardClassFields: true and the reference is to a parameter property) // 4. inside a static property initializer, a reference to a static method in the same class // 5. inside a TS export= declaration (since we will move the export statement during emit to avoid TDZ) if (usage.parent.kind === SyntaxKind.ExportSpecifier || (usage.parent.kind === SyntaxKind.ExportAssignment && (usage.parent as ExportAssignment).isExportEquals)) { // export specifiers do not use the variable, they only make it available for use return true; } // When resolving symbols for exports, the `usage` location passed in can be the export site directly if (usage.kind === SyntaxKind.ExportAssignment && (usage as ExportAssignment).isExportEquals) { return true; } if (isUsedInFunctionOrInstanceProperty(usage, declaration)) { if ( emitStandardClassFields && getContainingClass(declaration) && (isPropertyDeclaration(declaration) || isParameterPropertyDeclaration(declaration, declaration.parent)) ) { return !isPropertyImmediatelyReferencedWithinDeclaration(declaration, usage, /*stopAtAnyPropertyDeclaration*/ true); } else { return true; } } return false; function isImmediatelyUsedInInitializerOfBlockScopedVariable(declaration: VariableDeclaration, usage: Node): boolean { switch (declaration.parent.parent.kind) { case SyntaxKind.VariableStatement: case SyntaxKind.ForStatement: case SyntaxKind.ForOfStatement: // variable statement/for/for-of statement case, // use site should not be inside variable declaration (initializer of declaration or binding element) if (isSameScopeDescendentOf(usage, declaration, declContainer)) { return true; } break; } // ForIn/ForOf case - use site should not be used in expression part const grandparent = declaration.parent.parent; return isForInOrOfStatement(grandparent) && isSameScopeDescendentOf(usage, grandparent.expression, declContainer); } function isUsedInFunctionOrInstanceProperty(usage: Node, declaration: Node): boolean { return !!findAncestor(usage, current => { if (current === declContainer) { return "quit"; } if (isFunctionLike(current)) { return true; } if (isClassStaticBlockDeclaration(current)) { return declaration.pos < usage.pos; } const propertyDeclaration = tryCast(current.parent, isPropertyDeclaration); if (propertyDeclaration) { const initializerOfProperty = propertyDeclaration.initializer === current; if (initializerOfProperty) { if (isStatic(current.parent)) { if (declaration.kind === SyntaxKind.MethodDeclaration) { return true; } if (isPropertyDeclaration(declaration) && getContainingClass(usage) === getContainingClass(declaration)) { const propName = declaration.name; if (isIdentifier(propName) || isPrivateIdentifier(propName)) { const type = getTypeOfSymbol(getSymbolOfDeclaration(declaration)); const staticBlocks = filter(declaration.parent.members, isClassStaticBlockDeclaration); if (isPropertyInitializedInStaticBlocks(propName, type, staticBlocks, declaration.parent.pos, current.pos)) { return true; } } } } else { const isDeclarationInstanceProperty = declaration.kind === SyntaxKind.PropertyDeclaration && !isStatic(declaration); if (!isDeclarationInstanceProperty || getContainingClass(usage) !== getContainingClass(declaration)) { return true; } } } } return false; }); } /** stopAtAnyPropertyDeclaration is used for detecting ES-standard class field use-before-def errors */ function isPropertyImmediatelyReferencedWithinDeclaration(declaration: PropertyDeclaration | ParameterPropertyDeclaration, usage: Node, stopAtAnyPropertyDeclaration: boolean) { // always legal if usage is after declaration if (usage.end > declaration.end) { return false; } // still might be legal if usage is deferred (e.g. x: any = () => this.x) // otherwise illegal if immediately referenced within the declaration (e.g. x: any = this.x) const ancestorChangingReferenceScope = findAncestor(usage, (node: Node) => { if (node === declaration) { return "quit"; } switch (node.kind) { case SyntaxKind.ArrowFunction: return true; case SyntaxKind.PropertyDeclaration: // even when stopping at any property declaration, they need to come from the same class return stopAtAnyPropertyDeclaration && (isPropertyDeclaration(declaration) && node.parent === declaration.parent || isParameterPropertyDeclaration(declaration, declaration.parent) && node.parent === declaration.parent.parent) ? "quit" : true; case SyntaxKind.Block: switch (node.parent.kind) { case SyntaxKind.GetAccessor: case SyntaxKind.MethodDeclaration: case SyntaxKind.SetAccessor: return true; default: return false; } default: return false; } }); return ancestorChangingReferenceScope === undefined; } } function getRequiresScopeChangeCache(node: FunctionLikeDeclaration) { return getNodeLinks(node).declarationRequiresScopeChange; } function setRequiresScopeChangeCache(node: FunctionLikeDeclaration, value: boolean) { getNodeLinks(node).declarationRequiresScopeChange = value; } // The invalid initializer error is needed in two situation: // 1. When result is undefined, after checking for a missing "this." // 2. When result is defined function checkAndReportErrorForInvalidInitializer(errorLocation: Node | undefined, name: __String, propertyWithInvalidInitializer: PropertyDeclaration, result: Symbol | undefined) { if (!emitStandardClassFields) { if (errorLocation && !result && checkAndReportErrorForMissingPrefix(errorLocation, name, name)) { return true; } // We have a match, but the reference occurred within a property initializer and the identifier also binds // to a local variable in the constructor where the code will be emitted. Note that this is actually allowed // with emitStandardClassFields because the scope semantics are different. error( errorLocation, errorLocation && propertyWithInvalidInitializer.type && textRangeContainsPositionInclusive(propertyWithInvalidInitializer.type, errorLocation.pos) ? Diagnostics.Type_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor : Diagnostics.Initializer_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor, declarationNameToString(propertyWithInvalidInitializer.name), diagnosticName(name), ); return true; } return false; } function onFailedToResolveSymbol( errorLocation: Node | undefined, nameArg: __String | Identifier, meaning: SymbolFlags, nameNotFoundMessage: DiagnosticMessage, ) { const name = isString(nameArg) ? nameArg : (nameArg as Identifier).escapedText; addLazyDiagnostic(() => { if ( !errorLocation || errorLocation.parent.kind !== SyntaxKind.JSDocLink && !checkAndReportErrorForMissingPrefix(errorLocation, name, nameArg) && !checkAndReportErrorForExtendingInterface(errorLocation) && !checkAndReportErrorForUsingTypeAsNamespace(errorLocation, name, meaning) && !checkAndReportErrorForExportingPrimitiveType(errorLocation, name) && !checkAndReportErrorForUsingNamespaceAsTypeOrValue(errorLocation, name, meaning) && !checkAndReportErrorForUsingTypeAsValue(errorLocation, name, meaning) && !checkAndReportErrorForUsingValueAsType(errorLocation, name, meaning) ) { let suggestion: Symbol | undefined; let suggestedLib: string | undefined; // Report missing lib first if (nameArg) { suggestedLib = getSuggestedLibForNonExistentName(nameArg); if (suggestedLib) { error(errorLocation, nameNotFoundMessage, diagnosticName(nameArg), suggestedLib); } } // then spelling suggestions if (!suggestedLib && suggestionCount < maximumSuggestionCount) { suggestion = getSuggestedSymbolForNonexistentSymbol(errorLocation, name, meaning); const isGlobalScopeAugmentationDeclaration = suggestion?.valueDeclaration && isAmbientModule(suggestion.valueDeclaration) && isGlobalScopeAugmentation(suggestion.valueDeclaration); if (isGlobalScopeAugmentationDeclaration) { suggestion = undefined; } if (suggestion) { const suggestionName = symbolToString(suggestion); const isUncheckedJS = isUncheckedJSSuggestion(errorLocation, suggestion, /*excludeClasses*/ false); const message = meaning === SymbolFlags.Namespace || nameArg && typeof nameArg !== "string" && nodeIsSynthesized(nameArg) ? Diagnostics.Cannot_find_namespace_0_Did_you_mean_1 : isUncheckedJS ? Diagnostics.Could_not_find_name_0_Did_you_mean_1 : Diagnostics.Cannot_find_name_0_Did_you_mean_1; const diagnostic = createError(errorLocation, message, diagnosticName(nameArg), suggestionName); diagnostic.canonicalHead = getCanonicalDiagnostic(nameNotFoundMessage, diagnosticName(nameArg)); addErrorOrSuggestion(!isUncheckedJS, diagnostic); if (suggestion.valueDeclaration) { addRelatedInfo( diagnostic, createDiagnosticForNode(suggestion.valueDeclaration, Diagnostics._0_is_declared_here, suggestionName), ); } } } // And then fall back to unspecified "not found" if (!suggestion && !suggestedLib && nameArg) { error(errorLocation, nameNotFoundMessage, diagnosticName(nameArg)); } suggestionCount++; } }); } function onSuccessfullyResolvedSymbol( errorLocation: Node | undefined, result: Symbol, meaning: SymbolFlags, lastLocation: Node | undefined, associatedDeclarationForContainingInitializerOrBindingName: ParameterDeclaration | BindingElement | undefined, withinDeferredContext: boolean, ) { addLazyDiagnostic(() => { const name = result.escapedName; const isInExternalModule = lastLocation && isSourceFile(lastLocation) && isExternalOrCommonJsModule(lastLocation); // Only check for block-scoped variable if we have an error location and are looking for the // name with variable meaning // For example, // declare module foo { // interface bar {} // } // const foo/*1*/: foo/*2*/.bar; // The foo at /*1*/ and /*2*/ will share same symbol with two meanings: // block-scoped variable and namespace module. However, only when we // try to resolve name in /*1*/ which is used in variable position, // we want to check for block-scoped if ( errorLocation && (meaning & SymbolFlags.BlockScopedVariable || ((meaning & SymbolFlags.Class || meaning & SymbolFlags.Enum) && (meaning & SymbolFlags.Value) === SymbolFlags.Value)) ) { const exportOrLocalSymbol = getExportSymbolOfValueSymbolIfExported(result); if (exportOrLocalSymbol.flags & SymbolFlags.BlockScopedVariable || exportOrLocalSymbol.flags & SymbolFlags.Class || exportOrLocalSymbol.flags & SymbolFlags.Enum) { checkResolvedBlockScopedVariable(exportOrLocalSymbol, errorLocation); } } // If we're in an external module, we can't reference value symbols created from UMD export declarations if (isInExternalModule && (meaning & SymbolFlags.Value) === SymbolFlags.Value && !(errorLocation!.flags & NodeFlags.JSDoc)) { const merged = getMergedSymbol(result); if (length(merged.declarations) && every(merged.declarations, d => isNamespaceExportDeclaration(d) || isSourceFile(d) && !!d.symbol.globalExports)) { errorOrSuggestion(!compilerOptions.allowUmdGlobalAccess, errorLocation!, Diagnostics._0_refers_to_a_UMD_global_but_the_current_file_is_a_module_Consider_adding_an_import_instead, unescapeLeadingUnderscores(name)); } } // If we're in a parameter initializer or binding name, we can't reference the values of the parameter whose initializer we're within or parameters to the right if (associatedDeclarationForContainingInitializerOrBindingName && !withinDeferredContext && (meaning & SymbolFlags.Value) === SymbolFlags.Value) { const candidate = getMergedSymbol(getLateBoundSymbol(result)); const root = getRootDeclaration(associatedDeclarationForContainingInitializerOrBindingName) as ParameterDeclaration; // A parameter initializer or binding pattern initializer within a parameter cannot refer to itself if (candidate === getSymbolOfDeclaration(associatedDeclarationForContainingInitializerOrBindingName)) { error(errorLocation, Diagnostics.Parameter_0_cannot_reference_itself, declarationNameToString(associatedDeclarationForContainingInitializerOrBindingName.name)); } // And it cannot refer to any declarations which come after it else if (candidate.valueDeclaration && candidate.valueDeclaration.pos > associatedDeclarationForContainingInitializerOrBindingName.pos && root.parent.locals && getSymbol(root.parent.locals, candidate.escapedName, meaning) === candidate) { error(errorLocation, Diagnostics.Parameter_0_cannot_reference_identifier_1_declared_after_it, declarationNameToString(associatedDeclarationForContainingInitializerOrBindingName.name), declarationNameToString(errorLocation as Identifier)); } } if (errorLocation && meaning & SymbolFlags.Value && result.flags & SymbolFlags.Alias && !(result.flags & SymbolFlags.Value) && !isValidTypeOnlyAliasUseSite(errorLocation)) { const typeOnlyDeclaration = getTypeOnlyAliasDeclaration(result, SymbolFlags.Value); if (typeOnlyDeclaration) { const message = typeOnlyDeclaration.kind === SyntaxKind.ExportSpecifier || typeOnlyDeclaration.kind === SyntaxKind.ExportDeclaration || typeOnlyDeclaration.kind === SyntaxKind.NamespaceExport ? Diagnostics._0_cannot_be_used_as_a_value_because_it_was_exported_using_export_type : Diagnostics._0_cannot_be_used_as_a_value_because_it_was_imported_using_import_type; const unescapedName = unescapeLeadingUnderscores(name); addTypeOnlyDeclarationRelatedInfo( error(errorLocation, message, unescapedName), typeOnlyDeclaration, unescapedName, ); } } // Look at 'compilerOptions.isolatedModules' and not 'getIsolatedModules(...)' (which considers 'verbatimModuleSyntax') // here because 'verbatimModuleSyntax' will already have an error for importing a type without 'import type'. if (compilerOptions.isolatedModules && result && isInExternalModule && (meaning & SymbolFlags.Value) === SymbolFlags.Value) { const isGlobal = getSymbol(globals, name, meaning) === result; const nonValueSymbol = isGlobal && isSourceFile(lastLocation) && lastLocation.locals && getSymbol(lastLocation.locals, name, ~SymbolFlags.Value); if (nonValueSymbol) { const importDecl = nonValueSymbol.declarations?.find(d => d.kind === SyntaxKind.ImportSpecifier || d.kind === SyntaxKind.ImportClause || d.kind === SyntaxKind.NamespaceImport || d.kind === SyntaxKind.ImportEqualsDeclaration); if (importDecl && !isTypeOnlyImportDeclaration(importDecl)) { error(importDecl, Diagnostics.Import_0_conflicts_with_global_value_used_in_this_file_so_must_be_declared_with_a_type_only_import_when_isolatedModules_is_enabled, unescapeLeadingUnderscores(name)); } } } }); } function addTypeOnlyDeclarationRelatedInfo(diagnostic: Diagnostic, typeOnlyDeclaration: TypeOnlyCompatibleAliasDeclaration | undefined, unescapedName: string) { if (!typeOnlyDeclaration) return diagnostic; return addRelatedInfo( diagnostic, createDiagnosticForNode( typeOnlyDeclaration, typeOnlyDeclaration.kind === SyntaxKind.ExportSpecifier || typeOnlyDeclaration.kind === SyntaxKind.ExportDeclaration || typeOnlyDeclaration.kind === SyntaxKind.NamespaceExport ? Diagnostics._0_was_exported_here : Diagnostics._0_was_imported_here, unescapedName, ), ); } function diagnosticName(nameArg: __String | Identifier | PrivateIdentifier) { return isString(nameArg) ? unescapeLeadingUnderscores(nameArg as __String) : declarationNameToString(nameArg as Identifier); } function checkAndReportErrorForMissingPrefix(errorLocation: Node, name: __String, nameArg: __String | Identifier): boolean { if (!isIdentifier(errorLocation) || errorLocation.escapedText !== name || isTypeReferenceIdentifier(errorLocation) || isInTypeQuery(errorLocation)) { return false; } const container = getThisContainer(errorLocation, /*includeArrowFunctions*/ false, /*includeClassComputedPropertyName*/ false); let location: Node = container; while (location) { if (isClassLike(location.parent)) { const classSymbol = getSymbolOfDeclaration(location.parent); if (!classSymbol) { break; } // Check to see if a static member exists. const constructorType = getTypeOfSymbol(classSymbol); if (getPropertyOfType(constructorType, name)) { error(errorLocation, Diagnostics.Cannot_find_name_0_Did_you_mean_the_static_member_1_0, diagnosticName(nameArg), symbolToString(classSymbol)); return true; } // No static member is present. // Check if we're in an instance method and look for a relevant instance member. if (location === container && !isStatic(location)) { const instanceType = (getDeclaredTypeOfSymbol(classSymbol) as InterfaceType).thisType!; // TODO: GH#18217 if (getPropertyOfType(instanceType, name)) { error(errorLocation, Diagnostics.Cannot_find_name_0_Did_you_mean_the_instance_member_this_0, diagnosticName(nameArg)); return true; } } } location = location.parent; } return false; } function checkAndReportErrorForExtendingInterface(errorLocation: Node): boolean { const expression = getEntityNameForExtendingInterface(errorLocation); if (expression && resolveEntityName(expression, SymbolFlags.Interface, /*ignoreErrors*/ true)) { error(errorLocation, Diagnostics.Cannot_extend_an_interface_0_Did_you_mean_implements, getTextOfNode(expression)); return true; } return false; } /** * Climbs up parents to an ExpressionWithTypeArguments, and returns its expression, * but returns undefined if that expression is not an EntityNameExpression. */ function getEntityNameForExtendingInterface(node: Node): EntityNameExpression | undefined { switch (node.kind) { case SyntaxKind.Identifier: case SyntaxKind.PropertyAccessExpression: return node.parent ? getEntityNameForExtendingInterface(node.parent) : undefined; case SyntaxKind.ExpressionWithTypeArguments: if (isEntityNameExpression((node as ExpressionWithTypeArguments).expression)) { return (node as ExpressionWithTypeArguments).expression as EntityNameExpression; } // falls through default: return undefined; } } function checkAndReportErrorForUsingTypeAsNamespace(errorLocation: Node, name: __String, meaning: SymbolFlags): boolean { const namespaceMeaning = SymbolFlags.Namespace | (isInJSFile(errorLocation) ? SymbolFlags.Value : 0); if (meaning === namespaceMeaning) { const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.Type & ~namespaceMeaning, /*nameNotFoundMessage*/ undefined, /*isUse*/ false)); const parent = errorLocation.parent; if (symbol) { if (isQualifiedName(parent)) { Debug.assert(parent.left === errorLocation, "Should only be resolving left side of qualified name as a namespace"); const propName = parent.right.escapedText; const propType = getPropertyOfType(getDeclaredTypeOfSymbol(symbol), propName); if (propType) { error( parent, Diagnostics.Cannot_access_0_1_because_0_is_a_type_but_not_a_namespace_Did_you_mean_to_retrieve_the_type_of_the_property_1_in_0_with_0_1, unescapeLeadingUnderscores(name), unescapeLeadingUnderscores(propName), ); return true; } } error(errorLocation, Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_namespace_here, unescapeLeadingUnderscores(name)); return true; } } return false; } function checkAndReportErrorForUsingValueAsType(errorLocation: Node, name: __String, meaning: SymbolFlags): boolean { if (meaning & (SymbolFlags.Type & ~SymbolFlags.Namespace)) { const symbol = resolveSymbol(resolveName(errorLocation, name, ~SymbolFlags.Type & SymbolFlags.Value, /*nameNotFoundMessage*/ undefined, /*isUse*/ false)); if (symbol && !(symbol.flags & SymbolFlags.Namespace)) { error(errorLocation, Diagnostics._0_refers_to_a_value_but_is_being_used_as_a_type_here_Did_you_mean_typeof_0, unescapeLeadingUnderscores(name)); return true; } } return false; } function isPrimitiveTypeName(name: __String) { return name === "any" || name === "string" || name === "number" || name === "boolean" || name === "never" || name === "unknown"; } function checkAndReportErrorForExportingPrimitiveType(errorLocation: Node, name: __String): boolean { if (isPrimitiveTypeName(name) && errorLocation.parent.kind === SyntaxKind.ExportSpecifier) { error(errorLocation, Diagnostics.Cannot_export_0_Only_local_declarations_can_be_exported_from_a_module, name as string); return true; } return false; } function checkAndReportErrorForUsingTypeAsValue(errorLocation: Node, name: __String, meaning: SymbolFlags): boolean { if (meaning & SymbolFlags.Value) { if (isPrimitiveTypeName(name)) { const grandparent = errorLocation.parent.parent; if (grandparent && grandparent.parent && isHeritageClause(grandparent)) { const heritageKind = grandparent.token; const containerKind = grandparent.parent.kind; if (containerKind === SyntaxKind.InterfaceDeclaration && heritageKind === SyntaxKind.ExtendsKeyword) { error(errorLocation, Diagnostics.An_interface_cannot_extend_a_primitive_type_like_0_It_can_only_extend_other_named_object_types, unescapeLeadingUnderscores(name)); } else if (containerKind === SyntaxKind.ClassDeclaration && heritageKind === SyntaxKind.ExtendsKeyword) { error(errorLocation, Diagnostics.A_class_cannot_extend_a_primitive_type_like_0_Classes_can_only_extend_constructable_values, unescapeLeadingUnderscores(name)); } else if (containerKind === SyntaxKind.ClassDeclaration && heritageKind === SyntaxKind.ImplementsKeyword) { error(errorLocation, Diagnostics.A_class_cannot_implement_a_primitive_type_like_0_It_can_only_implement_other_named_object_types, unescapeLeadingUnderscores(name)); } } else { error(errorLocation, Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_value_here, unescapeLeadingUnderscores(name)); } return true; } const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.Type & ~SymbolFlags.Value, /*nameNotFoundMessage*/ undefined, /*isUse*/ false)); const allFlags = symbol && getSymbolFlags(symbol); if (symbol && allFlags !== undefined && !(allFlags & SymbolFlags.Value)) { const rawName = unescapeLeadingUnderscores(name); if (isES2015OrLaterConstructorName(name)) { error(errorLocation, Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_value_here_Do_you_need_to_change_your_target_library_Try_changing_the_lib_compiler_option_to_es2015_or_later, rawName); } else if (maybeMappedType(errorLocation, symbol)) { error(errorLocation, Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_value_here_Did_you_mean_to_use_1_in_0, rawName, rawName === "K" ? "P" : "K"); } else { error(errorLocation, Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_value_here, rawName); } return true; } } return false; } function maybeMappedType(node: Node, symbol: Symbol) { const container = findAncestor(node.parent, n => isComputedPropertyName(n) || isPropertySignature(n) ? false : isTypeLiteralNode(n) || "quit") as TypeLiteralNode | undefined; if (container && container.members.length === 1) { const type = getDeclaredTypeOfSymbol(symbol); return !!(type.flags & TypeFlags.Union) && allTypesAssignableToKind(type, TypeFlags.StringOrNumberLiteral, /*strict*/ true); } return false; } function isES2015OrLaterConstructorName(n: __String) { switch (n) { case "Promise": case "Symbol": case "Map": case "WeakMap": case "Set": case "WeakSet": return true; } return false; } function checkAndReportErrorForUsingNamespaceAsTypeOrValue(errorLocation: Node, name: __String, meaning: SymbolFlags): boolean { if (meaning & (SymbolFlags.Value & ~SymbolFlags.Type)) { const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.NamespaceModule, /*nameNotFoundMessage*/ undefined, /*isUse*/ false)); if (symbol) { error( errorLocation, Diagnostics.Cannot_use_namespace_0_as_a_value, unescapeLeadingUnderscores(name), ); return true; } } else if (meaning & (SymbolFlags.Type & ~SymbolFlags.Value)) { const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.Module, /*nameNotFoundMessage*/ undefined, /*isUse*/ false)); if (symbol) { error(errorLocation, Diagnostics.Cannot_use_namespace_0_as_a_type, unescapeLeadingUnderscores(name)); return true; } } return false; } function checkResolvedBlockScopedVariable(result: Symbol, errorLocation: Node): void { Debug.assert(!!(result.flags & SymbolFlags.BlockScopedVariable || result.flags & SymbolFlags.Class || result.flags & SymbolFlags.Enum)); if (result.flags & (SymbolFlags.Function | SymbolFlags.FunctionScopedVariable | SymbolFlags.Assignment) && result.flags & SymbolFlags.Class) { // constructor functions aren't block scoped return; } // Block-scoped variables cannot be used before their definition const declaration = result.declarations?.find( d => isBlockOrCatchScoped(d) || isClassLike(d) || (d.kind === SyntaxKind.EnumDeclaration), ); if (declaration === undefined) return Debug.fail("checkResolvedBlockScopedVariable could not find block-scoped declaration"); if (!(declaration.flags & NodeFlags.Ambient) && !isBlockScopedNameDeclaredBeforeUse(declaration, errorLocation)) { let diagnosticMessage; const declarationName = declarationNameToString(getNameOfDeclaration(declaration)); if (result.flags & SymbolFlags.BlockScopedVariable) { diagnosticMessage = error(errorLocation, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, declarationName); } else if (result.flags & SymbolFlags.Class) { diagnosticMessage = error(errorLocation, Diagnostics.Class_0_used_before_its_declaration, declarationName); } else if (result.flags & SymbolFlags.RegularEnum) { diagnosticMessage = error(errorLocation, Diagnostics.Enum_0_used_before_its_declaration, declarationName); } else { Debug.assert(!!(result.flags & SymbolFlags.ConstEnum)); if (getIsolatedModules(compilerOptions)) { diagnosticMessage = error(errorLocation, Diagnostics.Enum_0_used_before_its_declaration, declarationName); } } if (diagnosticMessage) { addRelatedInfo(diagnosticMessage, createDiagnosticForNode(declaration, Diagnostics._0_is_declared_here, declarationName)); } } } /* Starting from 'initial' node walk up the parent chain until 'stopAt' node is reached. * If at any point current node is equal to 'parent' node - return true. * If current node is an IIFE, continue walking up. * Return false if 'stopAt' node is reached or isFunctionLike(current) === true. */ function isSameScopeDescendentOf(initial: Node, parent: Node | undefined, stopAt: Node): boolean { return !!parent && !!findAncestor(initial, n => n === parent || (n === stopAt || isFunctionLike(n) && (!getImmediatelyInvokedFunctionExpression(n) || (getFunctionFlags(n) & FunctionFlags.AsyncGenerator)) ? "quit" : false)); } function getAnyImportSyntax(node: Node): AnyImportOrJsDocImport | undefined { switch (node.kind) { case SyntaxKind.ImportEqualsDeclaration: return node as ImportEqualsDeclaration; case SyntaxKind.ImportClause: return (node as ImportClause).parent; case SyntaxKind.NamespaceImport: return (node as NamespaceImport).parent.parent; case SyntaxKind.ImportSpecifier: return (node as ImportSpecifier).parent.parent.parent; default: return undefined; } } function getDeclarationOfAliasSymbol(symbol: Symbol): Declaration | undefined { return symbol.declarations && findLast(symbol.declarations, isAliasSymbolDeclaration); } /** * An alias symbol is created by one of the following declarations: * import = ... * import from ... * import * as from ... * import { x as } from ... * export { x as } from ... * export * as ns from ... * export = * export default * module.exports = * {} * {name: } * const { x } = require ... */ function isAliasSymbolDeclaration(node: Node): boolean { return node.kind === SyntaxKind.ImportEqualsDeclaration || node.kind === SyntaxKind.NamespaceExportDeclaration || node.kind === SyntaxKind.ImportClause && !!(node as ImportClause).name || node.kind === SyntaxKind.NamespaceImport || node.kind === SyntaxKind.NamespaceExport || node.kind === SyntaxKind.ImportSpecifier || node.kind === SyntaxKind.ExportSpecifier || node.kind === SyntaxKind.ExportAssignment && exportAssignmentIsAlias(node as ExportAssignment) || isBinaryExpression(node) && getAssignmentDeclarationKind(node) === AssignmentDeclarationKind.ModuleExports && exportAssignmentIsAlias(node) || isAccessExpression(node) && isBinaryExpression(node.parent) && node.parent.left === node && node.parent.operatorToken.kind === SyntaxKind.EqualsToken && isAliasableOrJsExpression(node.parent.right) || node.kind === SyntaxKind.ShorthandPropertyAssignment || node.kind === SyntaxKind.PropertyAssignment && isAliasableOrJsExpression((node as PropertyAssignment).initializer) || node.kind === SyntaxKind.VariableDeclaration && isVariableDeclarationInitializedToBareOrAccessedRequire(node) || node.kind === SyntaxKind.BindingElement && isVariableDeclarationInitializedToBareOrAccessedRequire(node.parent.parent); } function isAliasableOrJsExpression(e: Expression) { return isAliasableExpression(e) || isFunctionExpression(e) && isJSConstructor(e); } function getTargetOfImportEqualsDeclaration(node: ImportEqualsDeclaration | VariableDeclaration, dontResolveAlias: boolean): Symbol | undefined { const commonJSPropertyAccess = getCommonJSPropertyAccess(node); if (commonJSPropertyAccess) { const name = (getLeftmostAccessExpression(commonJSPropertyAccess.expression) as CallExpression).arguments[0] as StringLiteral; return isIdentifier(commonJSPropertyAccess.name) ? resolveSymbol(getPropertyOfType(resolveExternalModuleTypeByLiteral(name), commonJSPropertyAccess.name.escapedText)) : undefined; } if (isVariableDeclaration(node) || node.moduleReference.kind === SyntaxKind.ExternalModuleReference) { const immediate = resolveExternalModuleName( node, getExternalModuleRequireArgument(node) || getExternalModuleImportEqualsDeclarationExpression(node), ); const resolved = resolveExternalModuleSymbol(immediate); markSymbolOfAliasDeclarationIfTypeOnly(node, immediate, resolved, /*overwriteEmpty*/ false); return resolved; } const resolved = getSymbolOfPartOfRightHandSideOfImportEquals(node.moduleReference, dontResolveAlias); checkAndReportErrorForResolvingImportAliasToTypeOnlySymbol(node, resolved); return resolved; } function checkAndReportErrorForResolvingImportAliasToTypeOnlySymbol(node: ImportEqualsDeclaration, resolved: Symbol | undefined) { if (markSymbolOfAliasDeclarationIfTypeOnly(node, /*immediateTarget*/ undefined, resolved, /*overwriteEmpty*/ false) && !node.isTypeOnly) { const typeOnlyDeclaration = getTypeOnlyAliasDeclaration(getSymbolOfDeclaration(node))!; const isExport = typeOnlyDeclaration.kind === SyntaxKind.ExportSpecifier || typeOnlyDeclaration.kind === SyntaxKind.ExportDeclaration; const message = isExport ? Diagnostics.An_import_alias_cannot_reference_a_declaration_that_was_exported_using_export_type : Diagnostics.An_import_alias_cannot_reference_a_declaration_that_was_imported_using_import_type; const relatedMessage = isExport ? Diagnostics._0_was_exported_here : Diagnostics._0_was_imported_here; // TODO: how to get name for export *? const name = typeOnlyDeclaration.kind === SyntaxKind.ExportDeclaration ? "*" : moduleExportNameTextUnescaped(typeOnlyDeclaration.name); addRelatedInfo(error(node.moduleReference, message), createDiagnosticForNode(typeOnlyDeclaration, relatedMessage, name)); } } function resolveExportByName(moduleSymbol: Symbol, name: __String, sourceNode: TypeOnlyCompatibleAliasDeclaration | undefined, dontResolveAlias: boolean) { const exportValue = moduleSymbol.exports!.get(InternalSymbolName.ExportEquals); const exportSymbol = exportValue ? getPropertyOfType(getTypeOfSymbol(exportValue), name, /*skipObjectFunctionPropertyAugment*/ true) : moduleSymbol.exports!.get(name); const resolved = resolveSymbol(exportSymbol, dontResolveAlias); markSymbolOfAliasDeclarationIfTypeOnly(sourceNode, exportSymbol, resolved, /*overwriteEmpty*/ false); return resolved; } function isSyntacticDefault(node: Node) { return ((isExportAssignment(node) && !node.isExportEquals) || hasSyntacticModifier(node, ModifierFlags.Default) || isExportSpecifier(node) || isNamespaceExport(node)); } function getEmitSyntaxForModuleSpecifierExpression(usage: Expression) { return isStringLiteralLike(usage) ? host.getEmitSyntaxForUsageLocation(getSourceFileOfNode(usage), usage) : undefined; } function isESMFormatImportImportingCommonjsFormatFile(usageMode: ResolutionMode, targetMode: ResolutionMode) { return usageMode === ModuleKind.ESNext && targetMode === ModuleKind.CommonJS; } function isOnlyImportableAsDefault(usage: Expression) { // In Node.js, JSON modules don't get named exports if (ModuleKind.Node16 <= moduleKind && moduleKind <= ModuleKind.NodeNext) { const usageMode = getEmitSyntaxForModuleSpecifierExpression(usage); return usageMode === ModuleKind.ESNext && endsWith((usage as StringLiteralLike).text, Extension.Json); } return false; } function canHaveSyntheticDefault(file: SourceFile | undefined, moduleSymbol: Symbol, dontResolveAlias: boolean, usage: Expression) { const usageMode = file && getEmitSyntaxForModuleSpecifierExpression(usage); if (file && usageMode !== undefined) { const targetMode = host.getImpliedNodeFormatForEmit(file); if (usageMode === ModuleKind.ESNext && targetMode === ModuleKind.CommonJS && ModuleKind.Node16 <= moduleKind && moduleKind <= ModuleKind.NodeNext) { // In Node.js, CommonJS modules always have a synthetic default when imported into ESM return true; } if (usageMode === ModuleKind.ESNext && targetMode === ModuleKind.ESNext) { // No matter what the `module` setting is, if we're confident that both files // are ESM, there cannot be a synthetic default. return false; } } if (!allowSyntheticDefaultImports) { return false; } // Declaration files (and ambient modules) if (!file || file.isDeclarationFile) { // Definitely cannot have a synthetic default if they have a syntactic default member specified const defaultExportSymbol = resolveExportByName(moduleSymbol, InternalSymbolName.Default, /*sourceNode*/ undefined, /*dontResolveAlias*/ true); // Dont resolve alias because we want the immediately exported symbol's declaration if (defaultExportSymbol && some(defaultExportSymbol.declarations, isSyntacticDefault)) { return false; } // It _might_ still be incorrect to assume there is no __esModule marker on the import at runtime, even if there is no `default` member // So we check a bit more, if (resolveExportByName(moduleSymbol, escapeLeadingUnderscores("__esModule"), /*sourceNode*/ undefined, dontResolveAlias)) { // If there is an `__esModule` specified in the declaration (meaning someone explicitly added it or wrote it in their code), // it definitely is a module and does not have a synthetic default return false; } // There are _many_ declaration files not written with esmodules in mind that still get compiled into a format with __esModule set // Meaning there may be no default at runtime - however to be on the permissive side, we allow access to a synthetic default member // as there is no marker to indicate if the accompanying JS has `__esModule` or not, or is even native esm return true; } // TypeScript files never have a synthetic default (as they are always emitted with an __esModule marker) _unless_ they contain an export= statement if (!isSourceFileJS(file)) { return hasExportAssignmentSymbol(moduleSymbol); } // JS files have a synthetic default if they do not contain ES2015+ module syntax (export = is not valid in js) _and_ do not have an __esModule marker return typeof file.externalModuleIndicator !== "object" && !resolveExportByName(moduleSymbol, escapeLeadingUnderscores("__esModule"), /*sourceNode*/ undefined, dontResolveAlias); } function getTargetOfImportClause(node: ImportClause, dontResolveAlias: boolean): Symbol | undefined { const moduleSymbol = resolveExternalModuleName(node, node.parent.moduleSpecifier); if (moduleSymbol) { return getTargetofModuleDefault(moduleSymbol, node, dontResolveAlias); } } function getTargetofModuleDefault(moduleSymbol: Symbol, node: ImportClause | ImportOrExportSpecifier, dontResolveAlias: boolean) { let exportDefaultSymbol: Symbol | undefined; if (isShorthandAmbientModuleSymbol(moduleSymbol)) { exportDefaultSymbol = moduleSymbol; } else { exportDefaultSymbol = resolveExportByName(moduleSymbol, InternalSymbolName.Default, node, dontResolveAlias); } const file = moduleSymbol.declarations?.find(isSourceFile); const specifier = getModuleSpecifierForImportOrExport(node); if (!specifier) { return exportDefaultSymbol; } const hasDefaultOnly = isOnlyImportableAsDefault(specifier); const hasSyntheticDefault = canHaveSyntheticDefault(file, moduleSymbol, dontResolveAlias, specifier); if (!exportDefaultSymbol && !hasSyntheticDefault && !hasDefaultOnly) { if (hasExportAssignmentSymbol(moduleSymbol) && !allowSyntheticDefaultImports) { const compilerOptionName = moduleKind >= ModuleKind.ES2015 ? "allowSyntheticDefaultImports" : "esModuleInterop"; const exportEqualsSymbol = moduleSymbol.exports!.get(InternalSymbolName.ExportEquals); const exportAssignment = exportEqualsSymbol!.valueDeclaration; const err = error(node.name, Diagnostics.Module_0_can_only_be_default_imported_using_the_1_flag, symbolToString(moduleSymbol), compilerOptionName); if (exportAssignment) { addRelatedInfo( err, createDiagnosticForNode( exportAssignment, Diagnostics.This_module_is_declared_with_export_and_can_only_be_used_with_a_default_import_when_using_the_0_flag, compilerOptionName, ), ); } } else if (isImportClause(node)) { reportNonDefaultExport(moduleSymbol, node); } else { errorNoModuleMemberSymbol(moduleSymbol, moduleSymbol, node, isImportOrExportSpecifier(node) && node.propertyName || node.name); } } else if (hasSyntheticDefault || hasDefaultOnly) { // per emit behavior, a synthetic default overrides a "real" .default member if `__esModule` is not present const resolved = resolveExternalModuleSymbol(moduleSymbol, dontResolveAlias) || resolveSymbol(moduleSymbol, dontResolveAlias); markSymbolOfAliasDeclarationIfTypeOnly(node, moduleSymbol, resolved, /*overwriteEmpty*/ false); return resolved; } markSymbolOfAliasDeclarationIfTypeOnly(node, exportDefaultSymbol, /*finalTarget*/ undefined, /*overwriteEmpty*/ false); return exportDefaultSymbol; } function getModuleSpecifierForImportOrExport(node: ImportEqualsDeclaration | ImportClause | NamespaceImport | ImportOrExportSpecifier): Expression | undefined { switch (node.kind) { case SyntaxKind.ImportClause: return node.parent.moduleSpecifier; case SyntaxKind.ImportEqualsDeclaration: return isExternalModuleReference(node.moduleReference) ? node.moduleReference.expression : undefined; case SyntaxKind.NamespaceImport: return node.parent.parent.moduleSpecifier; case SyntaxKind.ImportSpecifier: return node.parent.parent.parent.moduleSpecifier; case SyntaxKind.ExportSpecifier: return node.parent.parent.moduleSpecifier; default: return Debug.assertNever(node); } } function reportNonDefaultExport(moduleSymbol: Symbol, node: ImportClause) { if (moduleSymbol.exports?.has(node.symbol.escapedName)) { error( node.name, Diagnostics.Module_0_has_no_default_export_Did_you_mean_to_use_import_1_from_0_instead, symbolToString(moduleSymbol), symbolToString(node.symbol), ); } else { const diagnostic = error(node.name, Diagnostics.Module_0_has_no_default_export, symbolToString(moduleSymbol)); const exportStar = moduleSymbol.exports?.get(InternalSymbolName.ExportStar); if (exportStar) { const defaultExport = exportStar.declarations?.find(decl => !!( isExportDeclaration(decl) && decl.moduleSpecifier && resolveExternalModuleName(decl, decl.moduleSpecifier)?.exports?.has(InternalSymbolName.Default) ) ); if (defaultExport) { addRelatedInfo(diagnostic, createDiagnosticForNode(defaultExport, Diagnostics.export_Asterisk_does_not_re_export_a_default)); } } } } function getTargetOfNamespaceImport(node: NamespaceImport, dontResolveAlias: boolean): Symbol | undefined { const moduleSpecifier = node.parent.parent.moduleSpecifier; const immediate = resolveExternalModuleName(node, moduleSpecifier); const resolved = resolveESModuleSymbol(immediate, moduleSpecifier, dontResolveAlias, /*suppressInteropError*/ false); markSymbolOfAliasDeclarationIfTypeOnly(node, immediate, resolved, /*overwriteEmpty*/ false); return resolved; } function getTargetOfNamespaceExport(node: NamespaceExport, dontResolveAlias: boolean): Symbol | undefined { const moduleSpecifier = node.parent.moduleSpecifier; const immediate = moduleSpecifier && resolveExternalModuleName(node, moduleSpecifier); const resolved = moduleSpecifier && resolveESModuleSymbol(immediate, moduleSpecifier, dontResolveAlias, /*suppressInteropError*/ false); markSymbolOfAliasDeclarationIfTypeOnly(node, immediate, resolved, /*overwriteEmpty*/ false); return resolved; } // This function creates a synthetic symbol that combines the value side of one symbol with the // type/namespace side of another symbol. Consider this example: // // declare module graphics { // interface Point { // x: number; // y: number; // } // } // declare var graphics: { // Point: new (x: number, y: number) => graphics.Point; // } // declare module "graphics" { // export = graphics; // } // // An 'import { Point } from "graphics"' needs to create a symbol that combines the value side 'Point' // property with the type/namespace side interface 'Point'. function combineValueAndTypeSymbols(valueSymbol: Symbol, typeSymbol: Symbol): Symbol { if (valueSymbol === unknownSymbol && typeSymbol === unknownSymbol) { return unknownSymbol; } if (valueSymbol.flags & (SymbolFlags.Type | SymbolFlags.Namespace)) { return valueSymbol; } const result = createSymbol(valueSymbol.flags | typeSymbol.flags, valueSymbol.escapedName); Debug.assert(valueSymbol.declarations || typeSymbol.declarations); result.declarations = deduplicate(concatenate(valueSymbol.declarations!, typeSymbol.declarations), equateValues); result.parent = valueSymbol.parent || typeSymbol.parent; if (valueSymbol.valueDeclaration) result.valueDeclaration = valueSymbol.valueDeclaration; if (typeSymbol.members) result.members = new Map(typeSymbol.members); if (valueSymbol.exports) result.exports = new Map(valueSymbol.exports); return result; } function getExportOfModule(symbol: Symbol, nameText: __String, specifier: Declaration, dontResolveAlias: boolean): Symbol | undefined { if (symbol.flags & SymbolFlags.Module) { const exportSymbol = getExportsOfSymbol(symbol).get(nameText); const resolved = resolveSymbol(exportSymbol, dontResolveAlias); const exportStarDeclaration = getSymbolLinks(symbol).typeOnlyExportStarMap?.get(nameText); markSymbolOfAliasDeclarationIfTypeOnly(specifier, exportSymbol, resolved, /*overwriteEmpty*/ false, exportStarDeclaration, nameText); return resolved; } } function getPropertyOfVariable(symbol: Symbol, name: __String): Symbol | undefined { if (symbol.flags & SymbolFlags.Variable) { const typeAnnotation = (symbol.valueDeclaration as VariableDeclaration).type; if (typeAnnotation) { return resolveSymbol(getPropertyOfType(getTypeFromTypeNode(typeAnnotation), name)); } } } function getExternalModuleMember(node: ImportDeclaration | ExportDeclaration | VariableDeclaration | JSDocImportTag, specifier: ImportOrExportSpecifier | BindingElement | PropertyAccessExpression, dontResolveAlias = false): Symbol | undefined { const moduleSpecifier = getExternalModuleRequireArgument(node) || (node as ImportDeclaration | ExportDeclaration | JSDocImportTag).moduleSpecifier!; const moduleSymbol = resolveExternalModuleName(node, moduleSpecifier)!; // TODO: GH#18217 const name = !isPropertyAccessExpression(specifier) && specifier.propertyName || specifier.name; if (!isIdentifier(name) && name.kind !== SyntaxKind.StringLiteral) { return undefined; } const nameText = moduleExportNameTextEscaped(name); const suppressInteropError = nameText === InternalSymbolName.Default && allowSyntheticDefaultImports; const targetSymbol = resolveESModuleSymbol(moduleSymbol, moduleSpecifier, /*dontResolveAlias*/ false, suppressInteropError); if (targetSymbol) { // Note: The empty string is a valid module export name: // // import { "" as foo } from "./foo"; // export { foo as "" }; // if (nameText || name.kind === SyntaxKind.StringLiteral) { if (isShorthandAmbientModuleSymbol(moduleSymbol)) { return moduleSymbol; } let symbolFromVariable: Symbol | undefined; // First check if module was specified with "export=". If so, get the member from the resolved type if (moduleSymbol && moduleSymbol.exports && moduleSymbol.exports.get(InternalSymbolName.ExportEquals)) { symbolFromVariable = getPropertyOfType(getTypeOfSymbol(targetSymbol), nameText, /*skipObjectFunctionPropertyAugment*/ true); } else { symbolFromVariable = getPropertyOfVariable(targetSymbol, nameText); } // if symbolFromVariable is export - get its final target symbolFromVariable = resolveSymbol(symbolFromVariable, dontResolveAlias); let symbolFromModule = getExportOfModule(targetSymbol, nameText, specifier, dontResolveAlias); if (symbolFromModule === undefined && nameText === InternalSymbolName.Default) { const file = moduleSymbol.declarations?.find(isSourceFile); if (isOnlyImportableAsDefault(moduleSpecifier) || canHaveSyntheticDefault(file, moduleSymbol, dontResolveAlias, moduleSpecifier)) { symbolFromModule = resolveExternalModuleSymbol(moduleSymbol, dontResolveAlias) || resolveSymbol(moduleSymbol, dontResolveAlias); } } const symbol = symbolFromModule && symbolFromVariable && symbolFromModule !== symbolFromVariable ? combineValueAndTypeSymbols(symbolFromVariable, symbolFromModule) : symbolFromModule || symbolFromVariable; if (!symbol) { errorNoModuleMemberSymbol(moduleSymbol, targetSymbol, node, name); } return symbol; } } } function errorNoModuleMemberSymbol(moduleSymbol: Symbol, targetSymbol: Symbol, node: Node, name: ModuleExportName) { const moduleName = getFullyQualifiedName(moduleSymbol, node); const declarationName = declarationNameToString(name); const suggestion = isIdentifier(name) ? getSuggestedSymbolForNonexistentModule(name, targetSymbol) : undefined; if (suggestion !== undefined) { const suggestionName = symbolToString(suggestion); const diagnostic = error(name, Diagnostics._0_has_no_exported_member_named_1_Did_you_mean_2, moduleName, declarationName, suggestionName); if (suggestion.valueDeclaration) { addRelatedInfo(diagnostic, createDiagnosticForNode(suggestion.valueDeclaration, Diagnostics._0_is_declared_here, suggestionName)); } } else { if (moduleSymbol.exports?.has(InternalSymbolName.Default)) { error( name, Diagnostics.Module_0_has_no_exported_member_1_Did_you_mean_to_use_import_1_from_0_instead, moduleName, declarationName, ); } else { reportNonExportedMember(node, name, declarationName, moduleSymbol, moduleName); } } } function reportNonExportedMember(node: Node, name: ModuleExportName, declarationName: string, moduleSymbol: Symbol, moduleName: string): void { const localSymbol = tryCast(moduleSymbol.valueDeclaration, canHaveLocals)?.locals?.get(moduleExportNameTextEscaped(name)); const exports = moduleSymbol.exports; if (localSymbol) { const exportedEqualsSymbol = exports?.get(InternalSymbolName.ExportEquals); if (exportedEqualsSymbol) { getSymbolIfSameReference(exportedEqualsSymbol, localSymbol) ? reportInvalidImportEqualsExportMember(node, name, declarationName, moduleName) : error(name, Diagnostics.Module_0_has_no_exported_member_1, moduleName, declarationName); } else { const exportedSymbol = exports ? find(symbolsToArray(exports), symbol => !!getSymbolIfSameReference(symbol, localSymbol)) : undefined; const diagnostic = exportedSymbol ? error(name, Diagnostics.Module_0_declares_1_locally_but_it_is_exported_as_2, moduleName, declarationName, symbolToString(exportedSymbol)) : error(name, Diagnostics.Module_0_declares_1_locally_but_it_is_not_exported, moduleName, declarationName); if (localSymbol.declarations) { addRelatedInfo(diagnostic, ...map(localSymbol.declarations, (decl, index) => createDiagnosticForNode(decl, index === 0 ? Diagnostics._0_is_declared_here : Diagnostics.and_here, declarationName))); } } } else { error(name, Diagnostics.Module_0_has_no_exported_member_1, moduleName, declarationName); } } function reportInvalidImportEqualsExportMember(node: Node, name: ModuleExportName, declarationName: string, moduleName: string) { if (moduleKind >= ModuleKind.ES2015) { const message = getESModuleInterop(compilerOptions) ? Diagnostics._0_can_only_be_imported_by_using_a_default_import : Diagnostics._0_can_only_be_imported_by_turning_on_the_esModuleInterop_flag_and_using_a_default_import; error(name, message, declarationName); } else { if (isInJSFile(node)) { const message = getESModuleInterop(compilerOptions) ? Diagnostics._0_can_only_be_imported_by_using_a_require_call_or_by_using_a_default_import : Diagnostics._0_can_only_be_imported_by_using_a_require_call_or_by_turning_on_the_esModuleInterop_flag_and_using_a_default_import; error(name, message, declarationName); } else { const message = getESModuleInterop(compilerOptions) ? Diagnostics._0_can_only_be_imported_by_using_import_1_require_2_or_a_default_import : Diagnostics._0_can_only_be_imported_by_using_import_1_require_2_or_by_turning_on_the_esModuleInterop_flag_and_using_a_default_import; error(name, message, declarationName, declarationName, moduleName); } } } function getTargetOfImportSpecifier(node: ImportSpecifier | BindingElement, dontResolveAlias: boolean): Symbol | undefined { if (isImportSpecifier(node) && moduleExportNameIsDefault(node.propertyName || node.name)) { const specifier = getModuleSpecifierForImportOrExport(node); const moduleSymbol = specifier && resolveExternalModuleName(node, specifier); if (moduleSymbol) { return getTargetofModuleDefault(moduleSymbol, node, dontResolveAlias); } } const root = isBindingElement(node) ? getRootDeclaration(node) as VariableDeclaration : node.parent.parent.parent; const commonJSPropertyAccess = getCommonJSPropertyAccess(root); const resolved = getExternalModuleMember(root, commonJSPropertyAccess || node, dontResolveAlias); const name = node.propertyName || node.name; if (commonJSPropertyAccess && resolved && isIdentifier(name)) { return resolveSymbol(getPropertyOfType(getTypeOfSymbol(resolved), name.escapedText), dontResolveAlias); } markSymbolOfAliasDeclarationIfTypeOnly(node, /*immediateTarget*/ undefined, resolved, /*overwriteEmpty*/ false); return resolved; } function getCommonJSPropertyAccess(node: Node) { if (isVariableDeclaration(node) && node.initializer && isPropertyAccessExpression(node.initializer)) { return node.initializer; } } function getTargetOfNamespaceExportDeclaration(node: NamespaceExportDeclaration, dontResolveAlias: boolean): Symbol | undefined { if (canHaveSymbol(node.parent)) { const resolved = resolveExternalModuleSymbol(node.parent.symbol, dontResolveAlias); markSymbolOfAliasDeclarationIfTypeOnly(node, /*immediateTarget*/ undefined, resolved, /*overwriteEmpty*/ false); return resolved; } } function getTargetOfExportSpecifier(node: ExportSpecifier, meaning: SymbolFlags, dontResolveAlias?: boolean) { const name = node.propertyName || node.name; if (moduleExportNameIsDefault(name)) { const specifier = getModuleSpecifierForImportOrExport(node); const moduleSymbol = specifier && resolveExternalModuleName(node, specifier); if (moduleSymbol) { return getTargetofModuleDefault(moduleSymbol, node, !!dontResolveAlias); } } const resolved = node.parent.parent.moduleSpecifier ? getExternalModuleMember(node.parent.parent, node, dontResolveAlias) : name.kind === SyntaxKind.StringLiteral ? undefined : // Skip for invalid syntax like this: export { "x" } resolveEntityName(name, meaning, /*ignoreErrors*/ false, dontResolveAlias); markSymbolOfAliasDeclarationIfTypeOnly(node, /*immediateTarget*/ undefined, resolved, /*overwriteEmpty*/ false); return resolved; } function getTargetOfExportAssignment(node: ExportAssignment | BinaryExpression, dontResolveAlias: boolean): Symbol | undefined { const expression = isExportAssignment(node) ? node.expression : node.right; const resolved = getTargetOfAliasLikeExpression(expression, dontResolveAlias); markSymbolOfAliasDeclarationIfTypeOnly(node, /*immediateTarget*/ undefined, resolved, /*overwriteEmpty*/ false); return resolved; } function getTargetOfAliasLikeExpression(expression: Expression, dontResolveAlias: boolean) { if (isClassExpression(expression)) { return checkExpressionCached(expression).symbol; } if (!isEntityName(expression) && !isEntityNameExpression(expression)) { return undefined; } const aliasLike = resolveEntityName(expression, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace, /*ignoreErrors*/ true, dontResolveAlias); if (aliasLike) { return aliasLike; } checkExpressionCached(expression); return getNodeLinks(expression).resolvedSymbol; } function getTargetOfAccessExpression(node: AccessExpression, dontRecursivelyResolve: boolean): Symbol | undefined { if (!(isBinaryExpression(node.parent) && node.parent.left === node && node.parent.operatorToken.kind === SyntaxKind.EqualsToken)) { return undefined; } return getTargetOfAliasLikeExpression(node.parent.right, dontRecursivelyResolve); } function getTargetOfAliasDeclaration(node: Declaration, dontRecursivelyResolve = false): Symbol | undefined { switch (node.kind) { case SyntaxKind.ImportEqualsDeclaration: case SyntaxKind.VariableDeclaration: return getTargetOfImportEqualsDeclaration(node as ImportEqualsDeclaration | VariableDeclaration, dontRecursivelyResolve); case SyntaxKind.ImportClause: return getTargetOfImportClause(node as ImportClause, dontRecursivelyResolve); case SyntaxKind.NamespaceImport: return getTargetOfNamespaceImport(node as NamespaceImport, dontRecursivelyResolve); case SyntaxKind.NamespaceExport: return getTargetOfNamespaceExport(node as NamespaceExport, dontRecursivelyResolve); case SyntaxKind.ImportSpecifier: case SyntaxKind.BindingElement: return getTargetOfImportSpecifier(node as ImportSpecifier | BindingElement, dontRecursivelyResolve); case SyntaxKind.ExportSpecifier: return getTargetOfExportSpecifier(node as ExportSpecifier, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace, dontRecursivelyResolve); case SyntaxKind.ExportAssignment: case SyntaxKind.BinaryExpression: return getTargetOfExportAssignment(node as ExportAssignment | BinaryExpression, dontRecursivelyResolve); case SyntaxKind.NamespaceExportDeclaration: return getTargetOfNamespaceExportDeclaration(node as NamespaceExportDeclaration, dontRecursivelyResolve); case SyntaxKind.ShorthandPropertyAssignment: return resolveEntityName((node as ShorthandPropertyAssignment).name, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace, /*ignoreErrors*/ true, dontRecursivelyResolve); case SyntaxKind.PropertyAssignment: return getTargetOfAliasLikeExpression((node as PropertyAssignment).initializer, dontRecursivelyResolve); case SyntaxKind.ElementAccessExpression: case SyntaxKind.PropertyAccessExpression: return getTargetOfAccessExpression(node as AccessExpression, dontRecursivelyResolve); default: return Debug.fail(); } } /** * Indicates that a symbol is an alias that does not merge with a local declaration. * OR Is a JSContainer which may merge an alias with a local declaration */ function isNonLocalAlias(symbol: Symbol | undefined, excludes = SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace): symbol is Symbol { if (!symbol) return false; return (symbol.flags & (SymbolFlags.Alias | excludes)) === SymbolFlags.Alias || !!(symbol.flags & SymbolFlags.Alias && symbol.flags & SymbolFlags.Assignment); } function resolveSymbol(symbol: Symbol, dontResolveAlias?: boolean): Symbol; function resolveSymbol(symbol: Symbol | undefined, dontResolveAlias?: boolean): Symbol | undefined; function resolveSymbol(symbol: Symbol | undefined, dontResolveAlias?: boolean): Symbol | undefined { return !dontResolveAlias && isNonLocalAlias(symbol) ? resolveAlias(symbol) : symbol; } function resolveAlias(symbol: Symbol): Symbol { Debug.assert((symbol.flags & SymbolFlags.Alias) !== 0, "Should only get Alias here."); const links = getSymbolLinks(symbol); if (!links.aliasTarget) { links.aliasTarget = resolvingSymbol; const node = getDeclarationOfAliasSymbol(symbol); if (!node) return Debug.fail(); const target = getTargetOfAliasDeclaration(node); if (links.aliasTarget === resolvingSymbol) { links.aliasTarget = target || unknownSymbol; } else { error(node, Diagnostics.Circular_definition_of_import_alias_0, symbolToString(symbol)); } } else if (links.aliasTarget === resolvingSymbol) { links.aliasTarget = unknownSymbol; } return links.aliasTarget; } function tryResolveAlias(symbol: Symbol): Symbol | undefined { const links = getSymbolLinks(symbol); if (links.aliasTarget !== resolvingSymbol) { return resolveAlias(symbol); } return undefined; } /** * Gets combined flags of a `symbol` and all alias targets it resolves to. `resolveAlias` * is typically recursive over chains of aliases, but stops mid-chain if an alias is merged * with another exported symbol, e.g. * ```ts * // a.ts * export const a = 0; * // b.ts * export { a } from "./a"; * export type a = number; * // c.ts * import { a } from "./b"; * ``` * Calling `resolveAlias` on the `a` in c.ts would stop at the merged symbol exported * from b.ts, even though there is still more alias to resolve. Consequently, if we were * trying to determine if the `a` in c.ts has a value meaning, looking at the flags on * the local symbol and on the symbol returned by `resolveAlias` is not enough. * @returns SymbolFlags.All if `symbol` is an alias that ultimately resolves to `unknown`; * combined flags of all alias targets otherwise. */ function getSymbolFlags(symbol: Symbol, excludeTypeOnlyMeanings?: boolean, excludeLocalMeanings?: boolean): SymbolFlags { const typeOnlyDeclaration = excludeTypeOnlyMeanings && getTypeOnlyAliasDeclaration(symbol); const typeOnlyDeclarationIsExportStar = typeOnlyDeclaration && isExportDeclaration(typeOnlyDeclaration); const typeOnlyResolution = typeOnlyDeclaration && ( typeOnlyDeclarationIsExportStar ? resolveExternalModuleName(typeOnlyDeclaration.moduleSpecifier, typeOnlyDeclaration.moduleSpecifier, /*ignoreErrors*/ true) : resolveAlias(typeOnlyDeclaration.symbol) ); const typeOnlyExportStarTargets = typeOnlyDeclarationIsExportStar && typeOnlyResolution ? getExportsOfModule(typeOnlyResolution) : undefined; let flags = excludeLocalMeanings ? SymbolFlags.None : symbol.flags; let seenSymbols; while (symbol.flags & SymbolFlags.Alias) { const target = getExportSymbolOfValueSymbolIfExported(resolveAlias(symbol)); if ( !typeOnlyDeclarationIsExportStar && target === typeOnlyResolution || typeOnlyExportStarTargets?.get(target.escapedName) === target ) { break; } if (target === unknownSymbol) { return SymbolFlags.All; } // Optimizations - try to avoid creating or adding to // `seenSymbols` if possible if (target === symbol || seenSymbols?.has(target)) { break; } if (target.flags & SymbolFlags.Alias) { if (seenSymbols) { seenSymbols.add(target); } else { seenSymbols = new Set([symbol, target]); } } flags |= target.flags; symbol = target; } return flags; } /** * Marks a symbol as type-only if its declaration is syntactically type-only. * If it is not itself marked type-only, but resolves to a type-only alias * somewhere in its resolution chain, save a reference to the type-only alias declaration * so the alias _not_ marked type-only can be identified as _transitively_ type-only. * * This function is called on each alias declaration that could be type-only or resolve to * another type-only alias during `resolveAlias`, so that later, when an alias is used in a * JS-emitting expression, we can quickly determine if that symbol is effectively type-only * and issue an error if so. * * @param aliasDeclaration The alias declaration not marked as type-only * @param immediateTarget The symbol to which the alias declaration immediately resolves * @param finalTarget The symbol to which the alias declaration ultimately resolves * @param overwriteEmpty Checks `resolvesToSymbol` for type-only declarations even if `aliasDeclaration` * has already been marked as not resolving to a type-only alias. Used when recursively resolving qualified * names of import aliases, e.g. `import C = a.b.C`. If namespace `a` is not found to be type-only, the * import declaration will initially be marked as not resolving to a type-only symbol. But, namespace `b` * must still be checked for a type-only marker, overwriting the previous negative result if found. */ function markSymbolOfAliasDeclarationIfTypeOnly( aliasDeclaration: Declaration | undefined, immediateTarget: Symbol | undefined, finalTarget: Symbol | undefined, overwriteEmpty: boolean, exportStarDeclaration?: ExportDeclaration & { readonly isTypeOnly: true; readonly moduleSpecifier: Expression; }, exportStarName?: __String, ): boolean { if (!aliasDeclaration || isPropertyAccessExpression(aliasDeclaration)) return false; // If the declaration itself is type-only, mark it and return. // No need to check what it resolves to. const sourceSymbol = getSymbolOfDeclaration(aliasDeclaration); if (isTypeOnlyImportOrExportDeclaration(aliasDeclaration)) { const links = getSymbolLinks(sourceSymbol); links.typeOnlyDeclaration = aliasDeclaration; return true; } if (exportStarDeclaration) { const links = getSymbolLinks(sourceSymbol); links.typeOnlyDeclaration = exportStarDeclaration; if (sourceSymbol.escapedName !== exportStarName) { links.typeOnlyExportStarName = exportStarName; } return true; } const links = getSymbolLinks(sourceSymbol); return markSymbolOfAliasDeclarationIfTypeOnlyWorker(links, immediateTarget, overwriteEmpty) || markSymbolOfAliasDeclarationIfTypeOnlyWorker(links, finalTarget, overwriteEmpty); } function markSymbolOfAliasDeclarationIfTypeOnlyWorker(aliasDeclarationLinks: SymbolLinks, target: Symbol | undefined, overwriteEmpty: boolean): boolean { if (target && (aliasDeclarationLinks.typeOnlyDeclaration === undefined || overwriteEmpty && aliasDeclarationLinks.typeOnlyDeclaration === false)) { const exportSymbol = target.exports?.get(InternalSymbolName.ExportEquals) ?? target; const typeOnly = exportSymbol.declarations && find(exportSymbol.declarations, isTypeOnlyImportOrExportDeclaration); aliasDeclarationLinks.typeOnlyDeclaration = typeOnly ?? getSymbolLinks(exportSymbol).typeOnlyDeclaration ?? false; } return !!aliasDeclarationLinks.typeOnlyDeclaration; } /** Indicates that a symbol directly or indirectly resolves to a type-only import or export. */ function getTypeOnlyAliasDeclaration(symbol: Symbol, include?: SymbolFlags): TypeOnlyAliasDeclaration | undefined { if (!(symbol.flags & SymbolFlags.Alias)) { return undefined; } const links = getSymbolLinks(symbol); if (links.typeOnlyDeclaration === undefined) { // We need to set a WIP value here to prevent reentrancy during `getImmediateAliasedSymbol` which, paradoxically, can depend on this links.typeOnlyDeclaration = false; const resolved = resolveSymbol(symbol); // do this before the `resolveImmediate` below, as it uses a different circularity cache and we might hide a circularity error if we blindly get the immediate alias first // While usually the alias will have been marked during the pass by the full typecheck, we may still need to calculate the alias declaration now markSymbolOfAliasDeclarationIfTypeOnly(symbol.declarations?.[0], getDeclarationOfAliasSymbol(symbol) && getImmediateAliasedSymbol(symbol), resolved, /*overwriteEmpty*/ true); } if (include === undefined) { return links.typeOnlyDeclaration || undefined; } if (links.typeOnlyDeclaration) { const resolved = links.typeOnlyDeclaration.kind === SyntaxKind.ExportDeclaration ? resolveSymbol(getExportsOfModule(links.typeOnlyDeclaration.symbol.parent!).get(links.typeOnlyExportStarName || symbol.escapedName))! : resolveAlias(links.typeOnlyDeclaration.symbol); return getSymbolFlags(resolved) & include ? links.typeOnlyDeclaration : undefined; } return undefined; } // This function is only for imports with entity names function getSymbolOfPartOfRightHandSideOfImportEquals(entityName: EntityName, dontResolveAlias?: boolean): Symbol | undefined { // There are three things we might try to look for. In the following examples, // the search term is enclosed in |...|: // // import a = |b|; // Namespace // import a = |b.c|; // Value, type, namespace // import a = |b.c|.d; // Namespace if (entityName.kind === SyntaxKind.Identifier && isRightSideOfQualifiedNameOrPropertyAccess(entityName)) { entityName = entityName.parent as QualifiedName; } // Check for case 1 and 3 in the above example if (entityName.kind === SyntaxKind.Identifier || entityName.parent.kind === SyntaxKind.QualifiedName) { return resolveEntityName(entityName, SymbolFlags.Namespace, /*ignoreErrors*/ false, dontResolveAlias); } else { // Case 2 in above example // entityName.kind could be a QualifiedName or a Missing identifier Debug.assert(entityName.parent.kind === SyntaxKind.ImportEqualsDeclaration); return resolveEntityName(entityName, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace, /*ignoreErrors*/ false, dontResolveAlias); } } function getFullyQualifiedName(symbol: Symbol, containingLocation?: Node): string { return symbol.parent ? getFullyQualifiedName(symbol.parent, containingLocation) + "." + symbolToString(symbol) : symbolToString(symbol, containingLocation, /*meaning*/ undefined, SymbolFormatFlags.DoNotIncludeSymbolChain | SymbolFormatFlags.AllowAnyNodeKind); } function getContainingQualifiedNameNode(node: QualifiedName) { while (isQualifiedName(node.parent)) { node = node.parent; } return node; } function tryGetQualifiedNameAsValue(node: QualifiedName) { let left: Identifier | QualifiedName = getFirstIdentifier(node); let symbol = resolveName(left, left, SymbolFlags.Value, /*nameNotFoundMessage*/ undefined, /*isUse*/ true); if (!symbol) { return undefined; } while (isQualifiedName(left.parent)) { const type = getTypeOfSymbol(symbol); symbol = getPropertyOfType(type, left.parent.right.escapedText); if (!symbol) { return undefined; } left = left.parent; } return symbol; } /** * Resolves a qualified name and any involved aliases. */ function resolveEntityName(name: EntityNameOrEntityNameExpression, meaning: SymbolFlags, ignoreErrors?: boolean, dontResolveAlias?: boolean, location?: Node): Symbol | undefined { if (nodeIsMissing(name)) { return undefined; } const namespaceMeaning = SymbolFlags.Namespace | (isInJSFile(name) ? meaning & SymbolFlags.Value : 0); let symbol: Symbol | undefined; if (name.kind === SyntaxKind.Identifier) { const message = meaning === namespaceMeaning || nodeIsSynthesized(name) ? Diagnostics.Cannot_find_namespace_0 : getCannotFindNameDiagnosticForName(getFirstIdentifier(name)); const symbolFromJSPrototype = isInJSFile(name) && !nodeIsSynthesized(name) ? resolveEntityNameFromAssignmentDeclaration(name, meaning) : undefined; symbol = getMergedSymbol(resolveName(location || name, name, meaning, ignoreErrors || symbolFromJSPrototype ? undefined : message, /*isUse*/ true, /*excludeGlobals*/ false)); if (!symbol) { return getMergedSymbol(symbolFromJSPrototype); } } else if (name.kind === SyntaxKind.QualifiedName || name.kind === SyntaxKind.PropertyAccessExpression) { const left = name.kind === SyntaxKind.QualifiedName ? name.left : name.expression; const right = name.kind === SyntaxKind.QualifiedName ? name.right : name.name; let namespace = resolveEntityName(left, namespaceMeaning, ignoreErrors, /*dontResolveAlias*/ false, location); if (!namespace || nodeIsMissing(right)) { return undefined; } else if (namespace === unknownSymbol) { return namespace; } if ( namespace.valueDeclaration && isInJSFile(namespace.valueDeclaration) && getEmitModuleResolutionKind(compilerOptions) !== ModuleResolutionKind.Bundler && isVariableDeclaration(namespace.valueDeclaration) && namespace.valueDeclaration.initializer && isCommonJsRequire(namespace.valueDeclaration.initializer) ) { const moduleName = (namespace.valueDeclaration.initializer as CallExpression).arguments[0] as StringLiteral; const moduleSym = resolveExternalModuleName(moduleName, moduleName); if (moduleSym) { const resolvedModuleSymbol = resolveExternalModuleSymbol(moduleSym); if (resolvedModuleSymbol) { namespace = resolvedModuleSymbol; } } } symbol = getMergedSymbol(getSymbol(getExportsOfSymbol(namespace), right.escapedText, meaning)); if (!symbol && (namespace.flags & SymbolFlags.Alias)) { // `namespace` can be resolved further if there was a symbol merge with a re-export symbol = getMergedSymbol(getSymbol(getExportsOfSymbol(resolveAlias(namespace)), right.escapedText, meaning)); } if (!symbol) { if (!ignoreErrors) { const namespaceName = getFullyQualifiedName(namespace); const declarationName = declarationNameToString(right); const suggestionForNonexistentModule = getSuggestedSymbolForNonexistentModule(right, namespace); if (suggestionForNonexistentModule) { error(right, Diagnostics._0_has_no_exported_member_named_1_Did_you_mean_2, namespaceName, declarationName, symbolToString(suggestionForNonexistentModule)); return undefined; } const containingQualifiedName = isQualifiedName(name) && getContainingQualifiedNameNode(name); const canSuggestTypeof = globalObjectType // <-- can't pull on types if global types aren't initialized yet && (meaning & SymbolFlags.Type) && containingQualifiedName && !isTypeOfExpression(containingQualifiedName.parent) && tryGetQualifiedNameAsValue(containingQualifiedName); if (canSuggestTypeof) { error( containingQualifiedName, Diagnostics._0_refers_to_a_value_but_is_being_used_as_a_type_here_Did_you_mean_typeof_0, entityNameToString(containingQualifiedName), ); return undefined; } if (meaning & SymbolFlags.Namespace && isQualifiedName(name.parent)) { const exportedTypeSymbol = getMergedSymbol(getSymbol(getExportsOfSymbol(namespace), right.escapedText, SymbolFlags.Type)); if (exportedTypeSymbol) { error( name.parent.right, Diagnostics.Cannot_access_0_1_because_0_is_a_type_but_not_a_namespace_Did_you_mean_to_retrieve_the_type_of_the_property_1_in_0_with_0_1, symbolToString(exportedTypeSymbol), unescapeLeadingUnderscores(name.parent.right.escapedText), ); return undefined; } } error(right, Diagnostics.Namespace_0_has_no_exported_member_1, namespaceName, declarationName); } return undefined; } } else { Debug.assertNever(name, "Unknown entity name kind."); } if (!nodeIsSynthesized(name) && isEntityName(name) && (symbol.flags & SymbolFlags.Alias || name.parent.kind === SyntaxKind.ExportAssignment)) { markSymbolOfAliasDeclarationIfTypeOnly(getAliasDeclarationFromName(name), symbol, /*finalTarget*/ undefined, /*overwriteEmpty*/ true); } return (symbol.flags & meaning) || dontResolveAlias ? symbol : resolveAlias(symbol); } /** * 1. For prototype-property methods like `A.prototype.m = function () ...`, try to resolve names in the scope of `A` too. * Note that prototype-property assignment to locations outside the current file (eg globals) doesn't work, so * name resolution won't work either. * 2. For property assignments like `{ x: function f () { } }`, try to resolve names in the scope of `f` too. */ function resolveEntityNameFromAssignmentDeclaration(name: Identifier, meaning: SymbolFlags) { if (isJSDocTypeReference(name.parent)) { const secondaryLocation = getAssignmentDeclarationLocation(name.parent); if (secondaryLocation) { return resolveName(secondaryLocation, name, meaning, /*nameNotFoundMessage*/ undefined, /*isUse*/ true); } } } function getAssignmentDeclarationLocation(node: TypeReferenceNode): Node | undefined { const typeAlias = findAncestor(node, node => !(isJSDocNode(node) || node.flags & NodeFlags.JSDoc) ? "quit" : isJSDocTypeAlias(node)); if (typeAlias) { return; } const host = getJSDocHost(node); if (host && isExpressionStatement(host) && isPrototypePropertyAssignment(host.expression)) { // /** @param {K} p */ X.prototype.m = function () { } <-- look for K on X's declaration const symbol = getSymbolOfDeclaration(host.expression.left); if (symbol) { return getDeclarationOfJSPrototypeContainer(symbol); } } if (host && isFunctionExpression(host) && isPrototypePropertyAssignment(host.parent) && isExpressionStatement(host.parent.parent)) { // X.prototype.m = /** @param {K} p */ function () { } <-- look for K on X's declaration const symbol = getSymbolOfDeclaration(host.parent.left); if (symbol) { return getDeclarationOfJSPrototypeContainer(symbol); } } if ( host && (isObjectLiteralMethod(host) || isPropertyAssignment(host)) && isBinaryExpression(host.parent.parent) && getAssignmentDeclarationKind(host.parent.parent) === AssignmentDeclarationKind.Prototype ) { // X.prototype = { /** @param {K} p */m() { } } <-- look for K on X's declaration const symbol = getSymbolOfDeclaration(host.parent.parent.left as BindableStaticNameExpression); if (symbol) { return getDeclarationOfJSPrototypeContainer(symbol); } } const sig = getEffectiveJSDocHost(node); if (sig && isFunctionLike(sig)) { const symbol = getSymbolOfDeclaration(sig); return symbol && symbol.valueDeclaration; } } function getDeclarationOfJSPrototypeContainer(symbol: Symbol) { const decl = symbol.parent!.valueDeclaration; if (!decl) { return undefined; } const initializer = isAssignmentDeclaration(decl) ? getAssignedExpandoInitializer(decl) : hasOnlyExpressionInitializer(decl) ? getDeclaredExpandoInitializer(decl) : undefined; return initializer || decl; } /** * Get the real symbol of a declaration with an expando initializer. * * Normally, declarations have an associated symbol, but when a declaration has an expando * initializer, the expando's symbol is the one that has all the members merged into it. */ function getExpandoSymbol(symbol: Symbol): Symbol | undefined { const decl = symbol.valueDeclaration; if (!decl || !isInJSFile(decl) || symbol.flags & SymbolFlags.TypeAlias || getExpandoInitializer(decl, /*isPrototypeAssignment*/ false)) { return undefined; } const init = isVariableDeclaration(decl) ? getDeclaredExpandoInitializer(decl) : getAssignedExpandoInitializer(decl); if (init) { const initSymbol = getSymbolOfNode(init); if (initSymbol) { return mergeJSSymbols(initSymbol, symbol); } } } function resolveExternalModuleName(location: Node, moduleReferenceExpression: Expression, ignoreErrors?: boolean): Symbol | undefined { const isClassic = getEmitModuleResolutionKind(compilerOptions) === ModuleResolutionKind.Classic; const errorMessage = isClassic ? Diagnostics.Cannot_find_module_0_Did_you_mean_to_set_the_moduleResolution_option_to_nodenext_or_to_add_aliases_to_the_paths_option : Diagnostics.Cannot_find_module_0_or_its_corresponding_type_declarations; return resolveExternalModuleNameWorker(location, moduleReferenceExpression, ignoreErrors ? undefined : errorMessage, ignoreErrors); } function resolveExternalModuleNameWorker(location: Node, moduleReferenceExpression: Expression, moduleNotFoundError: DiagnosticMessage | undefined, ignoreErrors = false, isForAugmentation = false): Symbol | undefined { return isStringLiteralLike(moduleReferenceExpression) ? resolveExternalModule(location, moduleReferenceExpression.text, moduleNotFoundError, !ignoreErrors ? moduleReferenceExpression : undefined, isForAugmentation) : undefined; } function resolveExternalModule(location: Node, moduleReference: string, moduleNotFoundError: DiagnosticMessage | undefined, errorNode: Node | undefined, isForAugmentation = false): Symbol | undefined { if (errorNode && startsWith(moduleReference, "@types/")) { const diag = Diagnostics.Cannot_import_type_declaration_files_Consider_importing_0_instead_of_1; const withoutAtTypePrefix = removePrefix(moduleReference, "@types/"); error(errorNode, diag, withoutAtTypePrefix, moduleReference); } const ambientModule = tryFindAmbientModule(moduleReference, /*withAugmentations*/ true); if (ambientModule) { return ambientModule; } const currentSourceFile = getSourceFileOfNode(location); const contextSpecifier = isStringLiteralLike(location) ? location : (isModuleDeclaration(location) ? location : location.parent && isModuleDeclaration(location.parent) && location.parent.name === location ? location.parent : undefined)?.name || (isLiteralImportTypeNode(location) ? location : undefined)?.argument.literal || (isInJSFile(location) && isJSDocImportTag(location) ? location.moduleSpecifier : undefined) || (isVariableDeclaration(location) && location.initializer && isRequireCall(location.initializer, /*requireStringLiteralLikeArgument*/ true) ? location.initializer.arguments[0] : undefined) || findAncestor(location, isImportCall)?.arguments[0] || findAncestor(location, isImportDeclaration)?.moduleSpecifier || findAncestor(location, isExternalModuleImportEqualsDeclaration)?.moduleReference.expression || findAncestor(location, isExportDeclaration)?.moduleSpecifier; const mode = contextSpecifier && isStringLiteralLike(contextSpecifier) ? host.getModeForUsageLocation(currentSourceFile, contextSpecifier) : host.getDefaultResolutionModeForFile(currentSourceFile); const moduleResolutionKind = getEmitModuleResolutionKind(compilerOptions); const resolvedModule = host.getResolvedModule(currentSourceFile, moduleReference, mode)?.resolvedModule; const resolutionDiagnostic = errorNode && resolvedModule && getResolutionDiagnostic(compilerOptions, resolvedModule, currentSourceFile); const sourceFile = resolvedModule && (!resolutionDiagnostic || resolutionDiagnostic === Diagnostics.Module_0_was_resolved_to_1_but_jsx_is_not_set) && host.getSourceFile(resolvedModule.resolvedFileName); if (sourceFile) { // If there's a resolutionDiagnostic we need to report it even if a sourceFile is found. if (resolutionDiagnostic) { error(errorNode, resolutionDiagnostic, moduleReference, resolvedModule.resolvedFileName); } if (resolvedModule.resolvedUsingTsExtension && isDeclarationFileName(moduleReference)) { const importOrExport = findAncestor(location, isImportDeclaration)?.importClause || findAncestor(location, or(isImportEqualsDeclaration, isExportDeclaration)); if (errorNode && importOrExport && !importOrExport.isTypeOnly || findAncestor(location, isImportCall)) { error( errorNode, Diagnostics.A_declaration_file_cannot_be_imported_without_import_type_Did_you_mean_to_import_an_implementation_file_0_instead, getSuggestedImportSource(Debug.checkDefined(tryExtractTSExtension(moduleReference))), ); } } else if (resolvedModule.resolvedUsingTsExtension && !shouldAllowImportingTsExtension(compilerOptions, currentSourceFile.fileName)) { const importOrExport = findAncestor(location, isImportDeclaration)?.importClause || findAncestor(location, or(isImportEqualsDeclaration, isExportDeclaration)); if (errorNode && !(importOrExport?.isTypeOnly || findAncestor(location, isImportTypeNode))) { const tsExtension = Debug.checkDefined(tryExtractTSExtension(moduleReference)); error(errorNode, Diagnostics.An_import_path_can_only_end_with_a_0_extension_when_allowImportingTsExtensions_is_enabled, tsExtension); } } if (sourceFile.symbol) { if (errorNode && resolvedModule.isExternalLibraryImport && !resolutionExtensionIsTSOrJson(resolvedModule.extension)) { errorOnImplicitAnyModule(/*isError*/ false, errorNode, currentSourceFile, mode, resolvedModule, moduleReference); } if (errorNode && (moduleResolutionKind === ModuleResolutionKind.Node16 || moduleResolutionKind === ModuleResolutionKind.NodeNext)) { const isSyncImport = (currentSourceFile.impliedNodeFormat === ModuleKind.CommonJS && !findAncestor(location, isImportCall)) || !!findAncestor(location, isImportEqualsDeclaration); const overrideHost = findAncestor(location, l => isImportTypeNode(l) || isExportDeclaration(l) || isImportDeclaration(l) || isJSDocImportTag(l)); // An override clause will take effect for type-only imports and import types, and allows importing the types across formats, regardless of // normal mode restrictions if (isSyncImport && sourceFile.impliedNodeFormat === ModuleKind.ESNext && !hasResolutionModeOverride(overrideHost)) { if (findAncestor(location, isImportEqualsDeclaration)) { // ImportEquals in a ESM file resolving to another ESM file error(errorNode, Diagnostics.Module_0_cannot_be_imported_using_this_construct_The_specifier_only_resolves_to_an_ES_module_which_cannot_be_imported_with_require_Use_an_ECMAScript_import_instead, moduleReference); } else { // CJS file resolving to an ESM file let diagnosticDetails; const ext = tryGetExtensionFromPath(currentSourceFile.fileName); if (ext === Extension.Ts || ext === Extension.Js || ext === Extension.Tsx || ext === Extension.Jsx) { diagnosticDetails = createModeMismatchDetails(currentSourceFile); } const message = overrideHost?.kind === SyntaxKind.ImportDeclaration && overrideHost.importClause?.isTypeOnly ? Diagnostics.Type_only_import_of_an_ECMAScript_module_from_a_CommonJS_module_must_have_a_resolution_mode_attribute : overrideHost?.kind === SyntaxKind.ImportType ? Diagnostics.Type_import_of_an_ECMAScript_module_from_a_CommonJS_module_must_have_a_resolution_mode_attribute : Diagnostics.The_current_file_is_a_CommonJS_module_whose_imports_will_produce_require_calls_however_the_referenced_file_is_an_ECMAScript_module_and_cannot_be_imported_with_require_Consider_writing_a_dynamic_import_0_call_instead; diagnostics.add(createDiagnosticForNodeFromMessageChain( getSourceFileOfNode(errorNode), errorNode, chainDiagnosticMessages(diagnosticDetails, message, moduleReference), )); } } } // merged symbol is module declaration symbol combined with all augmentations return getMergedSymbol(sourceFile.symbol); } if (errorNode && moduleNotFoundError && !isSideEffectImport(errorNode)) { // report errors only if it was requested error(errorNode, Diagnostics.File_0_is_not_a_module, sourceFile.fileName); } return undefined; } if (patternAmbientModules) { const pattern = findBestPatternMatch(patternAmbientModules, _ => _.pattern, moduleReference); if (pattern) { // If the module reference matched a pattern ambient module ('*.foo') but there's also a // module augmentation by the specific name requested ('a.foo'), we store the merged symbol // by the augmentation name ('a.foo'), because asking for *.foo should not give you exports // from a.foo. const augmentation = patternAmbientModuleAugmentations && patternAmbientModuleAugmentations.get(moduleReference); if (augmentation) { return getMergedSymbol(augmentation); } return getMergedSymbol(pattern.symbol); } } if (!errorNode) { return undefined; } // May be an untyped module. If so, ignore resolutionDiagnostic. if (resolvedModule && !resolutionExtensionIsTSOrJson(resolvedModule.extension) && resolutionDiagnostic === undefined || resolutionDiagnostic === Diagnostics.Could_not_find_a_declaration_file_for_module_0_1_implicitly_has_an_any_type) { if (isForAugmentation) { const diag = Diagnostics.Invalid_module_name_in_augmentation_Module_0_resolves_to_an_untyped_module_at_1_which_cannot_be_augmented; error(errorNode, diag, moduleReference, resolvedModule!.resolvedFileName); } else { errorOnImplicitAnyModule(/*isError*/ noImplicitAny && !!moduleNotFoundError, errorNode, currentSourceFile, mode, resolvedModule!, moduleReference); } // Failed imports and untyped modules are both treated in an untyped manner; only difference is whether we give a diagnostic first. return undefined; } if (moduleNotFoundError) { // See if this was possibly a projectReference redirect if (resolvedModule) { const redirect = host.getProjectReferenceRedirect(resolvedModule.resolvedFileName); if (redirect) { error(errorNode, Diagnostics.Output_file_0_has_not_been_built_from_source_file_1, redirect, resolvedModule.resolvedFileName); return undefined; } } if (resolutionDiagnostic) { error(errorNode, resolutionDiagnostic, moduleReference, resolvedModule.resolvedFileName); } else { const isExtensionlessRelativePathImport = pathIsRelative(moduleReference) && !hasExtension(moduleReference); const resolutionIsNode16OrNext = moduleResolutionKind === ModuleResolutionKind.Node16 || moduleResolutionKind === ModuleResolutionKind.NodeNext; if ( !getResolveJsonModule(compilerOptions) && fileExtensionIs(moduleReference, Extension.Json) && moduleResolutionKind !== ModuleResolutionKind.Classic && hasJsonModuleEmitEnabled(compilerOptions) ) { error(errorNode, Diagnostics.Cannot_find_module_0_Consider_using_resolveJsonModule_to_import_module_with_json_extension, moduleReference); } else if (mode === ModuleKind.ESNext && resolutionIsNode16OrNext && isExtensionlessRelativePathImport) { const absoluteRef = getNormalizedAbsolutePath(moduleReference, getDirectoryPath(currentSourceFile.path)); const suggestedExt = suggestedExtensions.find(([actualExt, _importExt]) => host.fileExists(absoluteRef + actualExt))?.[1]; if (suggestedExt) { error(errorNode, Diagnostics.Relative_import_paths_need_explicit_file_extensions_in_ECMAScript_imports_when_moduleResolution_is_node16_or_nodenext_Did_you_mean_0, moduleReference + suggestedExt); } else { error(errorNode, Diagnostics.Relative_import_paths_need_explicit_file_extensions_in_ECMAScript_imports_when_moduleResolution_is_node16_or_nodenext_Consider_adding_an_extension_to_the_import_path); } } else { if (host.getResolvedModule(currentSourceFile, moduleReference, mode)?.alternateResult) { const errorInfo = createModuleNotFoundChain(currentSourceFile, host, moduleReference, mode, moduleReference); errorOrSuggestion(/*isError*/ true, errorNode, chainDiagnosticMessages(errorInfo, moduleNotFoundError, moduleReference)); } else { error(errorNode, moduleNotFoundError, moduleReference); } } } } return undefined; function getSuggestedImportSource(tsExtension: string) { const importSourceWithoutExtension = removeExtension(moduleReference, tsExtension); /** * Direct users to import source with .js extension if outputting an ES module. * @see https://github.com/microsoft/TypeScript/issues/42151 */ if (emitModuleKindIsNonNodeESM(moduleKind) || mode === ModuleKind.ESNext) { const preferTs = isDeclarationFileName(moduleReference) && shouldAllowImportingTsExtension(compilerOptions); const ext = tsExtension === Extension.Mts || tsExtension === Extension.Dmts ? preferTs ? ".mts" : ".mjs" : tsExtension === Extension.Cts || tsExtension === Extension.Dmts ? preferTs ? ".cts" : ".cjs" : preferTs ? ".ts" : ".js"; return importSourceWithoutExtension + ext; } return importSourceWithoutExtension; } } function errorOnImplicitAnyModule(isError: boolean, errorNode: Node, sourceFile: SourceFile, mode: ResolutionMode, { packageId, resolvedFileName }: ResolvedModuleFull, moduleReference: string): void { if (isSideEffectImport(errorNode)) { return; } let errorInfo: DiagnosticMessageChain | undefined; if (!isExternalModuleNameRelative(moduleReference) && packageId) { errorInfo = createModuleNotFoundChain(sourceFile, host, moduleReference, mode, packageId.name); } errorOrSuggestion( isError, errorNode, chainDiagnosticMessages( errorInfo, Diagnostics.Could_not_find_a_declaration_file_for_module_0_1_implicitly_has_an_any_type, moduleReference, resolvedFileName, ), ); } function resolveExternalModuleSymbol(moduleSymbol: Symbol, dontResolveAlias?: boolean): Symbol; function resolveExternalModuleSymbol(moduleSymbol: Symbol | undefined, dontResolveAlias?: boolean): Symbol | undefined; function resolveExternalModuleSymbol(moduleSymbol: Symbol | undefined, dontResolveAlias?: boolean): Symbol | undefined { if (moduleSymbol?.exports) { const exportEquals = resolveSymbol(moduleSymbol.exports.get(InternalSymbolName.ExportEquals), dontResolveAlias); const exported = getCommonJsExportEquals(getMergedSymbol(exportEquals), getMergedSymbol(moduleSymbol)); return getMergedSymbol(exported) || moduleSymbol; } return undefined; } function getCommonJsExportEquals(exported: Symbol | undefined, moduleSymbol: Symbol): Symbol | undefined { if (!exported || exported === unknownSymbol || exported === moduleSymbol || moduleSymbol.exports!.size === 1 || exported.flags & SymbolFlags.Alias) { return exported; } const links = getSymbolLinks(exported); if (links.cjsExportMerged) { return links.cjsExportMerged; } const merged = exported.flags & SymbolFlags.Transient ? exported : cloneSymbol(exported); merged.flags = merged.flags | SymbolFlags.ValueModule; if (merged.exports === undefined) { merged.exports = createSymbolTable(); } moduleSymbol.exports!.forEach((s, name) => { if (name === InternalSymbolName.ExportEquals) return; merged.exports!.set(name, merged.exports!.has(name) ? mergeSymbol(merged.exports!.get(name)!, s) : s); }); if (merged === exported) { // We just mutated a symbol, reset any cached links we may have already set // (Notably required to make late bound members appear) getSymbolLinks(merged).resolvedExports = undefined; getSymbolLinks(merged).resolvedMembers = undefined; } getSymbolLinks(merged).cjsExportMerged = merged; return links.cjsExportMerged = merged; } // An external module with an 'export =' declaration may be referenced as an ES6 module provided the 'export =' // references a symbol that is at least declared as a module or a variable. The target of the 'export =' may // combine other declarations with the module or variable (e.g. a class/module, function/module, interface/variable). function resolveESModuleSymbol(moduleSymbol: Symbol | undefined, referencingLocation: Node, dontResolveAlias: boolean, suppressInteropError: boolean): Symbol | undefined { const symbol = resolveExternalModuleSymbol(moduleSymbol, dontResolveAlias); if (!dontResolveAlias && symbol) { if (!suppressInteropError && !(symbol.flags & (SymbolFlags.Module | SymbolFlags.Variable)) && !getDeclarationOfKind(symbol, SyntaxKind.SourceFile)) { const compilerOptionName = moduleKind >= ModuleKind.ES2015 ? "allowSyntheticDefaultImports" : "esModuleInterop"; error(referencingLocation, Diagnostics.This_module_can_only_be_referenced_with_ECMAScript_imports_Slashexports_by_turning_on_the_0_flag_and_referencing_its_default_export, compilerOptionName); return symbol; } const referenceParent = referencingLocation.parent; if ( (isImportDeclaration(referenceParent) && getNamespaceDeclarationNode(referenceParent)) || isImportCall(referenceParent) ) { const reference = isImportCall(referenceParent) ? referenceParent.arguments[0] : referenceParent.moduleSpecifier; const type = getTypeOfSymbol(symbol); const defaultOnlyType = getTypeWithSyntheticDefaultOnly(type, symbol, moduleSymbol!, reference); if (defaultOnlyType) { return cloneTypeAsModuleType(symbol, defaultOnlyType, referenceParent); } const targetFile = moduleSymbol?.declarations?.find(isSourceFile); const isEsmCjsRef = targetFile && isESMFormatImportImportingCommonjsFormatFile(getEmitSyntaxForModuleSpecifierExpression(reference), host.getImpliedNodeFormatForEmit(targetFile)); if (getESModuleInterop(compilerOptions) || isEsmCjsRef) { let sigs = getSignaturesOfStructuredType(type, SignatureKind.Call); if (!sigs || !sigs.length) { sigs = getSignaturesOfStructuredType(type, SignatureKind.Construct); } if ( (sigs && sigs.length) || getPropertyOfType(type, InternalSymbolName.Default, /*skipObjectFunctionPropertyAugment*/ true) || isEsmCjsRef ) { const moduleType = type.flags & TypeFlags.StructuredType ? getTypeWithSyntheticDefaultImportType(type, symbol, moduleSymbol!, reference) : createDefaultPropertyWrapperForModule(symbol, symbol.parent); return cloneTypeAsModuleType(symbol, moduleType, referenceParent); } } } } return symbol; } /** * Create a new symbol which has the module's type less the call and construct signatures */ function cloneTypeAsModuleType(symbol: Symbol, moduleType: Type, referenceParent: ImportDeclaration | ImportCall) { const result = createSymbol(symbol.flags, symbol.escapedName); result.declarations = symbol.declarations ? symbol.declarations.slice() : []; result.parent = symbol.parent; result.links.target = symbol; result.links.originatingImport = referenceParent; if (symbol.valueDeclaration) result.valueDeclaration = symbol.valueDeclaration; if (symbol.constEnumOnlyModule) result.constEnumOnlyModule = true; if (symbol.members) result.members = new Map(symbol.members); if (symbol.exports) result.exports = new Map(symbol.exports); const resolvedModuleType = resolveStructuredTypeMembers(moduleType as StructuredType); // Should already be resolved from the signature checks above result.links.type = createAnonymousType(result, resolvedModuleType.members, emptyArray, emptyArray, resolvedModuleType.indexInfos); return result; } function hasExportAssignmentSymbol(moduleSymbol: Symbol): boolean { return moduleSymbol.exports!.get(InternalSymbolName.ExportEquals) !== undefined; } function getExportsOfModuleAsArray(moduleSymbol: Symbol): Symbol[] { return symbolsToArray(getExportsOfModule(moduleSymbol)); } function getExportsAndPropertiesOfModule(moduleSymbol: Symbol): Symbol[] { const exports = getExportsOfModuleAsArray(moduleSymbol); const exportEquals = resolveExternalModuleSymbol(moduleSymbol); if (exportEquals !== moduleSymbol) { const type = getTypeOfSymbol(exportEquals); if (shouldTreatPropertiesOfExternalModuleAsExports(type)) { addRange(exports, getPropertiesOfType(type)); } } return exports; } function forEachExportAndPropertyOfModule(moduleSymbol: Symbol, cb: (symbol: Symbol, key: __String) => void): void { const exports = getExportsOfModule(moduleSymbol); exports.forEach((symbol, key) => { if (!isReservedMemberName(key)) { cb(symbol, key); } }); const exportEquals = resolveExternalModuleSymbol(moduleSymbol); if (exportEquals !== moduleSymbol) { const type = getTypeOfSymbol(exportEquals); if (shouldTreatPropertiesOfExternalModuleAsExports(type)) { forEachPropertyOfType(type, (symbol, escapedName) => { cb(symbol, escapedName); }); } } } function tryGetMemberInModuleExports(memberName: __String, moduleSymbol: Symbol): Symbol | undefined { const symbolTable = getExportsOfModule(moduleSymbol); if (symbolTable) { return symbolTable.get(memberName); } } function tryGetMemberInModuleExportsAndProperties(memberName: __String, moduleSymbol: Symbol): Symbol | undefined { const symbol = tryGetMemberInModuleExports(memberName, moduleSymbol); if (symbol) { return symbol; } const exportEquals = resolveExternalModuleSymbol(moduleSymbol); if (exportEquals === moduleSymbol) { return undefined; } const type = getTypeOfSymbol(exportEquals); return shouldTreatPropertiesOfExternalModuleAsExports(type) ? getPropertyOfType(type, memberName) : undefined; } function shouldTreatPropertiesOfExternalModuleAsExports(resolvedExternalModuleType: Type) { return !(resolvedExternalModuleType.flags & TypeFlags.Primitive || getObjectFlags(resolvedExternalModuleType) & ObjectFlags.Class || // `isArrayOrTupleLikeType` is too expensive to use in this auto-imports hot path isArrayType(resolvedExternalModuleType) || isTupleType(resolvedExternalModuleType)); } function getExportsOfSymbol(symbol: Symbol): SymbolTable { return symbol.flags & SymbolFlags.LateBindingContainer ? getResolvedMembersOrExportsOfSymbol(symbol, MembersOrExportsResolutionKind.resolvedExports) : symbol.flags & SymbolFlags.Module ? getExportsOfModule(symbol) : symbol.exports || emptySymbols; } function getExportsOfModule(moduleSymbol: Symbol): SymbolTable { const links = getSymbolLinks(moduleSymbol); if (!links.resolvedExports) { const { exports, typeOnlyExportStarMap } = getExportsOfModuleWorker(moduleSymbol); links.resolvedExports = exports; links.typeOnlyExportStarMap = typeOnlyExportStarMap; } return links.resolvedExports; } interface ExportCollisionTracker { specifierText: string; exportsWithDuplicate?: ExportDeclaration[]; } type ExportCollisionTrackerTable = Map<__String, ExportCollisionTracker>; /** * Extends one symbol table with another while collecting information on name collisions for error message generation into the `lookupTable` argument * Not passing `lookupTable` and `exportNode` disables this collection, and just extends the tables */ function extendExportSymbols(target: SymbolTable, source: SymbolTable | undefined, lookupTable?: ExportCollisionTrackerTable, exportNode?: ExportDeclaration) { if (!source) return; source.forEach((sourceSymbol, id) => { if (id === InternalSymbolName.Default) return; const targetSymbol = target.get(id); if (!targetSymbol) { target.set(id, sourceSymbol); if (lookupTable && exportNode) { lookupTable.set(id, { specifierText: getTextOfNode(exportNode.moduleSpecifier!), }); } } else if (lookupTable && exportNode && targetSymbol && resolveSymbol(targetSymbol) !== resolveSymbol(sourceSymbol)) { const collisionTracker = lookupTable.get(id)!; if (!collisionTracker.exportsWithDuplicate) { collisionTracker.exportsWithDuplicate = [exportNode]; } else { collisionTracker.exportsWithDuplicate.push(exportNode); } } }); } function getExportsOfModuleWorker(moduleSymbol: Symbol) { const visitedSymbols: Symbol[] = []; let typeOnlyExportStarMap: Map<__String, ExportDeclaration & { readonly isTypeOnly: true; readonly moduleSpecifier: Expression; }> | undefined; const nonTypeOnlyNames = new Set<__String>(); // A module defined by an 'export=' consists of one export that needs to be resolved moduleSymbol = resolveExternalModuleSymbol(moduleSymbol); const exports = visit(moduleSymbol) || emptySymbols; if (typeOnlyExportStarMap) { nonTypeOnlyNames.forEach(name => typeOnlyExportStarMap!.delete(name)); } return { exports, typeOnlyExportStarMap, }; // The ES6 spec permits export * declarations in a module to circularly reference the module itself. For example, // module 'a' can 'export * from "b"' and 'b' can 'export * from "a"' without error. function visit(symbol: Symbol | undefined, exportStar?: ExportDeclaration, isTypeOnly?: boolean): SymbolTable | undefined { if (!isTypeOnly && symbol?.exports) { // Add non-type-only names before checking if we've visited this module, // because we might have visited it via an 'export type *', and visiting // again with 'export *' will override the type-onlyness of its exports. symbol.exports.forEach((_, name) => nonTypeOnlyNames.add(name)); } if (!(symbol && symbol.exports && pushIfUnique(visitedSymbols, symbol))) { return; } const symbols = new Map(symbol.exports); // All export * declarations are collected in an __export symbol by the binder const exportStars = symbol.exports.get(InternalSymbolName.ExportStar); if (exportStars) { const nestedSymbols = createSymbolTable(); const lookupTable: ExportCollisionTrackerTable = new Map(); if (exportStars.declarations) { for (const node of exportStars.declarations) { const resolvedModule = resolveExternalModuleName(node, (node as ExportDeclaration).moduleSpecifier!); const exportedSymbols = visit(resolvedModule, node as ExportDeclaration, isTypeOnly || (node as ExportDeclaration).isTypeOnly); extendExportSymbols( nestedSymbols, exportedSymbols, lookupTable, node as ExportDeclaration, ); } } lookupTable.forEach(({ exportsWithDuplicate }, id) => { // It's not an error if the file with multiple `export *`s with duplicate names exports a member with that name itself if (id === "export=" || !(exportsWithDuplicate && exportsWithDuplicate.length) || symbols.has(id)) { return; } for (const node of exportsWithDuplicate) { diagnostics.add(createDiagnosticForNode( node, Diagnostics.Module_0_has_already_exported_a_member_named_1_Consider_explicitly_re_exporting_to_resolve_the_ambiguity, lookupTable.get(id)!.specifierText, unescapeLeadingUnderscores(id), )); } }); extendExportSymbols(symbols, nestedSymbols); } if (exportStar?.isTypeOnly) { typeOnlyExportStarMap ??= new Map(); symbols.forEach((_, escapedName) => typeOnlyExportStarMap!.set( escapedName, exportStar as ExportDeclaration & { readonly isTypeOnly: true; readonly moduleSpecifier: Expression; }, ) ); } return symbols; } } function getMergedSymbol(symbol: Symbol): Symbol; function getMergedSymbol(symbol: Symbol | undefined): Symbol | undefined; function getMergedSymbol(symbol: Symbol | undefined): Symbol | undefined { let merged: Symbol; return symbol && symbol.mergeId && (merged = mergedSymbols[symbol.mergeId]) ? merged : symbol; } function getSymbolOfDeclaration(node: Declaration): Symbol { return getMergedSymbol(node.symbol && getLateBoundSymbol(node.symbol)); } /** * Get the merged symbol for a node. If you know the node is a `Declaration`, it is faster and more type safe to * use use `getSymbolOfDeclaration` instead. */ function getSymbolOfNode(node: Node): Symbol | undefined { return canHaveSymbol(node) ? getSymbolOfDeclaration(node) : undefined; } function getParentOfSymbol(symbol: Symbol): Symbol | undefined { return getMergedSymbol(symbol.parent && getLateBoundSymbol(symbol.parent)); } function getFunctionExpressionParentSymbolOrSymbol(symbol: Symbol) { return symbol.valueDeclaration?.kind === SyntaxKind.ArrowFunction || symbol.valueDeclaration?.kind === SyntaxKind.FunctionExpression ? getSymbolOfNode(symbol.valueDeclaration.parent) || symbol : symbol; } function getAlternativeContainingModules(symbol: Symbol, enclosingDeclaration: Node): Symbol[] { const containingFile = getSourceFileOfNode(enclosingDeclaration); const id = getNodeId(containingFile); const links = getSymbolLinks(symbol); let results: Symbol[] | undefined; if (links.extendedContainersByFile && (results = links.extendedContainersByFile.get(id))) { return results; } if (containingFile && containingFile.imports) { // Try to make an import using an import already in the enclosing file, if possible for (const importRef of containingFile.imports) { if (nodeIsSynthesized(importRef)) continue; // Synthetic names can't be resolved by `resolveExternalModuleName` - they'll cause a debug assert if they error const resolvedModule = resolveExternalModuleName(enclosingDeclaration, importRef, /*ignoreErrors*/ true); if (!resolvedModule) continue; const ref = getAliasForSymbolInContainer(resolvedModule, symbol); if (!ref) continue; results = append(results, resolvedModule); } if (length(results)) { (links.extendedContainersByFile || (links.extendedContainersByFile = new Map())).set(id, results!); return results!; } } if (links.extendedContainers) { return links.extendedContainers; } // No results from files already being imported by this file - expand search (expensive, but not location-specific, so cached) const otherFiles = host.getSourceFiles(); for (const file of otherFiles) { if (!isExternalModule(file)) continue; const sym = getSymbolOfDeclaration(file); const ref = getAliasForSymbolInContainer(sym, symbol); if (!ref) continue; results = append(results, sym); } return links.extendedContainers = results || emptyArray; } /** * Attempts to find the symbol corresponding to the container a symbol is in - usually this * is just its' `.parent`, but for locals, this value is `undefined` */ function getContainersOfSymbol(symbol: Symbol, enclosingDeclaration: Node | undefined, meaning: SymbolFlags): Symbol[] | undefined { const container = getParentOfSymbol(symbol); // Type parameters end up in the `members` lists but are not externally visible if (container && !(symbol.flags & SymbolFlags.TypeParameter)) { return getWithAlternativeContainers(container); } const candidates = mapDefined(symbol.declarations, d => { if (!isAmbientModule(d) && d.parent) { // direct children of a module if (hasNonGlobalAugmentationExternalModuleSymbol(d.parent)) { return getSymbolOfDeclaration(d.parent as Declaration); } // export ='d member of an ambient module if (isModuleBlock(d.parent) && d.parent.parent && resolveExternalModuleSymbol(getSymbolOfDeclaration(d.parent.parent)) === symbol) { return getSymbolOfDeclaration(d.parent.parent); } } if (isClassExpression(d) && isBinaryExpression(d.parent) && d.parent.operatorToken.kind === SyntaxKind.EqualsToken && isAccessExpression(d.parent.left) && isEntityNameExpression(d.parent.left.expression)) { if (isModuleExportsAccessExpression(d.parent.left) || isExportsIdentifier(d.parent.left.expression)) { return getSymbolOfDeclaration(getSourceFileOfNode(d)); } checkExpressionCached(d.parent.left.expression); return getNodeLinks(d.parent.left.expression).resolvedSymbol; } }); if (!length(candidates)) { return undefined; } const containers = mapDefined(candidates, candidate => getAliasForSymbolInContainer(candidate, symbol) ? candidate : undefined); let bestContainers: Symbol[] = []; let alternativeContainers: Symbol[] = []; for (const container of containers) { const [bestMatch, ...rest] = getWithAlternativeContainers(container); bestContainers = append(bestContainers, bestMatch); alternativeContainers = addRange(alternativeContainers, rest); } return concatenate(bestContainers, alternativeContainers); function getWithAlternativeContainers(container: Symbol) { const additionalContainers = mapDefined(container.declarations, fileSymbolIfFileSymbolExportEqualsContainer); const reexportContainers = enclosingDeclaration && getAlternativeContainingModules(symbol, enclosingDeclaration); const objectLiteralContainer = getVariableDeclarationOfObjectLiteral(container, meaning); if ( enclosingDeclaration && container.flags & getQualifiedLeftMeaning(meaning) && getAccessibleSymbolChain(container, enclosingDeclaration, SymbolFlags.Namespace, /*useOnlyExternalAliasing*/ false) ) { return append(concatenate(concatenate([container], additionalContainers), reexportContainers), objectLiteralContainer); // This order expresses a preference for the real container if it is in scope } // we potentially have a symbol which is a member of the instance side of something - look for a variable in scope with the container's type // which may be acting like a namespace (eg, `Symbol` acts like a namespace when looking up `Symbol.toStringTag`) const firstVariableMatch = !(container.flags & getQualifiedLeftMeaning(meaning)) && container.flags & SymbolFlags.Type && getDeclaredTypeOfSymbol(container).flags & TypeFlags.Object && meaning === SymbolFlags.Value ? forEachSymbolTableInScope(enclosingDeclaration, t => { return forEachEntry(t, s => { if (s.flags & getQualifiedLeftMeaning(meaning) && getTypeOfSymbol(s) === getDeclaredTypeOfSymbol(container)) { return s; } }); }) : undefined; let res = firstVariableMatch ? [firstVariableMatch, ...additionalContainers, container] : [...additionalContainers, container]; res = append(res, objectLiteralContainer); res = addRange(res, reexportContainers); return res; } function fileSymbolIfFileSymbolExportEqualsContainer(d: Declaration) { return container && getFileSymbolIfFileSymbolExportEqualsContainer(d, container); } } function getVariableDeclarationOfObjectLiteral(symbol: Symbol, meaning: SymbolFlags) { // If we're trying to reference some object literal in, eg `var a = { x: 1 }`, the symbol for the literal, `__object`, is distinct // from the symbol of the declaration it is being assigned to. Since we can use the declaration to refer to the literal, however, // we'd like to make that connection here - potentially causing us to paint the declaration's visibility, and therefore the literal. const firstDecl: Node | false = !!length(symbol.declarations) && first(symbol.declarations!); if (meaning & SymbolFlags.Value && firstDecl && firstDecl.parent && isVariableDeclaration(firstDecl.parent)) { if (isObjectLiteralExpression(firstDecl) && firstDecl === firstDecl.parent.initializer || isTypeLiteralNode(firstDecl) && firstDecl === firstDecl.parent.type) { return getSymbolOfDeclaration(firstDecl.parent); } } } function getFileSymbolIfFileSymbolExportEqualsContainer(d: Declaration, container: Symbol) { const fileSymbol = getExternalModuleContainer(d); const exported = fileSymbol && fileSymbol.exports && fileSymbol.exports.get(InternalSymbolName.ExportEquals); return exported && getSymbolIfSameReference(exported, container) ? fileSymbol : undefined; } function getAliasForSymbolInContainer(container: Symbol, symbol: Symbol) { if (container === getParentOfSymbol(symbol)) { // fast path, `symbol` is either already the alias or isn't aliased return symbol; } // Check if container is a thing with an `export=` which points directly at `symbol`, and if so, return // the container itself as the alias for the symbol const exportEquals = container.exports && container.exports.get(InternalSymbolName.ExportEquals); if (exportEquals && getSymbolIfSameReference(exportEquals, symbol)) { return container; } const exports = getExportsOfSymbol(container); const quick = exports.get(symbol.escapedName); if (quick && getSymbolIfSameReference(quick, symbol)) { return quick; } return forEachEntry(exports, exported => { if (getSymbolIfSameReference(exported, symbol)) { return exported; } }); } /** * Checks if two symbols, through aliasing and/or merging, refer to the same thing */ function getSymbolIfSameReference(s1: Symbol, s2: Symbol) { if (getMergedSymbol(resolveSymbol(getMergedSymbol(s1))) === getMergedSymbol(resolveSymbol(getMergedSymbol(s2)))) { return s1; } } function getExportSymbolOfValueSymbolIfExported(symbol: Symbol): Symbol; function getExportSymbolOfValueSymbolIfExported(symbol: Symbol | undefined): Symbol | undefined; function getExportSymbolOfValueSymbolIfExported(symbol: Symbol | undefined): Symbol | undefined { return getMergedSymbol(symbol && (symbol.flags & SymbolFlags.ExportValue) !== 0 && symbol.exportSymbol || symbol); } function symbolIsValue(symbol: Symbol, includeTypeOnlyMembers?: boolean): boolean { return !!( symbol.flags & SymbolFlags.Value || symbol.flags & SymbolFlags.Alias && getSymbolFlags(symbol, !includeTypeOnlyMembers) & SymbolFlags.Value ); } function createType(flags: TypeFlags): Type { const result = new Type(checker, flags); typeCount++; result.id = typeCount; tracing?.recordType(result); return result; } function createTypeWithSymbol(flags: TypeFlags, symbol: Symbol): Type { const result = createType(flags); result.symbol = symbol; return result; } function createOriginType(flags: TypeFlags): Type { return new Type(checker, flags); } function createIntrinsicType(kind: TypeFlags, intrinsicName: string, objectFlags = ObjectFlags.None, debugIntrinsicName?: string): IntrinsicType { checkIntrinsicName(intrinsicName, debugIntrinsicName); const type = createType(kind) as IntrinsicType; type.intrinsicName = intrinsicName; type.debugIntrinsicName = debugIntrinsicName; type.objectFlags = objectFlags | ObjectFlags.CouldContainTypeVariablesComputed | ObjectFlags.IsGenericTypeComputed | ObjectFlags.IsUnknownLikeUnionComputed | ObjectFlags.IsNeverIntersectionComputed; return type; } function checkIntrinsicName(name: string, debug: string | undefined) { const key = `${name},${debug ?? ""}`; if (seenIntrinsicNames.has(key)) { Debug.fail(`Duplicate intrinsic type name ${name}${debug ? ` (${debug})` : ""}; you may need to pass a name to createIntrinsicType.`); } seenIntrinsicNames.add(key); } function createObjectType(objectFlags: ObjectFlags, symbol?: Symbol): ObjectType { const type = createTypeWithSymbol(TypeFlags.Object, symbol!) as ObjectType; type.objectFlags = objectFlags; type.members = undefined; type.properties = undefined; type.callSignatures = undefined; type.constructSignatures = undefined; type.indexInfos = undefined; return type; } function createTypeofType() { return getUnionType(arrayFrom(typeofNEFacts.keys(), getStringLiteralType)); } function createTypeParameter(symbol?: Symbol) { return createTypeWithSymbol(TypeFlags.TypeParameter, symbol!) as TypeParameter; } // A reserved member name starts with two underscores, but the third character cannot be an underscore, // @, or #. A third underscore indicates an escaped form of an identifier that started // with at least two underscores. The @ character indicates that the name is denoted by a well known ES // Symbol instance and the # character indicates that the name is a PrivateIdentifier. function isReservedMemberName(name: __String) { return (name as string).charCodeAt(0) === CharacterCodes._ && (name as string).charCodeAt(1) === CharacterCodes._ && (name as string).charCodeAt(2) !== CharacterCodes._ && (name as string).charCodeAt(2) !== CharacterCodes.at && (name as string).charCodeAt(2) !== CharacterCodes.hash; } function getNamedMembers(members: SymbolTable): Symbol[] { let result: Symbol[] | undefined; members.forEach((symbol, id) => { if (isNamedMember(symbol, id)) { (result || (result = [])).push(symbol); } }); return result || emptyArray; } function isNamedMember(member: Symbol, escapedName: __String) { return !isReservedMemberName(escapedName) && symbolIsValue(member); } function getNamedOrIndexSignatureMembers(members: SymbolTable): Symbol[] { const result = getNamedMembers(members); const index = getIndexSymbolFromSymbolTable(members); return index ? concatenate(result, [index]) : result; } function setStructuredTypeMembers(type: StructuredType, members: SymbolTable, callSignatures: readonly Signature[], constructSignatures: readonly Signature[], indexInfos: readonly IndexInfo[]): ResolvedType { const resolved = type as ResolvedType; resolved.members = members; resolved.properties = emptyArray; resolved.callSignatures = callSignatures; resolved.constructSignatures = constructSignatures; resolved.indexInfos = indexInfos; // This can loop back to getPropertyOfType() which would crash if `callSignatures` & `constructSignatures` are not initialized. if (members !== emptySymbols) resolved.properties = getNamedMembers(members); return resolved; } function createAnonymousType(symbol: Symbol | undefined, members: SymbolTable, callSignatures: readonly Signature[], constructSignatures: readonly Signature[], indexInfos: readonly IndexInfo[]): ResolvedType { return setStructuredTypeMembers(createObjectType(ObjectFlags.Anonymous, symbol), members, callSignatures, constructSignatures, indexInfos); } function getResolvedTypeWithoutAbstractConstructSignatures(type: ResolvedType) { if (type.constructSignatures.length === 0) return type; if (type.objectTypeWithoutAbstractConstructSignatures) return type.objectTypeWithoutAbstractConstructSignatures; const constructSignatures = filter(type.constructSignatures, signature => !(signature.flags & SignatureFlags.Abstract)); if (type.constructSignatures === constructSignatures) return type; const typeCopy = createAnonymousType( type.symbol, type.members, type.callSignatures, some(constructSignatures) ? constructSignatures : emptyArray, type.indexInfos, ); type.objectTypeWithoutAbstractConstructSignatures = typeCopy; typeCopy.objectTypeWithoutAbstractConstructSignatures = typeCopy; return typeCopy; } function forEachSymbolTableInScope(enclosingDeclaration: Node | undefined, callback: (symbolTable: SymbolTable, ignoreQualification?: boolean, isLocalNameLookup?: boolean, scopeNode?: Node) => T): T { let result: T; for (let location = enclosingDeclaration; location; location = location.parent) { // Locals of a source file are not in scope (because they get merged into the global symbol table) if (canHaveLocals(location) && location.locals && !isGlobalSourceFile(location)) { if (result = callback(location.locals, /*ignoreQualification*/ undefined, /*isLocalNameLookup*/ true, location)) { return result; } } switch (location.kind) { case SyntaxKind.SourceFile: if (!isExternalOrCommonJsModule(location as SourceFile)) { break; } // falls through case SyntaxKind.ModuleDeclaration: const sym = getSymbolOfDeclaration(location as ModuleDeclaration); // `sym` may not have exports if this module declaration is backed by the symbol for a `const` that's being rewritten // into a namespace - in such cases, it's best to just let the namespace appear empty (the const members couldn't have referred // to one another anyway) if (result = callback(sym?.exports || emptySymbols, /*ignoreQualification*/ undefined, /*isLocalNameLookup*/ true, location)) { return result; } break; case SyntaxKind.ClassDeclaration: case SyntaxKind.ClassExpression: case SyntaxKind.InterfaceDeclaration: // Type parameters are bound into `members` lists so they can merge across declarations // This is troublesome, since in all other respects, they behave like locals :cries: // TODO: the below is shared with similar code in `resolveName` - in fact, rephrasing all this symbol // lookup logic in terms of `resolveName` would be nice // The below is used to lookup type parameters within a class or interface, as they are added to the class/interface locals // These can never be latebound, so the symbol's raw members are sufficient. `getMembersOfNode` cannot be used, as it would // trigger resolving late-bound names, which we may already be in the process of doing while we're here! let table: Map<__String, Symbol> | undefined; // TODO: Should this filtered table be cached in some way? (getSymbolOfDeclaration(location as ClassLikeDeclaration | InterfaceDeclaration).members || emptySymbols).forEach((memberSymbol, key) => { if (memberSymbol.flags & (SymbolFlags.Type & ~SymbolFlags.Assignment)) { (table || (table = createSymbolTable())).set(key, memberSymbol); } }); if (table && (result = callback(table, /*ignoreQualification*/ undefined, /*isLocalNameLookup*/ false, location))) { return result; } break; } } return callback(globals, /*ignoreQualification*/ undefined, /*isLocalNameLookup*/ true); } function getQualifiedLeftMeaning(rightMeaning: SymbolFlags) { // If we are looking in value space, the parent meaning is value, other wise it is namespace return rightMeaning === SymbolFlags.Value ? SymbolFlags.Value : SymbolFlags.Namespace; } function getAccessibleSymbolChain(symbol: Symbol | undefined, enclosingDeclaration: Node | undefined, meaning: SymbolFlags, useOnlyExternalAliasing: boolean, visitedSymbolTablesMap = new Map()): Symbol[] | undefined { if (!(symbol && !isPropertyOrMethodDeclarationSymbol(symbol))) { return undefined; } const links = getSymbolLinks(symbol); const cache = (links.accessibleChainCache ||= new Map()); // Go from enclosingDeclaration to the first scope we check, so the cache is keyed off the scope and thus shared more const firstRelevantLocation = forEachSymbolTableInScope(enclosingDeclaration, (_, __, ___, node) => node); const key = `${useOnlyExternalAliasing ? 0 : 1}|${firstRelevantLocation && getNodeId(firstRelevantLocation)}|${meaning}`; if (cache.has(key)) { return cache.get(key); } const id = getSymbolId(symbol); let visitedSymbolTables = visitedSymbolTablesMap.get(id); if (!visitedSymbolTables) { visitedSymbolTablesMap.set(id, visitedSymbolTables = []); } const result = forEachSymbolTableInScope(enclosingDeclaration, getAccessibleSymbolChainFromSymbolTable); cache.set(key, result); return result; /** * @param {ignoreQualification} boolean Set when a symbol is being looked for through the exports of another symbol (meaning we have a route to qualify it already) */ function getAccessibleSymbolChainFromSymbolTable(symbols: SymbolTable, ignoreQualification?: boolean, isLocalNameLookup?: boolean): Symbol[] | undefined { if (!pushIfUnique(visitedSymbolTables!, symbols)) { return undefined; } const result = trySymbolTable(symbols, ignoreQualification, isLocalNameLookup); visitedSymbolTables!.pop(); return result; } function canQualifySymbol(symbolFromSymbolTable: Symbol, meaning: SymbolFlags) { // If the symbol is equivalent and doesn't need further qualification, this symbol is accessible return !needsQualification(symbolFromSymbolTable, enclosingDeclaration, meaning) || // If symbol needs qualification, make sure that parent is accessible, if it is then this symbol is accessible too !!getAccessibleSymbolChain(symbolFromSymbolTable.parent, enclosingDeclaration, getQualifiedLeftMeaning(meaning), useOnlyExternalAliasing, visitedSymbolTablesMap); } function isAccessible(symbolFromSymbolTable: Symbol, resolvedAliasSymbol?: Symbol, ignoreQualification?: boolean) { return (symbol === (resolvedAliasSymbol || symbolFromSymbolTable) || getMergedSymbol(symbol) === getMergedSymbol(resolvedAliasSymbol || symbolFromSymbolTable)) && // if the symbolFromSymbolTable is not external module (it could be if it was determined as ambient external module and would be in globals table) // and if symbolFromSymbolTable or alias resolution matches the symbol, // check the symbol can be qualified, it is only then this symbol is accessible !some(symbolFromSymbolTable.declarations, hasNonGlobalAugmentationExternalModuleSymbol) && (ignoreQualification || canQualifySymbol(getMergedSymbol(symbolFromSymbolTable), meaning)); } function trySymbolTable(symbols: SymbolTable, ignoreQualification: boolean | undefined, isLocalNameLookup: boolean | undefined): Symbol[] | undefined { // If symbol is directly available by its name in the symbol table if (isAccessible(symbols.get(symbol!.escapedName)!, /*resolvedAliasSymbol*/ undefined, ignoreQualification)) { return [symbol!]; } // Check if symbol is any of the aliases in scope const result = forEachEntry(symbols, symbolFromSymbolTable => { if ( symbolFromSymbolTable.flags & SymbolFlags.Alias && symbolFromSymbolTable.escapedName !== InternalSymbolName.ExportEquals && symbolFromSymbolTable.escapedName !== InternalSymbolName.Default && !(isUMDExportSymbol(symbolFromSymbolTable) && enclosingDeclaration && isExternalModule(getSourceFileOfNode(enclosingDeclaration))) // If `!useOnlyExternalAliasing`, we can use any type of alias to get the name && (!useOnlyExternalAliasing || some(symbolFromSymbolTable.declarations, isExternalModuleImportEqualsDeclaration)) // If we're looking up a local name to reference directly, omit namespace reexports, otherwise when we're trawling through an export list to make a dotted name, we can keep it && (isLocalNameLookup ? !some(symbolFromSymbolTable.declarations, isNamespaceReexportDeclaration) : true) // While exports are generally considered to be in scope, export-specifier declared symbols are _not_ // See similar comment in `resolveName` for details && (ignoreQualification || !getDeclarationOfKind(symbolFromSymbolTable, SyntaxKind.ExportSpecifier)) ) { const resolvedImportedSymbol = resolveAlias(symbolFromSymbolTable); const candidate = getCandidateListForSymbol(symbolFromSymbolTable, resolvedImportedSymbol, ignoreQualification); if (candidate) { return candidate; } } if (symbolFromSymbolTable.escapedName === symbol!.escapedName && symbolFromSymbolTable.exportSymbol) { if (isAccessible(getMergedSymbol(symbolFromSymbolTable.exportSymbol), /*resolvedAliasSymbol*/ undefined, ignoreQualification)) { return [symbol!]; } } }); // If there's no result and we're looking at the global symbol table, treat `globalThis` like an alias and try to lookup thru that return result || (symbols === globals ? getCandidateListForSymbol(globalThisSymbol, globalThisSymbol, ignoreQualification) : undefined); } function getCandidateListForSymbol(symbolFromSymbolTable: Symbol, resolvedImportedSymbol: Symbol, ignoreQualification: boolean | undefined) { if (isAccessible(symbolFromSymbolTable, resolvedImportedSymbol, ignoreQualification)) { return [symbolFromSymbolTable]; } // Look in the exported members, if we can find accessibleSymbolChain, symbol is accessible using this chain // but only if the symbolFromSymbolTable can be qualified const candidateTable = getExportsOfSymbol(resolvedImportedSymbol); const accessibleSymbolsFromExports = candidateTable && getAccessibleSymbolChainFromSymbolTable(candidateTable, /*ignoreQualification*/ true); if (accessibleSymbolsFromExports && canQualifySymbol(symbolFromSymbolTable, getQualifiedLeftMeaning(meaning))) { return [symbolFromSymbolTable].concat(accessibleSymbolsFromExports); } } } function needsQualification(symbol: Symbol, enclosingDeclaration: Node | undefined, meaning: SymbolFlags) { let qualify = false; forEachSymbolTableInScope(enclosingDeclaration, symbolTable => { // If symbol of this name is not available in the symbol table we are ok let symbolFromSymbolTable = getMergedSymbol(symbolTable.get(symbol.escapedName)); if (!symbolFromSymbolTable) { // Continue to the next symbol table return false; } // If the symbol with this name is present it should refer to the symbol if (symbolFromSymbolTable === symbol) { // No need to qualify return true; } // Qualify if the symbol from symbol table has same meaning as expected const shouldResolveAlias = symbolFromSymbolTable.flags & SymbolFlags.Alias && !getDeclarationOfKind(symbolFromSymbolTable, SyntaxKind.ExportSpecifier); symbolFromSymbolTable = shouldResolveAlias ? resolveAlias(symbolFromSymbolTable) : symbolFromSymbolTable; const flags = shouldResolveAlias ? getSymbolFlags(symbolFromSymbolTable) : symbolFromSymbolTable.flags; if (flags & meaning) { qualify = true; return true; } // Continue to the next symbol table return false; }); return qualify; } function isPropertyOrMethodDeclarationSymbol(symbol: Symbol) { if (symbol.declarations && symbol.declarations.length) { for (const declaration of symbol.declarations) { switch (declaration.kind) { case SyntaxKind.PropertyDeclaration: case SyntaxKind.MethodDeclaration: case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: continue; default: return false; } } return true; } return false; } function isTypeSymbolAccessible(typeSymbol: Symbol, enclosingDeclaration: Node | undefined): boolean { const access = isSymbolAccessibleWorker(typeSymbol, enclosingDeclaration, SymbolFlags.Type, /*shouldComputeAliasesToMakeVisible*/ false, /*allowModules*/ true); return access.accessibility === SymbolAccessibility.Accessible; } function isValueSymbolAccessible(typeSymbol: Symbol, enclosingDeclaration: Node | undefined): boolean { const access = isSymbolAccessibleWorker(typeSymbol, enclosingDeclaration, SymbolFlags.Value, /*shouldComputeAliasesToMakeVisible*/ false, /*allowModules*/ true); return access.accessibility === SymbolAccessibility.Accessible; } function isSymbolAccessibleByFlags(typeSymbol: Symbol, enclosingDeclaration: Node | undefined, flags: SymbolFlags): boolean { const access = isSymbolAccessibleWorker(typeSymbol, enclosingDeclaration, flags, /*shouldComputeAliasesToMakeVisible*/ false, /*allowModules*/ false); return access.accessibility === SymbolAccessibility.Accessible; } function isAnySymbolAccessible(symbols: Symbol[] | undefined, enclosingDeclaration: Node | undefined, initialSymbol: Symbol, meaning: SymbolFlags, shouldComputeAliasesToMakeVisible: boolean, allowModules: boolean): SymbolAccessibilityResult | undefined { if (!length(symbols)) return; let hadAccessibleChain: Symbol | undefined; let earlyModuleBail = false; for (const symbol of symbols!) { // Symbol is accessible if it by itself is accessible const accessibleSymbolChain = getAccessibleSymbolChain(symbol, enclosingDeclaration, meaning, /*useOnlyExternalAliasing*/ false); if (accessibleSymbolChain) { hadAccessibleChain = symbol; const hasAccessibleDeclarations = hasVisibleDeclarations(accessibleSymbolChain[0], shouldComputeAliasesToMakeVisible); if (hasAccessibleDeclarations) { return hasAccessibleDeclarations; } } if (allowModules) { if (some(symbol.declarations, hasNonGlobalAugmentationExternalModuleSymbol)) { if (shouldComputeAliasesToMakeVisible) { earlyModuleBail = true; // Generally speaking, we want to use the aliases that already exist to refer to a module, if present // In order to do so, we need to find those aliases in order to retain them in declaration emit; so // if we are in declaration emit, we cannot use the fast path for module visibility until we've exhausted // all other visibility options (in order to capture the possible aliases used to reference the module) continue; } // Any meaning of a module symbol is always accessible via an `import` type return { accessibility: SymbolAccessibility.Accessible, }; } } // If we haven't got the accessible symbol, it doesn't mean the symbol is actually inaccessible. // It could be a qualified symbol and hence verify the path // e.g.: // module m { // export class c { // } // } // const x: typeof m.c // In the above example when we start with checking if typeof m.c symbol is accessible, // we are going to see if c can be accessed in scope directly. // But it can't, hence the accessible is going to be undefined, but that doesn't mean m.c is inaccessible // It is accessible if the parent m is accessible because then m.c can be accessed through qualification const containers = getContainersOfSymbol(symbol, enclosingDeclaration, meaning); const parentResult = isAnySymbolAccessible(containers, enclosingDeclaration, initialSymbol, initialSymbol === symbol ? getQualifiedLeftMeaning(meaning) : meaning, shouldComputeAliasesToMakeVisible, allowModules); if (parentResult) { return parentResult; } } if (earlyModuleBail) { return { accessibility: SymbolAccessibility.Accessible, }; } if (hadAccessibleChain) { return { accessibility: SymbolAccessibility.NotAccessible, errorSymbolName: symbolToString(initialSymbol, enclosingDeclaration, meaning), errorModuleName: hadAccessibleChain !== initialSymbol ? symbolToString(hadAccessibleChain, enclosingDeclaration, SymbolFlags.Namespace) : undefined, }; } } /** * Check if the given symbol in given enclosing declaration is accessible and mark all associated alias to be visible if requested * * @param symbol a Symbol to check if accessible * @param enclosingDeclaration a Node containing reference to the symbol * @param meaning a SymbolFlags to check if such meaning of the symbol is accessible * @param shouldComputeAliasToMakeVisible a boolean value to indicate whether to return aliases to be mark visible in case the symbol is accessible */ function isSymbolAccessible(symbol: Symbol | undefined, enclosingDeclaration: Node | undefined, meaning: SymbolFlags, shouldComputeAliasesToMakeVisible: boolean): SymbolAccessibilityResult { return isSymbolAccessibleWorker(symbol, enclosingDeclaration, meaning, shouldComputeAliasesToMakeVisible, /*allowModules*/ true); } function isSymbolAccessibleWorker(symbol: Symbol | undefined, enclosingDeclaration: Node | undefined, meaning: SymbolFlags, shouldComputeAliasesToMakeVisible: boolean, allowModules: boolean): SymbolAccessibilityResult { if (symbol && enclosingDeclaration) { const result = isAnySymbolAccessible([symbol], enclosingDeclaration, symbol, meaning, shouldComputeAliasesToMakeVisible, allowModules); if (result) { return result; } // This could be a symbol that is not exported in the external module // or it could be a symbol from different external module that is not aliased and hence cannot be named const symbolExternalModule = forEach(symbol.declarations, getExternalModuleContainer); if (symbolExternalModule) { const enclosingExternalModule = getExternalModuleContainer(enclosingDeclaration); if (symbolExternalModule !== enclosingExternalModule) { // name from different external module that is not visible return { accessibility: SymbolAccessibility.CannotBeNamed, errorSymbolName: symbolToString(symbol, enclosingDeclaration, meaning), errorModuleName: symbolToString(symbolExternalModule), errorNode: isInJSFile(enclosingDeclaration) ? enclosingDeclaration : undefined, }; } } // Just a local name that is not accessible return { accessibility: SymbolAccessibility.NotAccessible, errorSymbolName: symbolToString(symbol, enclosingDeclaration, meaning), }; } return { accessibility: SymbolAccessibility.Accessible }; } function getExternalModuleContainer(declaration: Node) { const node = findAncestor(declaration, hasExternalModuleSymbol); return node && getSymbolOfDeclaration(node as AmbientModuleDeclaration | SourceFile); } function hasExternalModuleSymbol(declaration: Node) { return isAmbientModule(declaration) || (declaration.kind === SyntaxKind.SourceFile && isExternalOrCommonJsModule(declaration as SourceFile)); } function hasNonGlobalAugmentationExternalModuleSymbol(declaration: Node) { return isModuleWithStringLiteralName(declaration) || (declaration.kind === SyntaxKind.SourceFile && isExternalOrCommonJsModule(declaration as SourceFile)); } function hasVisibleDeclarations(symbol: Symbol, shouldComputeAliasToMakeVisible: boolean): SymbolVisibilityResult | undefined { let aliasesToMakeVisible: LateVisibilityPaintedStatement[] | undefined; if (!every(filter(symbol.declarations, d => d.kind !== SyntaxKind.Identifier), getIsDeclarationVisible)) { return undefined; } return { accessibility: SymbolAccessibility.Accessible, aliasesToMakeVisible }; function getIsDeclarationVisible(declaration: Declaration) { if (!isDeclarationVisible(declaration)) { // Mark the unexported alias as visible if its parent is visible // because these kind of aliases can be used to name types in declaration file const anyImportSyntax = getAnyImportSyntax(declaration); if ( anyImportSyntax && !hasSyntacticModifier(anyImportSyntax, ModifierFlags.Export) && // import clause without export isDeclarationVisible(anyImportSyntax.parent) ) { return addVisibleAlias(declaration, anyImportSyntax); } else if ( isVariableDeclaration(declaration) && isVariableStatement(declaration.parent.parent) && !hasSyntacticModifier(declaration.parent.parent, ModifierFlags.Export) && // unexported variable statement isDeclarationVisible(declaration.parent.parent.parent) ) { return addVisibleAlias(declaration, declaration.parent.parent); } else if ( isLateVisibilityPaintedStatement(declaration) // unexported top-level statement && !hasSyntacticModifier(declaration, ModifierFlags.Export) && isDeclarationVisible(declaration.parent) ) { return addVisibleAlias(declaration, declaration); } else if (isBindingElement(declaration)) { if ( symbol.flags & SymbolFlags.Alias && isInJSFile(declaration) && declaration.parent?.parent // exported import-like top-level JS require statement && isVariableDeclaration(declaration.parent.parent) && declaration.parent.parent.parent?.parent && isVariableStatement(declaration.parent.parent.parent.parent) && !hasSyntacticModifier(declaration.parent.parent.parent.parent, ModifierFlags.Export) && declaration.parent.parent.parent.parent.parent // check if the thing containing the variable statement is visible (ie, the file) && isDeclarationVisible(declaration.parent.parent.parent.parent.parent) ) { return addVisibleAlias(declaration, declaration.parent.parent.parent.parent); } else if (symbol.flags & SymbolFlags.BlockScopedVariable) { const variableStatement = findAncestor(declaration, isVariableStatement)!; if (hasSyntacticModifier(variableStatement, ModifierFlags.Export)) { return true; } if (!isDeclarationVisible(variableStatement.parent)) { return false; } return addVisibleAlias(declaration, variableStatement); } } // Declaration is not visible return false; } return true; } function addVisibleAlias(declaration: Declaration, aliasingStatement: LateVisibilityPaintedStatement) { // In function "buildTypeDisplay" where we decide whether to write type-alias or serialize types, // we want to just check if type- alias is accessible or not but we don't care about emitting those alias at that time // since we will do the emitting later in trackSymbol. if (shouldComputeAliasToMakeVisible) { getNodeLinks(declaration).isVisible = true; aliasesToMakeVisible = appendIfUnique(aliasesToMakeVisible, aliasingStatement); } return true; } } function getMeaningOfEntityNameReference(entityName: EntityNameOrEntityNameExpression): SymbolFlags { // get symbol of the first identifier of the entityName let meaning: SymbolFlags; if ( entityName.parent.kind === SyntaxKind.TypeQuery || entityName.parent.kind === SyntaxKind.ExpressionWithTypeArguments && !isPartOfTypeNode(entityName.parent) || entityName.parent.kind === SyntaxKind.ComputedPropertyName || entityName.parent.kind === SyntaxKind.TypePredicate && (entityName.parent as TypePredicateNode).parameterName === entityName ) { // Typeof value meaning = SymbolFlags.Value | SymbolFlags.ExportValue; } else if ( entityName.kind === SyntaxKind.QualifiedName || entityName.kind === SyntaxKind.PropertyAccessExpression || entityName.parent.kind === SyntaxKind.ImportEqualsDeclaration || (entityName.parent.kind === SyntaxKind.QualifiedName && (entityName.parent as QualifiedName).left === entityName) || (entityName.parent.kind === SyntaxKind.PropertyAccessExpression && (entityName.parent as PropertyAccessExpression).expression === entityName) || (entityName.parent.kind === SyntaxKind.ElementAccessExpression && (entityName.parent as ElementAccessExpression).expression === entityName) ) { // Left identifier from type reference or TypeAlias // Entity name of the import declaration meaning = SymbolFlags.Namespace; } else { // Type Reference or TypeAlias entity = Identifier meaning = SymbolFlags.Type; } return meaning; } function isEntityNameVisible(entityName: EntityNameOrEntityNameExpression, enclosingDeclaration: Node, shouldComputeAliasToMakeVisible = true): SymbolVisibilityResult { const meaning = getMeaningOfEntityNameReference(entityName); const firstIdentifier = getFirstIdentifier(entityName); const symbol = resolveName(enclosingDeclaration, firstIdentifier.escapedText, meaning, /*nameNotFoundMessage*/ undefined, /*isUse*/ false); if (symbol && symbol.flags & SymbolFlags.TypeParameter && meaning & SymbolFlags.Type) { return { accessibility: SymbolAccessibility.Accessible }; } if (!symbol && isThisIdentifier(firstIdentifier) && isSymbolAccessible(getSymbolOfDeclaration(getThisContainer(firstIdentifier, /*includeArrowFunctions*/ false, /*includeClassComputedPropertyName*/ false)), firstIdentifier, meaning, /*shouldComputeAliasesToMakeVisible*/ false).accessibility === SymbolAccessibility.Accessible) { return { accessibility: SymbolAccessibility.Accessible }; } if (!symbol) { return { accessibility: SymbolAccessibility.NotResolved, errorSymbolName: getTextOfNode(firstIdentifier), errorNode: firstIdentifier, }; } // Verify if the symbol is accessible return hasVisibleDeclarations(symbol, shouldComputeAliasToMakeVisible) || { accessibility: SymbolAccessibility.NotAccessible, errorSymbolName: getTextOfNode(firstIdentifier), errorNode: firstIdentifier, }; } function symbolToString(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags, flags: SymbolFormatFlags = SymbolFormatFlags.AllowAnyNodeKind, writer?: EmitTextWriter): string { let nodeFlags = NodeBuilderFlags.IgnoreErrors; let internalNodeFlags = InternalNodeBuilderFlags.None; if (flags & SymbolFormatFlags.UseOnlyExternalAliasing) { nodeFlags |= NodeBuilderFlags.UseOnlyExternalAliasing; } if (flags & SymbolFormatFlags.WriteTypeParametersOrArguments) { nodeFlags |= NodeBuilderFlags.WriteTypeParametersInQualifiedName; } if (flags & SymbolFormatFlags.UseAliasDefinedOutsideCurrentScope) { nodeFlags |= NodeBuilderFlags.UseAliasDefinedOutsideCurrentScope; } if (flags & SymbolFormatFlags.DoNotIncludeSymbolChain) { internalNodeFlags |= InternalNodeBuilderFlags.DoNotIncludeSymbolChain; } if (flags & SymbolFormatFlags.WriteComputedProps) { internalNodeFlags |= InternalNodeBuilderFlags.WriteComputedProps; } const builder = flags & SymbolFormatFlags.AllowAnyNodeKind ? nodeBuilder.symbolToNode : nodeBuilder.symbolToEntityName; return writer ? symbolToStringWorker(writer).getText() : usingSingleLineStringWriter(symbolToStringWorker); function symbolToStringWorker(writer: EmitTextWriter) { const entity = builder(symbol, meaning!, enclosingDeclaration, nodeFlags, internalNodeFlags)!; // TODO: GH#18217 // add neverAsciiEscape for GH#39027 const printer = enclosingDeclaration?.kind === SyntaxKind.SourceFile ? createPrinterWithRemoveCommentsNeverAsciiEscape() : createPrinterWithRemoveComments(); const sourceFile = enclosingDeclaration && getSourceFileOfNode(enclosingDeclaration); printer.writeNode(EmitHint.Unspecified, entity, /*sourceFile*/ sourceFile, writer); return writer; } } function signatureToString(signature: Signature, enclosingDeclaration?: Node, flags = TypeFormatFlags.None, kind?: SignatureKind, writer?: EmitTextWriter): string { return writer ? signatureToStringWorker(writer).getText() : usingSingleLineStringWriter(signatureToStringWorker); function signatureToStringWorker(writer: EmitTextWriter) { let sigOutput: SyntaxKind; if (flags & TypeFormatFlags.WriteArrowStyleSignature) { sigOutput = kind === SignatureKind.Construct ? SyntaxKind.ConstructorType : SyntaxKind.FunctionType; } else { sigOutput = kind === SignatureKind.Construct ? SyntaxKind.ConstructSignature : SyntaxKind.CallSignature; } const sig = nodeBuilder.signatureToSignatureDeclaration(signature, sigOutput, enclosingDeclaration, toNodeBuilderFlags(flags) | NodeBuilderFlags.IgnoreErrors | NodeBuilderFlags.WriteTypeParametersInQualifiedName); const printer = createPrinterWithRemoveCommentsOmitTrailingSemicolon(); const sourceFile = enclosingDeclaration && getSourceFileOfNode(enclosingDeclaration); printer.writeNode(EmitHint.Unspecified, sig!, /*sourceFile*/ sourceFile, getTrailingSemicolonDeferringWriter(writer)); // TODO: GH#18217 return writer; } } function typeToString(type: Type, enclosingDeclaration?: Node, flags: TypeFormatFlags = TypeFormatFlags.AllowUniqueESSymbolType | TypeFormatFlags.UseAliasDefinedOutsideCurrentScope, writer: EmitTextWriter = createTextWriter("")): string { const noTruncation = compilerOptions.noErrorTruncation || flags & TypeFormatFlags.NoTruncation; const typeNode = nodeBuilder.typeToTypeNode(type, enclosingDeclaration, toNodeBuilderFlags(flags) | NodeBuilderFlags.IgnoreErrors | (noTruncation ? NodeBuilderFlags.NoTruncation : NodeBuilderFlags.None), /*internalFlags*/ undefined); if (typeNode === undefined) return Debug.fail("should always get typenode"); // The unresolved type gets a synthesized comment on `any` to hint to users that it's not a plain `any`. // Otherwise, we always strip comments out. const printer = type !== unresolvedType ? createPrinterWithRemoveComments() : createPrinterWithDefaults(); const sourceFile = enclosingDeclaration && getSourceFileOfNode(enclosingDeclaration); printer.writeNode(EmitHint.Unspecified, typeNode, /*sourceFile*/ sourceFile, writer); const result = writer.getText(); const maxLength = noTruncation ? noTruncationMaximumTruncationLength * 2 : defaultMaximumTruncationLength * 2; if (maxLength && result && result.length >= maxLength) { return result.substr(0, maxLength - "...".length) + "..."; } return result; } function getTypeNamesForErrorDisplay(left: Type, right: Type): [string, string] { let leftStr = symbolValueDeclarationIsContextSensitive(left.symbol) ? typeToString(left, left.symbol.valueDeclaration) : typeToString(left); let rightStr = symbolValueDeclarationIsContextSensitive(right.symbol) ? typeToString(right, right.symbol.valueDeclaration) : typeToString(right); if (leftStr === rightStr) { leftStr = getTypeNameForErrorDisplay(left); rightStr = getTypeNameForErrorDisplay(right); } return [leftStr, rightStr]; } function getTypeNameForErrorDisplay(type: Type) { return typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.UseFullyQualifiedType); } function symbolValueDeclarationIsContextSensitive(symbol: Symbol): boolean { return symbol && !!symbol.valueDeclaration && isExpression(symbol.valueDeclaration) && !isContextSensitive(symbol.valueDeclaration); } function toNodeBuilderFlags(flags = TypeFormatFlags.None): NodeBuilderFlags { return flags & TypeFormatFlags.NodeBuilderFlagsMask; } function isClassInstanceSide(type: Type) { return !!type.symbol && !!(type.symbol.flags & SymbolFlags.Class) && (type === getDeclaredTypeOfClassOrInterface(type.symbol) || (!!(type.flags & TypeFlags.Object) && !!(getObjectFlags(type) & ObjectFlags.IsClassInstanceClone))); } /** * Same as getTypeFromTypeNode, but for use in createNodeBuilder * Inside createNodeBuilder we shadow getTypeFromTypeNode to make sure anyone using this function will call the local version that does type mapping if appropriate * This function is used to still be able to call the original getTypeFromTypeNode from the local scope version of getTypeFromTypeNode */ function getTypeFromTypeNodeWithoutContext(node: TypeNode) { return getTypeFromTypeNode(node); } function createNodeBuilder() { return { typeToTypeNode: (type: Type, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, internalFlags?: InternalNodeBuilderFlags, tracker?: SymbolTracker) => withContext(enclosingDeclaration, flags, internalFlags, tracker, context => typeToTypeNodeHelper(type, context)), typePredicateToTypePredicateNode: (typePredicate: TypePredicate, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, internalFlags?: InternalNodeBuilderFlags, tracker?: SymbolTracker) => withContext(enclosingDeclaration, flags, internalFlags, tracker, context => typePredicateToTypePredicateNodeHelper(typePredicate, context)), expressionOrTypeToTypeNode: (expr: Expression | JsxAttributeValue | undefined, type: Type, addUndefined?: boolean, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, internalFlags?: InternalNodeBuilderFlags, tracker?: SymbolTracker) => withContext(enclosingDeclaration, flags, internalFlags, tracker, context => expressionOrTypeToTypeNode(context, expr, type, addUndefined)), serializeTypeForDeclaration: (declaration: Declaration, type: Type, symbol: Symbol, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, internalFlags?: InternalNodeBuilderFlags, tracker?: SymbolTracker) => withContext(enclosingDeclaration, flags, internalFlags, tracker, context => serializeTypeForDeclaration(context, declaration, type, symbol)), serializeReturnTypeForSignature: (signature: Signature, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, internalFlags?: InternalNodeBuilderFlags, tracker?: SymbolTracker) => withContext(enclosingDeclaration, flags, internalFlags, tracker, context => serializeReturnTypeForSignature(context, signature)), indexInfoToIndexSignatureDeclaration: (indexInfo: IndexInfo, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, internalFlags?: InternalNodeBuilderFlags, tracker?: SymbolTracker) => withContext(enclosingDeclaration, flags, internalFlags, tracker, context => indexInfoToIndexSignatureDeclarationHelper(indexInfo, context, /*typeNode*/ undefined)), signatureToSignatureDeclaration: (signature: Signature, kind: SignatureDeclaration["kind"], enclosingDeclaration?: Node, flags?: NodeBuilderFlags, internalFlags?: InternalNodeBuilderFlags, tracker?: SymbolTracker) => withContext(enclosingDeclaration, flags, internalFlags, tracker, context => signatureToSignatureDeclarationHelper(signature, kind, context)), symbolToEntityName: (symbol: Symbol, meaning: SymbolFlags, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, internalFlags?: InternalNodeBuilderFlags, tracker?: SymbolTracker) => withContext(enclosingDeclaration, flags, internalFlags, tracker, context => symbolToName(symbol, context, meaning, /*expectsIdentifier*/ false)), symbolToExpression: (symbol: Symbol, meaning: SymbolFlags, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, internalFlags?: InternalNodeBuilderFlags, tracker?: SymbolTracker) => withContext(enclosingDeclaration, flags, internalFlags, tracker, context => symbolToExpression(symbol, context, meaning)), symbolToTypeParameterDeclarations: (symbol: Symbol, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, internalFlags?: InternalNodeBuilderFlags, tracker?: SymbolTracker) => withContext(enclosingDeclaration, flags, internalFlags, tracker, context => typeParametersToTypeParameterDeclarations(symbol, context)), symbolToParameterDeclaration: (symbol: Symbol, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, internalFlags?: InternalNodeBuilderFlags, tracker?: SymbolTracker) => withContext(enclosingDeclaration, flags, internalFlags, tracker, context => symbolToParameterDeclaration(symbol, context)), typeParameterToDeclaration: (parameter: TypeParameter, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, internalFlags?: InternalNodeBuilderFlags, tracker?: SymbolTracker) => withContext(enclosingDeclaration, flags, internalFlags, tracker, context => typeParameterToDeclaration(parameter, context)), symbolTableToDeclarationStatements: (symbolTable: SymbolTable, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, internalFlags?: InternalNodeBuilderFlags, tracker?: SymbolTracker) => withContext(enclosingDeclaration, flags, internalFlags, tracker, context => symbolTableToDeclarationStatements(symbolTable, context)), symbolToNode: (symbol: Symbol, meaning: SymbolFlags, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, internalFlags?: InternalNodeBuilderFlags, tracker?: SymbolTracker) => withContext(enclosingDeclaration, flags, internalFlags, tracker, context => symbolToNode(symbol, context, meaning)), }; function getTypeFromTypeNode(context: NodeBuilderContext, node: TypeNode, noMappedTypes?: false): Type; function getTypeFromTypeNode(context: NodeBuilderContext, node: TypeNode, noMappedTypes: true): Type | undefined; function getTypeFromTypeNode(context: NodeBuilderContext, node: TypeNode, noMappedTypes?: boolean): Type | undefined { const type = getTypeFromTypeNodeWithoutContext(node); if (!context.mapper) return type; const mappedType = instantiateType(type, context.mapper); return noMappedTypes && mappedType !== type ? undefined : mappedType; } /** * Unlike the utilities `setTextRange`, this checks if the `location` we're trying to set on `range` is within the * same file as the active context. If not, the range is not applied. This prevents us from copying ranges across files, * which will confuse the node printer (as it assumes all node ranges are within the current file). * Additionally, if `range` _isn't synthetic_, or isn't in the current file, it will _copy_ it to _remove_ its' position * information. * * It also calls `setOriginalNode` to setup a `.original` pointer, since you basically *always* want these in the node builder. */ function setTextRange(context: NodeBuilderContext, range: T, location: Node | undefined): T { if (!nodeIsSynthesized(range) || !(range.flags & NodeFlags.Synthesized) || !context.enclosingFile || context.enclosingFile !== getSourceFileOfNode(getOriginalNode(range))) { range = factory.cloneNode(range); // if `range` is synthesized or originates in another file, copy it so it definitely has synthetic positions } if (range === location) return range; if (!location) { return range; } // Don't overwrite the original node if `range` has an `original` node that points either directly or indirectly to `location` let original = range.original; while (original && original !== location) { original = original.original; } if (!original) { setOriginalNode(range, location); } // only set positions if range comes from the same file since copying text across files isn't supported by the emitter if (context.enclosingFile && context.enclosingFile === getSourceFileOfNode(getOriginalNode(location))) { return setTextRangeWorker(range, location); } return range; } /** * Same as expressionOrTypeToTypeNodeHelper, but also checks if the expression can be syntactically typed. */ function expressionOrTypeToTypeNode(context: NodeBuilderContext, expr: Expression | JsxAttributeValue | undefined, type: Type, addUndefined?: boolean) { const restoreFlags = saveRestoreFlags(context); if (expr && !(context.internalFlags & InternalNodeBuilderFlags.NoSyntacticPrinter)) { syntacticNodeBuilder.serializeTypeOfExpression(expr, context, addUndefined); } context.internalFlags |= InternalNodeBuilderFlags.NoSyntacticPrinter; const result = expressionOrTypeToTypeNodeHelper(context, expr, type, addUndefined); restoreFlags(); return result; } function expressionOrTypeToTypeNodeHelper(context: NodeBuilderContext, expr: Expression | JsxAttributeValue | undefined, type: Type, addUndefined?: boolean) { if (expr) { const typeNode = isAssertionExpression(expr) ? expr.type : isJSDocTypeAssertion(expr) ? getJSDocTypeAssertionType(expr) : undefined; if (typeNode && !isConstTypeReference(typeNode)) { const result = tryReuseExistingTypeNode(context, typeNode, type, expr.parent, addUndefined); if (result) { return result; } } } if (addUndefined) { type = getOptionalType(type); } return typeToTypeNodeHelper(type, context); } function tryReuseExistingTypeNode( context: NodeBuilderContext, typeNode: TypeNode, type: Type, host: Node, addUndefined?: boolean, ) { const originalType = type; if (addUndefined) { type = getOptionalType(type, !isParameter(host)); } const clone = tryReuseExistingNonParameterTypeNode(context, typeNode, type, host); if (clone) { // explicitly add `| undefined` if it's missing from the input type nodes and the type contains `undefined` (and not the missing type) if (addUndefined && containsNonMissingUndefinedType(type) && !someType(getTypeFromTypeNode(context, typeNode), t => !!(t.flags & TypeFlags.Undefined))) { return factory.createUnionTypeNode([clone, factory.createKeywordTypeNode(SyntaxKind.UndefinedKeyword)]); } return clone; } if (addUndefined && originalType !== type) { const cloneMissingUndefined = tryReuseExistingNonParameterTypeNode(context, typeNode, originalType, host); if (cloneMissingUndefined) { return factory.createUnionTypeNode([cloneMissingUndefined, factory.createKeywordTypeNode(SyntaxKind.UndefinedKeyword)]); } } return undefined; } function tryReuseExistingNonParameterTypeNode( context: NodeBuilderContext, existing: TypeNode, type: Type, host = context.enclosingDeclaration, annotationType = getTypeFromTypeNode(context, existing, /*noMappedTypes*/ true), ) { if (annotationType && typeNodeIsEquivalentToType(host, type, annotationType) && existingTypeNodeIsNotReferenceOrIsReferenceWithCompatibleTypeArgumentCount(existing, type)) { const result = tryReuseExistingTypeNodeHelper(context, existing); if (result) { return result; } } return undefined; } function symbolToNode(symbol: Symbol, context: NodeBuilderContext, meaning: SymbolFlags) { if (context.internalFlags & InternalNodeBuilderFlags.WriteComputedProps) { if (symbol.valueDeclaration) { const name = getNameOfDeclaration(symbol.valueDeclaration); if (name && isComputedPropertyName(name)) return name; } const nameType = getSymbolLinks(symbol).nameType; if (nameType && nameType.flags & (TypeFlags.EnumLiteral | TypeFlags.UniqueESSymbol)) { context.enclosingDeclaration = nameType.symbol.valueDeclaration; return factory.createComputedPropertyName(symbolToExpression(nameType.symbol, context, meaning)); } } return symbolToExpression(symbol, context, meaning); } function withContext(enclosingDeclaration: Node | undefined, flags: NodeBuilderFlags | undefined, internalFlags: InternalNodeBuilderFlags | undefined, tracker: SymbolTracker | undefined, cb: (context: NodeBuilderContext) => T): T | undefined { const moduleResolverHost = tracker?.trackSymbol ? tracker.moduleResolverHost : (internalFlags || InternalNodeBuilderFlags.None) & InternalNodeBuilderFlags.DoNotIncludeSymbolChain ? createBasicNodeBuilderModuleSpecifierResolutionHost(host) : undefined; const context: NodeBuilderContext = { enclosingDeclaration, enclosingFile: enclosingDeclaration && getSourceFileOfNode(enclosingDeclaration), flags: flags || NodeBuilderFlags.None, internalFlags: internalFlags || InternalNodeBuilderFlags.None, tracker: undefined!, encounteredError: false, reportedDiagnostic: false, visitedTypes: undefined, symbolDepth: undefined, inferTypeParameters: undefined, approximateLength: 0, trackedSymbols: undefined, bundled: !!compilerOptions.outFile && !!enclosingDeclaration && isExternalOrCommonJsModule(getSourceFileOfNode(enclosingDeclaration)), truncating: false, usedSymbolNames: undefined, remappedSymbolNames: undefined, remappedSymbolReferences: undefined, reverseMappedStack: undefined, mustCreateTypeParameterSymbolList: true, typeParameterSymbolList: undefined, mustCreateTypeParametersNamesLookups: true, typeParameterNames: undefined, typeParameterNamesByText: undefined, typeParameterNamesByTextNextNameCount: undefined, mapper: undefined, }; context.tracker = new SymbolTrackerImpl(context, tracker, moduleResolverHost); const resultingNode = cb(context); if (context.truncating && context.flags & NodeBuilderFlags.NoTruncation) { context.tracker.reportTruncationError(); } return context.encounteredError ? undefined : resultingNode; } function saveRestoreFlags(context: NodeBuilderContext) { const flags = context.flags; const internalFlags = context.internalFlags; return restore; function restore() { context.flags = flags; context.internalFlags = internalFlags; } } function checkTruncationLength(context: NodeBuilderContext): boolean { if (context.truncating) return context.truncating; return context.truncating = context.approximateLength > ((context.flags & NodeBuilderFlags.NoTruncation) ? noTruncationMaximumTruncationLength : defaultMaximumTruncationLength); } function typeToTypeNodeHelper(type: Type, context: NodeBuilderContext): TypeNode { const restoreFlags = saveRestoreFlags(context); const typeNode = typeToTypeNodeWorker(type, context); restoreFlags(); return typeNode; } function typeToTypeNodeWorker(type: Type, context: NodeBuilderContext): TypeNode { if (cancellationToken && cancellationToken.throwIfCancellationRequested) { cancellationToken.throwIfCancellationRequested(); } const inTypeAlias = context.flags & NodeBuilderFlags.InTypeAlias; context.flags &= ~NodeBuilderFlags.InTypeAlias; if (!type) { if (!(context.flags & NodeBuilderFlags.AllowEmptyUnionOrIntersection)) { context.encounteredError = true; return undefined!; // TODO: GH#18217 } context.approximateLength += 3; return factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); } if (!(context.flags & NodeBuilderFlags.NoTypeReduction)) { type = getReducedType(type); } if (type.flags & TypeFlags.Any) { if (type.aliasSymbol) { return factory.createTypeReferenceNode(symbolToEntityNameNode(type.aliasSymbol), mapToTypeNodes(type.aliasTypeArguments, context)); } if (type === unresolvedType) { return addSyntheticLeadingComment(factory.createKeywordTypeNode(SyntaxKind.AnyKeyword), SyntaxKind.MultiLineCommentTrivia, "unresolved"); } context.approximateLength += 3; return factory.createKeywordTypeNode(type === intrinsicMarkerType ? SyntaxKind.IntrinsicKeyword : SyntaxKind.AnyKeyword); } if (type.flags & TypeFlags.Unknown) { return factory.createKeywordTypeNode(SyntaxKind.UnknownKeyword); } if (type.flags & TypeFlags.String) { context.approximateLength += 6; return factory.createKeywordTypeNode(SyntaxKind.StringKeyword); } if (type.flags & TypeFlags.Number) { context.approximateLength += 6; return factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); } if (type.flags & TypeFlags.BigInt) { context.approximateLength += 6; return factory.createKeywordTypeNode(SyntaxKind.BigIntKeyword); } if (type.flags & TypeFlags.Boolean && !type.aliasSymbol) { context.approximateLength += 7; return factory.createKeywordTypeNode(SyntaxKind.BooleanKeyword); } if (type.flags & TypeFlags.EnumLike) { if (type.symbol.flags & SymbolFlags.EnumMember) { const parentSymbol = getParentOfSymbol(type.symbol)!; const parentName = symbolToTypeNode(parentSymbol, context, SymbolFlags.Type); if (getDeclaredTypeOfSymbol(parentSymbol) === type) { return parentName; } const memberName = symbolName(type.symbol); if (isIdentifierText(memberName, ScriptTarget.ES5)) { return appendReferenceToType( parentName as TypeReferenceNode | ImportTypeNode, factory.createTypeReferenceNode(memberName, /*typeArguments*/ undefined), ); } if (isImportTypeNode(parentName)) { (parentName as any).isTypeOf = true; // mutably update, node is freshly manufactured anyhow return factory.createIndexedAccessTypeNode(parentName, factory.createLiteralTypeNode(factory.createStringLiteral(memberName))); } else if (isTypeReferenceNode(parentName)) { return factory.createIndexedAccessTypeNode(factory.createTypeQueryNode(parentName.typeName), factory.createLiteralTypeNode(factory.createStringLiteral(memberName))); } else { return Debug.fail("Unhandled type node kind returned from `symbolToTypeNode`."); } } return symbolToTypeNode(type.symbol, context, SymbolFlags.Type); } if (type.flags & TypeFlags.StringLiteral) { context.approximateLength += (type as StringLiteralType).value.length + 2; return factory.createLiteralTypeNode(setEmitFlags(factory.createStringLiteral((type as StringLiteralType).value, !!(context.flags & NodeBuilderFlags.UseSingleQuotesForStringLiteralType)), EmitFlags.NoAsciiEscaping)); } if (type.flags & TypeFlags.NumberLiteral) { const value = (type as NumberLiteralType).value; context.approximateLength += ("" + value).length; return factory.createLiteralTypeNode(value < 0 ? factory.createPrefixUnaryExpression(SyntaxKind.MinusToken, factory.createNumericLiteral(-value)) : factory.createNumericLiteral(value)); } if (type.flags & TypeFlags.BigIntLiteral) { context.approximateLength += (pseudoBigIntToString((type as BigIntLiteralType).value).length) + 1; return factory.createLiteralTypeNode(factory.createBigIntLiteral((type as BigIntLiteralType).value)); } if (type.flags & TypeFlags.BooleanLiteral) { context.approximateLength += (type as IntrinsicType).intrinsicName.length; return factory.createLiteralTypeNode((type as IntrinsicType).intrinsicName === "true" ? factory.createTrue() : factory.createFalse()); } if (type.flags & TypeFlags.UniqueESSymbol) { if (!(context.flags & NodeBuilderFlags.AllowUniqueESSymbolType)) { if (isValueSymbolAccessible(type.symbol, context.enclosingDeclaration)) { context.approximateLength += 6; return symbolToTypeNode(type.symbol, context, SymbolFlags.Value); } if (context.tracker.reportInaccessibleUniqueSymbolError) { context.tracker.reportInaccessibleUniqueSymbolError(); } } context.approximateLength += 13; return factory.createTypeOperatorNode(SyntaxKind.UniqueKeyword, factory.createKeywordTypeNode(SyntaxKind.SymbolKeyword)); } if (type.flags & TypeFlags.Void) { context.approximateLength += 4; return factory.createKeywordTypeNode(SyntaxKind.VoidKeyword); } if (type.flags & TypeFlags.Undefined) { context.approximateLength += 9; return factory.createKeywordTypeNode(SyntaxKind.UndefinedKeyword); } if (type.flags & TypeFlags.Null) { context.approximateLength += 4; return factory.createLiteralTypeNode(factory.createNull()); } if (type.flags & TypeFlags.Never) { context.approximateLength += 5; return factory.createKeywordTypeNode(SyntaxKind.NeverKeyword); } if (type.flags & TypeFlags.ESSymbol) { context.approximateLength += 6; return factory.createKeywordTypeNode(SyntaxKind.SymbolKeyword); } if (type.flags & TypeFlags.NonPrimitive) { context.approximateLength += 6; return factory.createKeywordTypeNode(SyntaxKind.ObjectKeyword); } if (isThisTypeParameter(type)) { if (context.flags & NodeBuilderFlags.InObjectTypeLiteral) { if (!context.encounteredError && !(context.flags & NodeBuilderFlags.AllowThisInObjectLiteral)) { context.encounteredError = true; } context.tracker.reportInaccessibleThisError?.(); } context.approximateLength += 4; return factory.createThisTypeNode(); } if (!inTypeAlias && type.aliasSymbol && (context.flags & NodeBuilderFlags.UseAliasDefinedOutsideCurrentScope || isTypeSymbolAccessible(type.aliasSymbol, context.enclosingDeclaration))) { const typeArgumentNodes = mapToTypeNodes(type.aliasTypeArguments, context); if (isReservedMemberName(type.aliasSymbol.escapedName) && !(type.aliasSymbol.flags & SymbolFlags.Class)) return factory.createTypeReferenceNode(factory.createIdentifier(""), typeArgumentNodes); if (length(typeArgumentNodes) === 1 && type.aliasSymbol === globalArrayType.symbol) { return factory.createArrayTypeNode(typeArgumentNodes![0]); } return symbolToTypeNode(type.aliasSymbol, context, SymbolFlags.Type, typeArgumentNodes); } const objectFlags = getObjectFlags(type); if (objectFlags & ObjectFlags.Reference) { Debug.assert(!!(type.flags & TypeFlags.Object)); return (type as TypeReference).node ? visitAndTransformType(type as TypeReference, typeReferenceToTypeNode) : typeReferenceToTypeNode(type as TypeReference); } if (type.flags & TypeFlags.TypeParameter || objectFlags & ObjectFlags.ClassOrInterface) { if (type.flags & TypeFlags.TypeParameter && contains(context.inferTypeParameters, type)) { context.approximateLength += symbolName(type.symbol).length + 6; let constraintNode: TypeNode | undefined; const constraint = getConstraintOfTypeParameter(type as TypeParameter); if (constraint) { // If the infer type has a constraint that is not the same as the constraint // we would have normally inferred based on context, we emit the constraint // using `infer T extends ?`. We omit inferred constraints from type references // as they may be elided. const inferredConstraint = getInferredTypeParameterConstraint(type as TypeParameter, /*omitTypeReferences*/ true); if (!(inferredConstraint && isTypeIdenticalTo(constraint, inferredConstraint))) { context.approximateLength += 9; constraintNode = constraint && typeToTypeNodeHelper(constraint, context); } } return factory.createInferTypeNode(typeParameterToDeclarationWithConstraint(type as TypeParameter, context, constraintNode)); } if ( context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams && type.flags & TypeFlags.TypeParameter ) { const name = typeParameterToName(type, context); context.approximateLength += idText(name).length; return factory.createTypeReferenceNode(factory.createIdentifier(idText(name)), /*typeArguments*/ undefined); } // Ignore constraint/default when creating a usage (as opposed to declaration) of a type parameter. if (type.symbol) { return symbolToTypeNode(type.symbol, context, SymbolFlags.Type); } const name = (type === markerSuperTypeForCheck || type === markerSubTypeForCheck) && varianceTypeParameter && varianceTypeParameter.symbol ? (type === markerSubTypeForCheck ? "sub-" : "super-") + symbolName(varianceTypeParameter.symbol) : "?"; return factory.createTypeReferenceNode(factory.createIdentifier(name), /*typeArguments*/ undefined); } if (type.flags & TypeFlags.Union && (type as UnionType).origin) { type = (type as UnionType).origin!; } if (type.flags & (TypeFlags.Union | TypeFlags.Intersection)) { const types = type.flags & TypeFlags.Union ? formatUnionTypes((type as UnionType).types) : (type as IntersectionType).types; if (length(types) === 1) { return typeToTypeNodeHelper(types[0], context); } const typeNodes = mapToTypeNodes(types, context, /*isBareList*/ true); if (typeNodes && typeNodes.length > 0) { return type.flags & TypeFlags.Union ? factory.createUnionTypeNode(typeNodes) : factory.createIntersectionTypeNode(typeNodes); } else { if (!context.encounteredError && !(context.flags & NodeBuilderFlags.AllowEmptyUnionOrIntersection)) { context.encounteredError = true; } return undefined!; // TODO: GH#18217 } } if (objectFlags & (ObjectFlags.Anonymous | ObjectFlags.Mapped)) { Debug.assert(!!(type.flags & TypeFlags.Object)); // The type is an object literal type. return createAnonymousTypeNode(type as ObjectType); } if (type.flags & TypeFlags.Index) { const indexedType = (type as IndexType).type; context.approximateLength += 6; const indexTypeNode = typeToTypeNodeHelper(indexedType, context); return factory.createTypeOperatorNode(SyntaxKind.KeyOfKeyword, indexTypeNode); } if (type.flags & TypeFlags.TemplateLiteral) { const texts = (type as TemplateLiteralType).texts; const types = (type as TemplateLiteralType).types; const templateHead = factory.createTemplateHead(texts[0]); const templateSpans = factory.createNodeArray( map(types, (t, i) => factory.createTemplateLiteralTypeSpan( typeToTypeNodeHelper(t, context), (i < types.length - 1 ? factory.createTemplateMiddle : factory.createTemplateTail)(texts[i + 1]), )), ); context.approximateLength += 2; return factory.createTemplateLiteralType(templateHead, templateSpans); } if (type.flags & TypeFlags.StringMapping) { const typeNode = typeToTypeNodeHelper((type as StringMappingType).type, context); return symbolToTypeNode((type as StringMappingType).symbol, context, SymbolFlags.Type, [typeNode]); } if (type.flags & TypeFlags.IndexedAccess) { const objectTypeNode = typeToTypeNodeHelper((type as IndexedAccessType).objectType, context); const indexTypeNode = typeToTypeNodeHelper((type as IndexedAccessType).indexType, context); context.approximateLength += 2; return factory.createIndexedAccessTypeNode(objectTypeNode, indexTypeNode); } if (type.flags & TypeFlags.Conditional) { return visitAndTransformType(type, type => conditionalTypeToTypeNode(type as ConditionalType)); } if (type.flags & TypeFlags.Substitution) { const typeNode = typeToTypeNodeHelper((type as SubstitutionType).baseType, context); const noInferSymbol = isNoInferType(type) && getGlobalTypeSymbol("NoInfer" as __String, /*reportErrors*/ false); return noInferSymbol ? symbolToTypeNode(noInferSymbol, context, SymbolFlags.Type, [typeNode]) : typeNode; } return Debug.fail("Should be unreachable."); function conditionalTypeToTypeNode(type: ConditionalType) { const checkTypeNode = typeToTypeNodeHelper(type.checkType, context); context.approximateLength += 15; if (context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams && type.root.isDistributive && !(type.checkType.flags & TypeFlags.TypeParameter)) { const newParam = createTypeParameter(createSymbol(SymbolFlags.TypeParameter, "T" as __String)); const name = typeParameterToName(newParam, context); const newTypeVariable = factory.createTypeReferenceNode(name); context.approximateLength += 37; // 15 each for two added conditionals, 7 for an added infer type const newMapper = prependTypeMapping(type.root.checkType, newParam, type.mapper); const saveInferTypeParameters = context.inferTypeParameters; context.inferTypeParameters = type.root.inferTypeParameters; const extendsTypeNode = typeToTypeNodeHelper(instantiateType(type.root.extendsType, newMapper), context); context.inferTypeParameters = saveInferTypeParameters; const trueTypeNode = typeToTypeNodeOrCircularityElision(instantiateType(getTypeFromTypeNode(context, type.root.node.trueType), newMapper)); const falseTypeNode = typeToTypeNodeOrCircularityElision(instantiateType(getTypeFromTypeNode(context, type.root.node.falseType), newMapper)); // outermost conditional makes `T` a type parameter, allowing the inner conditionals to be distributive // second conditional makes `T` have `T & checkType` substitution, so it is correctly usable as the checkType // inner conditional runs the check the user provided on the check type (distributively) and returns the result // checkType extends infer T ? T extends checkType ? T extends extendsType ? trueType : falseType : never : never; // this is potentially simplifiable to // checkType extends infer T ? T extends checkType & extendsType ? trueType : falseType : never; // but that may confuse users who read the output more. // On the other hand, // checkType extends infer T extends checkType ? T extends extendsType ? trueType : falseType : never; // may also work with `infer ... extends ...` in, but would produce declarations only compatible with the latest TS. return factory.createConditionalTypeNode( checkTypeNode, factory.createInferTypeNode(factory.createTypeParameterDeclaration(/*modifiers*/ undefined, factory.cloneNode(newTypeVariable.typeName) as Identifier)), factory.createConditionalTypeNode( factory.createTypeReferenceNode(factory.cloneNode(name)), typeToTypeNodeHelper(type.checkType, context), factory.createConditionalTypeNode(newTypeVariable, extendsTypeNode, trueTypeNode, falseTypeNode), factory.createKeywordTypeNode(SyntaxKind.NeverKeyword), ), factory.createKeywordTypeNode(SyntaxKind.NeverKeyword), ); } const saveInferTypeParameters = context.inferTypeParameters; context.inferTypeParameters = type.root.inferTypeParameters; const extendsTypeNode = typeToTypeNodeHelper(type.extendsType, context); context.inferTypeParameters = saveInferTypeParameters; const trueTypeNode = typeToTypeNodeOrCircularityElision(getTrueTypeFromConditionalType(type)); const falseTypeNode = typeToTypeNodeOrCircularityElision(getFalseTypeFromConditionalType(type)); return factory.createConditionalTypeNode(checkTypeNode, extendsTypeNode, trueTypeNode, falseTypeNode); } function typeToTypeNodeOrCircularityElision(type: Type) { if (type.flags & TypeFlags.Union) { if (context.visitedTypes?.has(getTypeId(type))) { if (!(context.flags & NodeBuilderFlags.AllowAnonymousIdentifier)) { context.encounteredError = true; context.tracker?.reportCyclicStructureError?.(); } return createElidedInformationPlaceholder(context); } return visitAndTransformType(type, type => typeToTypeNodeHelper(type, context)); } return typeToTypeNodeHelper(type, context); } function isMappedTypeHomomorphic(type: MappedType) { return !!getHomomorphicTypeVariable(type); } function isHomomorphicMappedTypeWithNonHomomorphicInstantiation(type: MappedType) { return !!type.target && isMappedTypeHomomorphic(type.target as MappedType) && !isMappedTypeHomomorphic(type); } function createMappedTypeNodeFromType(type: MappedType) { Debug.assert(!!(type.flags & TypeFlags.Object)); const readonlyToken = type.declaration.readonlyToken ? factory.createToken(type.declaration.readonlyToken.kind) as ReadonlyKeyword | PlusToken | MinusToken : undefined; const questionToken = type.declaration.questionToken ? factory.createToken(type.declaration.questionToken.kind) as QuestionToken | PlusToken | MinusToken : undefined; let appropriateConstraintTypeNode: TypeNode; let newTypeVariable: TypeReferenceNode | undefined; // If the mapped type isn't `keyof` constraint-declared, _but_ still has modifiers preserved, and its naive instantiation won't preserve modifiers because its constraint isn't `keyof` constrained, we have work to do const needsModifierPreservingWrapper = !isMappedTypeWithKeyofConstraintDeclaration(type) && !(getModifiersTypeFromMappedType(type).flags & TypeFlags.Unknown) && context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams && !(getConstraintTypeFromMappedType(type).flags & TypeFlags.TypeParameter && getConstraintOfTypeParameter(getConstraintTypeFromMappedType(type))?.flags! & TypeFlags.Index); if (isMappedTypeWithKeyofConstraintDeclaration(type)) { // We have a { [P in keyof T]: X } // We do this to ensure we retain the toplevel keyof-ness of the type which may be lost due to keyof distribution during `getConstraintTypeFromMappedType` if (isHomomorphicMappedTypeWithNonHomomorphicInstantiation(type) && context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams) { const newParam = createTypeParameter(createSymbol(SymbolFlags.TypeParameter, "T" as __String)); const name = typeParameterToName(newParam, context); newTypeVariable = factory.createTypeReferenceNode(name); } appropriateConstraintTypeNode = factory.createTypeOperatorNode(SyntaxKind.KeyOfKeyword, newTypeVariable || typeToTypeNodeHelper(getModifiersTypeFromMappedType(type), context)); } else if (needsModifierPreservingWrapper) { // So, step 1: new type variable const newParam = createTypeParameter(createSymbol(SymbolFlags.TypeParameter, "T" as __String)); const name = typeParameterToName(newParam, context); newTypeVariable = factory.createTypeReferenceNode(name); // step 2: make that new type variable itself the constraint node, making the mapped type `{[K in T_1]: Template}` appropriateConstraintTypeNode = newTypeVariable; } else { appropriateConstraintTypeNode = typeToTypeNodeHelper(getConstraintTypeFromMappedType(type), context); } const typeParameterNode = typeParameterToDeclarationWithConstraint(getTypeParameterFromMappedType(type), context, appropriateConstraintTypeNode); const nameTypeNode = type.declaration.nameType ? typeToTypeNodeHelper(getNameTypeFromMappedType(type)!, context) : undefined; const templateTypeNode = typeToTypeNodeHelper(removeMissingType(getTemplateTypeFromMappedType(type), !!(getMappedTypeModifiers(type) & MappedTypeModifiers.IncludeOptional)), context); const mappedTypeNode = factory.createMappedTypeNode(readonlyToken, typeParameterNode, nameTypeNode, questionToken, templateTypeNode, /*members*/ undefined); context.approximateLength += 10; const result = setEmitFlags(mappedTypeNode, EmitFlags.SingleLine); if (isHomomorphicMappedTypeWithNonHomomorphicInstantiation(type) && context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams) { // homomorphic mapped type with a non-homomorphic naive inlining // wrap it with a conditional like `SomeModifiersType extends infer U ? {..the mapped type...} : never` to ensure the resulting // type stays homomorphic const originalConstraint = instantiateType(getConstraintOfTypeParameter(getTypeFromTypeNode(context, (type.declaration.typeParameter.constraint! as TypeOperatorNode).type) as TypeParameter) || unknownType, type.mapper); return factory.createConditionalTypeNode( typeToTypeNodeHelper(getModifiersTypeFromMappedType(type), context), factory.createInferTypeNode(factory.createTypeParameterDeclaration(/*modifiers*/ undefined, factory.cloneNode(newTypeVariable!.typeName) as Identifier, originalConstraint.flags & TypeFlags.Unknown ? undefined : typeToTypeNodeHelper(originalConstraint, context))), result, factory.createKeywordTypeNode(SyntaxKind.NeverKeyword), ); } else if (needsModifierPreservingWrapper) { // and step 3: once the mapped type is reconstructed, create a `ConstraintType extends infer T_1 extends keyof ModifiersType ? {[K in T_1]: Template} : never` // subtly different from the `keyof` constraint case, by including the `keyof` constraint on the `infer` type parameter, it doesn't rely on the constraint type being itself // constrained to a `keyof` type to preserve its modifier-preserving behavior. This is all basically because we preserve modifiers for a wider set of mapped types than // just homomorphic ones. return factory.createConditionalTypeNode( typeToTypeNodeHelper(getConstraintTypeFromMappedType(type), context), factory.createInferTypeNode(factory.createTypeParameterDeclaration(/*modifiers*/ undefined, factory.cloneNode(newTypeVariable!.typeName) as Identifier, factory.createTypeOperatorNode(SyntaxKind.KeyOfKeyword, typeToTypeNodeHelper(getModifiersTypeFromMappedType(type), context)))), result, factory.createKeywordTypeNode(SyntaxKind.NeverKeyword), ); } return result; } function createAnonymousTypeNode(type: ObjectType): TypeNode { const typeId = type.id; const symbol = type.symbol; if (symbol) { const isInstantiationExpressionType = !!(getObjectFlags(type) & ObjectFlags.InstantiationExpressionType); if (isInstantiationExpressionType) { const instantiationExpressionType = type as InstantiationExpressionType; const existing = instantiationExpressionType.node; if (isTypeQueryNode(existing)) { const typeNode = tryReuseExistingNonParameterTypeNode(context, existing, type); if (typeNode) { return typeNode; } } if (context.visitedTypes?.has(typeId)) { return createElidedInformationPlaceholder(context); } return visitAndTransformType(type, createTypeNodeFromObjectType); } const isInstanceType = isClassInstanceSide(type) ? SymbolFlags.Type : SymbolFlags.Value; if (isJSConstructor(symbol.valueDeclaration)) { // Instance and static types share the same symbol; only add 'typeof' for the static side. return symbolToTypeNode(symbol, context, isInstanceType); } // Always use 'typeof T' for type of class, enum, and module objects else if ( symbol.flags & SymbolFlags.Class && !getBaseTypeVariableOfClass(symbol) && !(symbol.valueDeclaration && isClassLike(symbol.valueDeclaration) && context.flags & NodeBuilderFlags.WriteClassExpressionAsTypeLiteral && (!isClassDeclaration(symbol.valueDeclaration) || isSymbolAccessible(symbol, context.enclosingDeclaration, isInstanceType, /*shouldComputeAliasesToMakeVisible*/ false).accessibility !== SymbolAccessibility.Accessible)) || symbol.flags & (SymbolFlags.Enum | SymbolFlags.ValueModule) || shouldWriteTypeOfFunctionSymbol() ) { return symbolToTypeNode(symbol, context, isInstanceType); } else if (context.visitedTypes?.has(typeId)) { // If type is an anonymous type literal in a type alias declaration, use type alias name const typeAlias = getTypeAliasForTypeLiteral(type); if (typeAlias) { // The specified symbol flags need to be reinterpreted as type flags return symbolToTypeNode(typeAlias, context, SymbolFlags.Type); } else { return createElidedInformationPlaceholder(context); } } else { return visitAndTransformType(type, createTypeNodeFromObjectType); } } else { // Anonymous types without a symbol are never circular. return createTypeNodeFromObjectType(type); } function shouldWriteTypeOfFunctionSymbol() { const isStaticMethodSymbol = !!(symbol.flags & SymbolFlags.Method) && // typeof static method some(symbol.declarations, declaration => isStatic(declaration)); const isNonLocalFunctionSymbol = !!(symbol.flags & SymbolFlags.Function) && (symbol.parent || // is exported function symbol forEach(symbol.declarations, declaration => declaration.parent.kind === SyntaxKind.SourceFile || declaration.parent.kind === SyntaxKind.ModuleBlock)); if (isStaticMethodSymbol || isNonLocalFunctionSymbol) { // typeof is allowed only for static/non local functions return (!!(context.flags & NodeBuilderFlags.UseTypeOfFunction) || (context.visitedTypes?.has(typeId))) && // it is type of the symbol uses itself recursively (!(context.flags & NodeBuilderFlags.UseStructuralFallback) || isValueSymbolAccessible(symbol, context.enclosingDeclaration)); // And the build is going to succeed without visibility error or there is no structural fallback allowed } } } function visitAndTransformType(type: T, transform: (type: T) => TypeNode) { const typeId = type.id; const isConstructorObject = getObjectFlags(type) & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & SymbolFlags.Class; const id = getObjectFlags(type) & ObjectFlags.Reference && (type as TypeReference & T).node ? "N" + getNodeId((type as TypeReference & T).node!) : type.flags & TypeFlags.Conditional ? "N" + getNodeId((type as ConditionalType & T).root.node) : type.symbol ? (isConstructorObject ? "+" : "") + getSymbolId(type.symbol) : undefined; // Since instantiations of the same anonymous type have the same symbol, tracking symbols instead // of types allows us to catch circular references to instantiations of the same anonymous type if (!context.visitedTypes) { context.visitedTypes = new Set(); } if (id && !context.symbolDepth) { context.symbolDepth = new Map(); } const links = context.enclosingDeclaration && getNodeLinks(context.enclosingDeclaration); const key = `${getTypeId(type)}|${context.flags}|${context.internalFlags}`; if (links) { links.serializedTypes ||= new Map(); } const cachedResult = links?.serializedTypes?.get(key); if (cachedResult) { // TODO:: check if we instead store late painted statements associated with this? cachedResult.trackedSymbols?.forEach( ([symbol, enclosingDeclaration, meaning]) => context.tracker.trackSymbol( symbol, enclosingDeclaration, meaning, ), ); if (cachedResult.truncating) { context.truncating = true; } context.approximateLength += cachedResult.addedLength; return deepCloneOrReuseNode(cachedResult.node); } let depth: number | undefined; if (id) { depth = context.symbolDepth!.get(id) || 0; if (depth > 10) { return createElidedInformationPlaceholder(context); } context.symbolDepth!.set(id, depth + 1); } context.visitedTypes.add(typeId); const prevTrackedSymbols = context.trackedSymbols; context.trackedSymbols = undefined; const startLength = context.approximateLength; const result = transform(type); const addedLength = context.approximateLength - startLength; if (!context.reportedDiagnostic && !context.encounteredError) { links?.serializedTypes?.set(key, { node: result, truncating: context.truncating, addedLength, trackedSymbols: context.trackedSymbols, }); } context.visitedTypes.delete(typeId); if (id) { context.symbolDepth!.set(id, depth!); } context.trackedSymbols = prevTrackedSymbols; return result; function deepCloneOrReuseNode(node: T): T { if (!nodeIsSynthesized(node) && getParseTreeNode(node) === node) { return node; } return setTextRange(context, factory.cloneNode(visitEachChildWorker(node, deepCloneOrReuseNode, /*context*/ undefined, deepCloneOrReuseNodes, deepCloneOrReuseNode)), node); } function deepCloneOrReuseNodes( nodes: NodeArray | undefined, visitor: Visitor, test?: (node: Node) => boolean, start?: number, count?: number, ): NodeArray | undefined { if (nodes && nodes.length === 0) { // Ensure we explicitly make a copy of an empty array; visitNodes will not do this unless the array has elements, // which can lead to us reusing the same empty NodeArray more than once within the same AST during type noding. return setTextRangeWorker(factory.createNodeArray(/*elements*/ undefined, nodes.hasTrailingComma), nodes); } return visitNodes(nodes, visitor, test, start, count); } } function createTypeNodeFromObjectType(type: ObjectType): TypeNode { if (isGenericMappedType(type) || (type as MappedType).containsError) { return createMappedTypeNodeFromType(type as MappedType); } const resolved = resolveStructuredTypeMembers(type); if (!resolved.properties.length && !resolved.indexInfos.length) { if (!resolved.callSignatures.length && !resolved.constructSignatures.length) { context.approximateLength += 2; return setEmitFlags(factory.createTypeLiteralNode(/*members*/ undefined), EmitFlags.SingleLine); } if (resolved.callSignatures.length === 1 && !resolved.constructSignatures.length) { const signature = resolved.callSignatures[0]; const signatureNode = signatureToSignatureDeclarationHelper(signature, SyntaxKind.FunctionType, context) as FunctionTypeNode; return signatureNode; } if (resolved.constructSignatures.length === 1 && !resolved.callSignatures.length) { const signature = resolved.constructSignatures[0]; const signatureNode = signatureToSignatureDeclarationHelper(signature, SyntaxKind.ConstructorType, context) as ConstructorTypeNode; return signatureNode; } } const abstractSignatures = filter(resolved.constructSignatures, signature => !!(signature.flags & SignatureFlags.Abstract)); if (some(abstractSignatures)) { const types = map(abstractSignatures, s => getOrCreateTypeFromSignature(s)); // count the number of type elements excluding abstract constructors const typeElementCount = resolved.callSignatures.length + (resolved.constructSignatures.length - abstractSignatures.length) + resolved.indexInfos.length + // exclude `prototype` when writing a class expression as a type literal, as per // the logic in `createTypeNodesFromResolvedType`. (context.flags & NodeBuilderFlags.WriteClassExpressionAsTypeLiteral ? countWhere(resolved.properties, p => !(p.flags & SymbolFlags.Prototype)) : length(resolved.properties)); // don't include an empty object literal if there were no other static-side // properties to write, i.e. `abstract class C { }` becomes `abstract new () => {}` // and not `(abstract new () => {}) & {}` if (typeElementCount) { // create a copy of the object type without any abstract construct signatures. types.push(getResolvedTypeWithoutAbstractConstructSignatures(resolved)); } return typeToTypeNodeHelper(getIntersectionType(types), context); } const restoreFlags = saveRestoreFlags(context); context.flags |= NodeBuilderFlags.InObjectTypeLiteral; const members = createTypeNodesFromResolvedType(resolved); restoreFlags(); const typeLiteralNode = factory.createTypeLiteralNode(members); context.approximateLength += 2; setEmitFlags(typeLiteralNode, (context.flags & NodeBuilderFlags.MultilineObjectLiterals) ? 0 : EmitFlags.SingleLine); return typeLiteralNode; } function typeReferenceToTypeNode(type: TypeReference) { let typeArguments: readonly Type[] = getTypeArguments(type); if (type.target === globalArrayType || type.target === globalReadonlyArrayType) { if (context.flags & NodeBuilderFlags.WriteArrayAsGenericType) { const typeArgumentNode = typeToTypeNodeHelper(typeArguments[0], context); return factory.createTypeReferenceNode(type.target === globalArrayType ? "Array" : "ReadonlyArray", [typeArgumentNode]); } const elementType = typeToTypeNodeHelper(typeArguments[0], context); const arrayType = factory.createArrayTypeNode(elementType); return type.target === globalArrayType ? arrayType : factory.createTypeOperatorNode(SyntaxKind.ReadonlyKeyword, arrayType); } else if (type.target.objectFlags & ObjectFlags.Tuple) { typeArguments = sameMap(typeArguments, (t, i) => removeMissingType(t, !!((type.target as TupleType).elementFlags[i] & ElementFlags.Optional))); if (typeArguments.length > 0) { const arity = getTypeReferenceArity(type); const tupleConstituentNodes = mapToTypeNodes(typeArguments.slice(0, arity), context); if (tupleConstituentNodes) { const { labeledElementDeclarations } = type.target as TupleType; for (let i = 0; i < tupleConstituentNodes.length; i++) { const flags = (type.target as TupleType).elementFlags[i]; const labeledElementDeclaration = labeledElementDeclarations?.[i]; if (labeledElementDeclaration) { tupleConstituentNodes[i] = factory.createNamedTupleMember( flags & ElementFlags.Variable ? factory.createToken(SyntaxKind.DotDotDotToken) : undefined, factory.createIdentifier(unescapeLeadingUnderscores(getTupleElementLabel(labeledElementDeclaration))), flags & ElementFlags.Optional ? factory.createToken(SyntaxKind.QuestionToken) : undefined, flags & ElementFlags.Rest ? factory.createArrayTypeNode(tupleConstituentNodes[i]) : tupleConstituentNodes[i], ); } else { tupleConstituentNodes[i] = flags & ElementFlags.Variable ? factory.createRestTypeNode(flags & ElementFlags.Rest ? factory.createArrayTypeNode(tupleConstituentNodes[i]) : tupleConstituentNodes[i]) : flags & ElementFlags.Optional ? factory.createOptionalTypeNode(tupleConstituentNodes[i]) : tupleConstituentNodes[i]; } } const tupleTypeNode = setEmitFlags(factory.createTupleTypeNode(tupleConstituentNodes), EmitFlags.SingleLine); return (type.target as TupleType).readonly ? factory.createTypeOperatorNode(SyntaxKind.ReadonlyKeyword, tupleTypeNode) : tupleTypeNode; } } if (context.encounteredError || (context.flags & NodeBuilderFlags.AllowEmptyTuple)) { const tupleTypeNode = setEmitFlags(factory.createTupleTypeNode([]), EmitFlags.SingleLine); return (type.target as TupleType).readonly ? factory.createTypeOperatorNode(SyntaxKind.ReadonlyKeyword, tupleTypeNode) : tupleTypeNode; } context.encounteredError = true; return undefined!; // TODO: GH#18217 } else if ( context.flags & NodeBuilderFlags.WriteClassExpressionAsTypeLiteral && type.symbol.valueDeclaration && isClassLike(type.symbol.valueDeclaration) && !isValueSymbolAccessible(type.symbol, context.enclosingDeclaration) ) { return createAnonymousTypeNode(type); } else { const outerTypeParameters = type.target.outerTypeParameters; let i = 0; let resultType: TypeReferenceNode | ImportTypeNode | undefined; if (outerTypeParameters) { const length = outerTypeParameters.length; while (i < length) { // Find group of type arguments for type parameters with the same declaring container. const start = i; const parent = getParentSymbolOfTypeParameter(outerTypeParameters[i])!; do { i++; } while (i < length && getParentSymbolOfTypeParameter(outerTypeParameters[i]) === parent); // When type parameters are their own type arguments for the whole group (i.e. we have // the default outer type arguments), we don't show the group. if (!rangeEquals(outerTypeParameters, typeArguments, start, i)) { const typeArgumentSlice = mapToTypeNodes(typeArguments.slice(start, i), context); const restoreFlags = saveRestoreFlags(context); context.flags |= NodeBuilderFlags.ForbidIndexedAccessSymbolReferences; const ref = symbolToTypeNode(parent, context, SymbolFlags.Type, typeArgumentSlice) as TypeReferenceNode | ImportTypeNode; restoreFlags(); resultType = !resultType ? ref : appendReferenceToType(resultType, ref as TypeReferenceNode); } } } let typeArgumentNodes: readonly TypeNode[] | undefined; if (typeArguments.length > 0) { let typeParameterCount = 0; if (type.target.typeParameters) { typeParameterCount = Math.min(type.target.typeParameters.length, typeArguments.length); // Maybe we should do this for more types, but for now we only elide type arguments that are // identical to their associated type parameters' defaults for `Iterable`, `IterableIterator`, // `AsyncIterable`, and `AsyncIterableIterator` to provide backwards-compatible .d.ts emit due // to each now having three type parameters instead of only one. if ( isReferenceToType(type, getGlobalIterableType(/*reportErrors*/ false)) || isReferenceToType(type, getGlobalIterableIteratorType(/*reportErrors*/ false)) || isReferenceToType(type, getGlobalAsyncIterableType(/*reportErrors*/ false)) || isReferenceToType(type, getGlobalAsyncIterableIteratorType(/*reportErrors*/ false)) ) { if ( !type.node || !isTypeReferenceNode(type.node) || !type.node.typeArguments || type.node.typeArguments.length < typeParameterCount ) { while (typeParameterCount > 0) { const typeArgument = typeArguments[typeParameterCount - 1]; const typeParameter = type.target.typeParameters[typeParameterCount - 1]; const defaultType = getDefaultFromTypeParameter(typeParameter); if (!defaultType || !isTypeIdenticalTo(typeArgument, defaultType)) { break; } typeParameterCount--; } } } } typeArgumentNodes = mapToTypeNodes(typeArguments.slice(i, typeParameterCount), context); } const restoreFlags = saveRestoreFlags(context); context.flags |= NodeBuilderFlags.ForbidIndexedAccessSymbolReferences; const finalRef = symbolToTypeNode(type.symbol, context, SymbolFlags.Type, typeArgumentNodes); restoreFlags(); return !resultType ? finalRef : appendReferenceToType(resultType, finalRef as TypeReferenceNode); } } function appendReferenceToType(root: TypeReferenceNode | ImportTypeNode, ref: TypeReferenceNode): TypeReferenceNode | ImportTypeNode { if (isImportTypeNode(root)) { // first shift type arguments let typeArguments = root.typeArguments; let qualifier = root.qualifier; if (qualifier) { if (isIdentifier(qualifier)) { if (typeArguments !== getIdentifierTypeArguments(qualifier)) { qualifier = setIdentifierTypeArguments(factory.cloneNode(qualifier), typeArguments); } } else { if (typeArguments !== getIdentifierTypeArguments(qualifier.right)) { qualifier = factory.updateQualifiedName(qualifier, qualifier.left, setIdentifierTypeArguments(factory.cloneNode(qualifier.right), typeArguments)); } } } typeArguments = ref.typeArguments; // then move qualifiers const ids = getAccessStack(ref); for (const id of ids) { qualifier = qualifier ? factory.createQualifiedName(qualifier, id) : id; } return factory.updateImportTypeNode( root, root.argument, root.attributes, qualifier, typeArguments, root.isTypeOf, ); } else { // first shift type arguments let typeArguments = root.typeArguments; let typeName = root.typeName; if (isIdentifier(typeName)) { if (typeArguments !== getIdentifierTypeArguments(typeName)) { typeName = setIdentifierTypeArguments(factory.cloneNode(typeName), typeArguments); } } else { if (typeArguments !== getIdentifierTypeArguments(typeName.right)) { typeName = factory.updateQualifiedName(typeName, typeName.left, setIdentifierTypeArguments(factory.cloneNode(typeName.right), typeArguments)); } } typeArguments = ref.typeArguments; // then move qualifiers const ids = getAccessStack(ref); for (const id of ids) { typeName = factory.createQualifiedName(typeName, id); } return factory.updateTypeReferenceNode( root, typeName, typeArguments, ); } } function getAccessStack(ref: TypeReferenceNode): Identifier[] { let state = ref.typeName; const ids = []; while (!isIdentifier(state)) { ids.unshift(state.right); state = state.left; } ids.unshift(state); return ids; } function createTypeNodesFromResolvedType(resolvedType: ResolvedType): TypeElement[] | undefined { if (checkTruncationLength(context)) { if (context.flags & NodeBuilderFlags.NoTruncation) { return [addSyntheticTrailingComment(factory.createNotEmittedTypeElement(), SyntaxKind.MultiLineCommentTrivia, "elided")]; } return [factory.createPropertySignature(/*modifiers*/ undefined, "...", /*questionToken*/ undefined, /*type*/ undefined)]; } const typeElements: TypeElement[] = []; for (const signature of resolvedType.callSignatures) { typeElements.push(signatureToSignatureDeclarationHelper(signature, SyntaxKind.CallSignature, context) as CallSignatureDeclaration); } for (const signature of resolvedType.constructSignatures) { if (signature.flags & SignatureFlags.Abstract) continue; typeElements.push(signatureToSignatureDeclarationHelper(signature, SyntaxKind.ConstructSignature, context) as ConstructSignatureDeclaration); } for (const info of resolvedType.indexInfos) { typeElements.push(indexInfoToIndexSignatureDeclarationHelper(info, context, resolvedType.objectFlags & ObjectFlags.ReverseMapped ? createElidedInformationPlaceholder(context) : undefined)); } const properties = resolvedType.properties; if (!properties) { return typeElements; } let i = 0; for (const propertySymbol of properties) { i++; if (context.flags & NodeBuilderFlags.WriteClassExpressionAsTypeLiteral) { if (propertySymbol.flags & SymbolFlags.Prototype) { continue; } if (getDeclarationModifierFlagsFromSymbol(propertySymbol) & (ModifierFlags.Private | ModifierFlags.Protected) && context.tracker.reportPrivateInBaseOfClassExpression) { context.tracker.reportPrivateInBaseOfClassExpression(unescapeLeadingUnderscores(propertySymbol.escapedName)); } } if (checkTruncationLength(context) && (i + 2 < properties.length - 1)) { if (context.flags & NodeBuilderFlags.NoTruncation) { const typeElement = typeElements.pop()!; typeElements.push(addSyntheticTrailingComment(typeElement, SyntaxKind.MultiLineCommentTrivia, `... ${properties.length - i} more elided ...`)); } else { typeElements.push(factory.createPropertySignature(/*modifiers*/ undefined, `... ${properties.length - i} more ...`, /*questionToken*/ undefined, /*type*/ undefined)); } addPropertyToElementList(properties[properties.length - 1], context, typeElements); break; } addPropertyToElementList(propertySymbol, context, typeElements); } return typeElements.length ? typeElements : undefined; } } function createElidedInformationPlaceholder(context: NodeBuilderContext) { context.approximateLength += 3; if (!(context.flags & NodeBuilderFlags.NoTruncation)) { return factory.createTypeReferenceNode(factory.createIdentifier("..."), /*typeArguments*/ undefined); } return addSyntheticLeadingComment(factory.createKeywordTypeNode(SyntaxKind.AnyKeyword), SyntaxKind.MultiLineCommentTrivia, "elided"); } function shouldUsePlaceholderForProperty(propertySymbol: Symbol, context: NodeBuilderContext) { // Use placeholders for reverse mapped types we've either // (1) already descended into, or // (2) are nested reverse mappings within a mapping over a non-anonymous type, or // (3) are deeply nested properties that originate from the same mapped type. // Condition (2) is a restriction mostly just to // reduce the blowup in printback size from doing, eg, a deep reverse mapping over `Window`. // Since anonymous types usually come from expressions, this allows us to preserve the output // for deep mappings which likely come from expressions, while truncating those parts which // come from mappings over library functions. // Condition (3) limits printing of possibly infinitely deep reverse mapped types. const depth = 3; return !!(getCheckFlags(propertySymbol) & CheckFlags.ReverseMapped) && ( contains(context.reverseMappedStack, propertySymbol as ReverseMappedSymbol) || ( context.reverseMappedStack?.[0] && !(getObjectFlags(last(context.reverseMappedStack).links.propertyType) & ObjectFlags.Anonymous) ) || isDeeplyNestedReverseMappedTypeProperty() ); function isDeeplyNestedReverseMappedTypeProperty() { if ((context.reverseMappedStack?.length ?? 0) < depth) { return false; } for (let i = 0; i < depth; i++) { const prop = context.reverseMappedStack![context.reverseMappedStack!.length - 1 - i]; if (prop.links.mappedType.symbol !== (propertySymbol as ReverseMappedSymbol).links.mappedType.symbol) { return false; } } return true; } } function addPropertyToElementList(propertySymbol: Symbol, context: NodeBuilderContext, typeElements: TypeElement[]) { const propertyIsReverseMapped = !!(getCheckFlags(propertySymbol) & CheckFlags.ReverseMapped); const propertyType = shouldUsePlaceholderForProperty(propertySymbol, context) ? anyType : getNonMissingTypeOfSymbol(propertySymbol); const saveEnclosingDeclaration = context.enclosingDeclaration; context.enclosingDeclaration = undefined; if (context.tracker.canTrackSymbol && isLateBoundName(propertySymbol.escapedName)) { if (propertySymbol.declarations) { const decl = first(propertySymbol.declarations); if (hasLateBindableName(decl)) { if (isBinaryExpression(decl)) { const name = getNameOfDeclaration(decl); if (name && isElementAccessExpression(name) && isPropertyAccessEntityNameExpression(name.argumentExpression)) { trackComputedName(name.argumentExpression, saveEnclosingDeclaration, context); } } else { trackComputedName(decl.name.expression, saveEnclosingDeclaration, context); } } } else { context.tracker.reportNonSerializableProperty(symbolToString(propertySymbol)); } } context.enclosingDeclaration = propertySymbol.valueDeclaration || propertySymbol.declarations?.[0] || saveEnclosingDeclaration; const propertyName = getPropertyNameNodeForSymbol(propertySymbol, context); context.enclosingDeclaration = saveEnclosingDeclaration; context.approximateLength += symbolName(propertySymbol).length + 1; if (propertySymbol.flags & SymbolFlags.Accessor) { const writeType = getWriteTypeOfSymbol(propertySymbol); if (propertyType !== writeType && !isErrorType(propertyType) && !isErrorType(writeType)) { const getterDeclaration = getDeclarationOfKind(propertySymbol, SyntaxKind.GetAccessor)!; const getterSignature = getSignatureFromDeclaration(getterDeclaration); typeElements.push( setCommentRange( context, signatureToSignatureDeclarationHelper(getterSignature, SyntaxKind.GetAccessor, context, { name: propertyName }) as GetAccessorDeclaration, getterDeclaration, ), ); const setterDeclaration = getDeclarationOfKind(propertySymbol, SyntaxKind.SetAccessor)!; const setterSignature = getSignatureFromDeclaration(setterDeclaration); typeElements.push( setCommentRange( context, signatureToSignatureDeclarationHelper(setterSignature, SyntaxKind.SetAccessor, context, { name: propertyName }) as SetAccessorDeclaration, setterDeclaration, ), ); return; } } const optionalToken = propertySymbol.flags & SymbolFlags.Optional ? factory.createToken(SyntaxKind.QuestionToken) : undefined; if (propertySymbol.flags & (SymbolFlags.Function | SymbolFlags.Method) && !getPropertiesOfObjectType(propertyType).length && !isReadonlySymbol(propertySymbol)) { const signatures = getSignaturesOfType(filterType(propertyType, t => !(t.flags & TypeFlags.Undefined)), SignatureKind.Call); for (const signature of signatures) { const methodDeclaration = signatureToSignatureDeclarationHelper(signature, SyntaxKind.MethodSignature, context, { name: propertyName, questionToken: optionalToken }) as MethodSignature; typeElements.push(preserveCommentsOn(methodDeclaration)); } if (signatures.length || !optionalToken) { return; } } let propertyTypeNode: TypeNode; if (shouldUsePlaceholderForProperty(propertySymbol, context)) { propertyTypeNode = createElidedInformationPlaceholder(context); } else { if (propertyIsReverseMapped) { context.reverseMappedStack ||= []; context.reverseMappedStack.push(propertySymbol as ReverseMappedSymbol); } propertyTypeNode = propertyType ? serializeTypeForDeclaration(context, /*declaration*/ undefined, propertyType, propertySymbol) : factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); if (propertyIsReverseMapped) { context.reverseMappedStack!.pop(); } } const modifiers = isReadonlySymbol(propertySymbol) ? [factory.createToken(SyntaxKind.ReadonlyKeyword)] : undefined; if (modifiers) { context.approximateLength += 9; } const propertySignature = factory.createPropertySignature( modifiers, propertyName, optionalToken, propertyTypeNode, ); typeElements.push(preserveCommentsOn(propertySignature)); function preserveCommentsOn(node: T) { const jsdocPropertyTag = propertySymbol.declarations?.find((d): d is JSDocPropertyTag => d.kind === SyntaxKind.JSDocPropertyTag); if (jsdocPropertyTag) { const commentText = getTextOfJSDocComment(jsdocPropertyTag.comment); if (commentText) { setSyntheticLeadingComments(node, [{ kind: SyntaxKind.MultiLineCommentTrivia, text: "*\n * " + commentText.replace(/\n/g, "\n * ") + "\n ", pos: -1, end: -1, hasTrailingNewLine: true }]); } } else if (propertySymbol.valueDeclaration) { // Copy comments to node for declaration emit setCommentRange(context, node, propertySymbol.valueDeclaration); } return node; } } function setCommentRange(context: NodeBuilderContext, node: T, range: Node): T { if (context.enclosingFile && context.enclosingFile === getSourceFileOfNode(range)) { // Copy comments to node for declaration emit return setCommentRangeWorker(node, range); } return node; } function mapToTypeNodes(types: readonly Type[] | undefined, context: NodeBuilderContext, isBareList?: boolean): TypeNode[] | undefined { if (some(types)) { if (checkTruncationLength(context)) { if (!isBareList) { return [ context.flags & NodeBuilderFlags.NoTruncation ? addSyntheticLeadingComment(factory.createKeywordTypeNode(SyntaxKind.AnyKeyword), SyntaxKind.MultiLineCommentTrivia, "elided") : factory.createTypeReferenceNode("...", /*typeArguments*/ undefined), ]; } else if (types.length > 2) { return [ typeToTypeNodeHelper(types[0], context), context.flags & NodeBuilderFlags.NoTruncation ? addSyntheticLeadingComment(factory.createKeywordTypeNode(SyntaxKind.AnyKeyword), SyntaxKind.MultiLineCommentTrivia, `... ${types.length - 2} more elided ...`) : factory.createTypeReferenceNode(`... ${types.length - 2} more ...`, /*typeArguments*/ undefined), typeToTypeNodeHelper(types[types.length - 1], context), ]; } } const mayHaveNameCollisions = !(context.flags & NodeBuilderFlags.UseFullyQualifiedType); /** Map from type reference identifier text to [type, index in `result` where the type node is] */ const seenNames = mayHaveNameCollisions ? createMultiMap<__String, [Type, number]>() : undefined; const result: TypeNode[] = []; let i = 0; for (const type of types) { i++; if (checkTruncationLength(context) && (i + 2 < types.length - 1)) { result.push( context.flags & NodeBuilderFlags.NoTruncation ? addSyntheticLeadingComment(factory.createKeywordTypeNode(SyntaxKind.AnyKeyword), SyntaxKind.MultiLineCommentTrivia, `... ${types.length - i} more elided ...`) : factory.createTypeReferenceNode(`... ${types.length - i} more ...`, /*typeArguments*/ undefined), ); const typeNode = typeToTypeNodeHelper(types[types.length - 1], context); if (typeNode) { result.push(typeNode); } break; } context.approximateLength += 2; // Account for whitespace + separator const typeNode = typeToTypeNodeHelper(type, context); if (typeNode) { result.push(typeNode); if (seenNames && isIdentifierTypeReference(typeNode)) { seenNames.add(typeNode.typeName.escapedText, [type, result.length - 1]); } } } if (seenNames) { // To avoid printing types like `[Foo, Foo]` or `Bar & Bar` where // occurrences of the same name actually come from different // namespaces, go through the single-identifier type reference nodes // we just generated, and see if any names were generated more than // once while referring to different types. If so, regenerate the // type node for each entry by that name with the // `UseFullyQualifiedType` flag enabled. const restoreFlags = saveRestoreFlags(context); context.flags |= NodeBuilderFlags.UseFullyQualifiedType; seenNames.forEach(types => { if (!arrayIsHomogeneous(types, ([a], [b]) => typesAreSameReference(a, b))) { for (const [type, resultIndex] of types) { result[resultIndex] = typeToTypeNodeHelper(type, context); } } }); restoreFlags(); } return result; } } function typesAreSameReference(a: Type, b: Type): boolean { return a === b || !!a.symbol && a.symbol === b.symbol || !!a.aliasSymbol && a.aliasSymbol === b.aliasSymbol; } function indexInfoToIndexSignatureDeclarationHelper(indexInfo: IndexInfo, context: NodeBuilderContext, typeNode: TypeNode | undefined): IndexSignatureDeclaration { const name = getNameFromIndexInfo(indexInfo) || "x"; const indexerTypeNode = typeToTypeNodeHelper(indexInfo.keyType, context); const indexingParameter = factory.createParameterDeclaration( /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, name, /*questionToken*/ undefined, indexerTypeNode, /*initializer*/ undefined, ); if (!typeNode) { typeNode = typeToTypeNodeHelper(indexInfo.type || anyType, context); } if (!indexInfo.type && !(context.flags & NodeBuilderFlags.AllowEmptyIndexInfoType)) { context.encounteredError = true; } context.approximateLength += name.length + 4; return factory.createIndexSignature( indexInfo.isReadonly ? [factory.createToken(SyntaxKind.ReadonlyKeyword)] : undefined, [indexingParameter], typeNode, ); } interface SignatureToSignatureDeclarationOptions { modifiers?: readonly Modifier[]; name?: PropertyName; questionToken?: QuestionToken; } function signatureToSignatureDeclarationHelper(signature: Signature, kind: SignatureDeclaration["kind"], context: NodeBuilderContext, options?: SignatureToSignatureDeclarationOptions): SignatureDeclaration { let typeParameters: TypeParameterDeclaration[] | undefined; let typeArguments: TypeNode[] | undefined; const expandedParams = getExpandedParameters(signature, /*skipUnionExpanding*/ true)[0]; const cleanup = enterNewScope(context, signature.declaration, expandedParams, signature.typeParameters, signature.parameters, signature.mapper); context.approximateLength += 3; // Usually a signature contributes a few more characters than this, but 3 is the minimum if (context.flags & NodeBuilderFlags.WriteTypeArgumentsOfSignature && signature.target && signature.mapper && signature.target.typeParameters) { typeArguments = signature.target.typeParameters.map(parameter => typeToTypeNodeHelper(instantiateType(parameter, signature.mapper), context)); } else { typeParameters = signature.typeParameters && signature.typeParameters.map(parameter => typeParameterToDeclaration(parameter, context)); } const restoreFlags = saveRestoreFlags(context); context.flags &= ~NodeBuilderFlags.SuppressAnyReturnType; // SuppressAnyReturnType should only apply to the signature `return` position // If the expanded parameter list had a variadic in a non-trailing position, don't expand it const parameters = (some(expandedParams, p => p !== expandedParams[expandedParams.length - 1] && !!(getCheckFlags(p) & CheckFlags.RestParameter)) ? signature.parameters : expandedParams).map(parameter => symbolToParameterDeclaration(parameter, context, kind === SyntaxKind.Constructor)); const thisParameter = context.flags & NodeBuilderFlags.OmitThisParameter ? undefined : tryGetThisParameterDeclaration(signature, context); if (thisParameter) { parameters.unshift(thisParameter); } restoreFlags(); const returnTypeNode = serializeReturnTypeForSignature(context, signature); let modifiers = options?.modifiers; if ((kind === SyntaxKind.ConstructorType) && signature.flags & SignatureFlags.Abstract) { const flags = modifiersToFlags(modifiers); modifiers = factory.createModifiersFromModifierFlags(flags | ModifierFlags.Abstract); } const node = kind === SyntaxKind.CallSignature ? factory.createCallSignature(typeParameters, parameters, returnTypeNode) : kind === SyntaxKind.ConstructSignature ? factory.createConstructSignature(typeParameters, parameters, returnTypeNode) : kind === SyntaxKind.MethodSignature ? factory.createMethodSignature(modifiers, options?.name ?? factory.createIdentifier(""), options?.questionToken, typeParameters, parameters, returnTypeNode) : kind === SyntaxKind.MethodDeclaration ? factory.createMethodDeclaration(modifiers, /*asteriskToken*/ undefined, options?.name ?? factory.createIdentifier(""), /*questionToken*/ undefined, typeParameters, parameters, returnTypeNode, /*body*/ undefined) : kind === SyntaxKind.Constructor ? factory.createConstructorDeclaration(modifiers, parameters, /*body*/ undefined) : kind === SyntaxKind.GetAccessor ? factory.createGetAccessorDeclaration(modifiers, options?.name ?? factory.createIdentifier(""), parameters, returnTypeNode, /*body*/ undefined) : kind === SyntaxKind.SetAccessor ? factory.createSetAccessorDeclaration(modifiers, options?.name ?? factory.createIdentifier(""), parameters, /*body*/ undefined) : kind === SyntaxKind.IndexSignature ? factory.createIndexSignature(modifiers, parameters, returnTypeNode) : kind === SyntaxKind.JSDocFunctionType ? factory.createJSDocFunctionType(parameters, returnTypeNode) : kind === SyntaxKind.FunctionType ? factory.createFunctionTypeNode(typeParameters, parameters, returnTypeNode ?? factory.createTypeReferenceNode(factory.createIdentifier(""))) : kind === SyntaxKind.ConstructorType ? factory.createConstructorTypeNode(modifiers, typeParameters, parameters, returnTypeNode ?? factory.createTypeReferenceNode(factory.createIdentifier(""))) : kind === SyntaxKind.FunctionDeclaration ? factory.createFunctionDeclaration(modifiers, /*asteriskToken*/ undefined, options?.name ? cast(options.name, isIdentifier) : factory.createIdentifier(""), typeParameters, parameters, returnTypeNode, /*body*/ undefined) : kind === SyntaxKind.FunctionExpression ? factory.createFunctionExpression(modifiers, /*asteriskToken*/ undefined, options?.name ? cast(options.name, isIdentifier) : factory.createIdentifier(""), typeParameters, parameters, returnTypeNode, factory.createBlock([])) : kind === SyntaxKind.ArrowFunction ? factory.createArrowFunction(modifiers, typeParameters, parameters, returnTypeNode, /*equalsGreaterThanToken*/ undefined, factory.createBlock([])) : Debug.assertNever(kind); if (typeArguments) { node.typeArguments = factory.createNodeArray(typeArguments); } if (signature.declaration?.kind === SyntaxKind.JSDocSignature && signature.declaration.parent.kind === SyntaxKind.JSDocOverloadTag) { const comment = getTextOfNode(signature.declaration.parent.parent, /*includeTrivia*/ true).slice(2, -2).split(/\r\n|\n|\r/).map(line => line.replace(/^\s+/, " ")).join("\n"); addSyntheticLeadingComment(node, SyntaxKind.MultiLineCommentTrivia, comment, /*hasTrailingNewLine*/ true); } cleanup?.(); return node; } type IntroducesNewScopeNode = SignatureDeclaration | JSDocSignature | MappedTypeNode; function isNewScopeNode(node: Node): node is IntroducesNewScopeNode { return isFunctionLike(node) || isJSDocSignature(node) || isMappedTypeNode(node); } function getTypeParametersInScope(node: IntroducesNewScopeNode | ConditionalTypeNode) { return isFunctionLike(node) || isJSDocSignature(node) ? getSignatureFromDeclaration(node).typeParameters : isConditionalTypeNode(node) ? getInferTypeParameters(node) : [getDeclaredTypeOfTypeParameter(getSymbolOfDeclaration(node.typeParameter))]; } function getParametersInScope(node: IntroducesNewScopeNode | ConditionalTypeNode) { return isFunctionLike(node) || isJSDocSignature(node) ? getSignatureFromDeclaration(node).parameters : undefined; } function enterNewScope( context: NodeBuilderContext, declaration: IntroducesNewScopeNode | ConditionalTypeNode | undefined, expandedParams: readonly Symbol[] | undefined, typeParameters: readonly TypeParameter[] | undefined, originalParameters?: readonly Symbol[] | undefined, mapper?: TypeMapper, ) { const cleanupContext = cloneNodeBuilderContext(context); // For regular function/method declarations, the enclosing declaration will already be signature.declaration, // so this is a no-op, but for arrow functions and function expressions, the enclosing declaration will be // the declaration that the arrow function / function expression is assigned to. // // If the parameters or return type include "typeof globalThis.paramName", using the wrong scope will lead // us to believe that we can emit "typeof paramName" instead, even though that would refer to the parameter, // not the global. Make sure we are in the right scope by changing the enclosingDeclaration to the function. // // We can't use the declaration directly; it may be in another file and so we may lose access to symbols // accessible to the current enclosing declaration, or gain access to symbols not accessible to the current // enclosing declaration. To keep this chain accurate, insert a fake scope into the chain which makes the // function's parameters visible. let cleanupParams: (() => void) | undefined; let cleanupTypeParams: (() => void) | undefined; const oldEnclosingDecl = context.enclosingDeclaration; const oldMapper = context.mapper; if (mapper) { context.mapper = mapper; } if (context.enclosingDeclaration && declaration) { // As a performance optimization, reuse the same fake scope within this chain. // This is especially needed when we are working on an excessively deep type; // if we don't do this, then we spend all of our time adding more and more // scopes that need to be searched in isSymbolAccessible later. Since all we // really want to do is to mark certain names as unavailable, we can just keep // all of the names we're introducing in one large table and push/pop from it as // needed; isSymbolAccessible will walk upward and find the closest "fake" scope, // which will conveniently report on any and all faked scopes in the chain. // // It'd likely be better to store this somewhere else for isSymbolAccessible, but // since that API _only_ uses the enclosing declaration (and its parents), this is // seems like the best way to inject names into that search process. // // Note that we only check the most immediate enclosingDeclaration; the only place we // could potentially add another fake scope into the chain is right here, so we don't // traverse all ancestors. cleanupParams = !some(expandedParams) ? undefined : pushFakeScope( "params", add => { if (!expandedParams) return; for (let pIndex = 0; pIndex < expandedParams.length; pIndex++) { const param = expandedParams[pIndex]; const originalParam = originalParameters?.[pIndex]; if (originalParameters && originalParam !== param) { // Can't reference parameters that come from an expansion add(param.escapedName, unknownSymbol); // Can't reference the original expanded parameter either if (originalParam) { add(originalParam.escapedName, unknownSymbol); } } else if ( !forEach(param.declarations, d => { if (isParameter(d) && isBindingPattern(d.name)) { bindPattern(d.name); return true; } return undefined; function bindPattern(p: BindingPattern): void { forEach(p.elements, e => { switch (e.kind) { case SyntaxKind.OmittedExpression: return; case SyntaxKind.BindingElement: return bindElement(e); default: return Debug.assertNever(e); } }); } function bindElement(e: BindingElement): void { if (isBindingPattern(e.name)) { return bindPattern(e.name); } const symbol = getSymbolOfDeclaration(e); add(symbol.escapedName, symbol); } }) ) { add(param.escapedName, param); } } }, ); if (context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams && some(typeParameters)) { cleanupTypeParams = pushFakeScope( "typeParams", add => { for (const typeParam of typeParameters ?? emptyArray) { const typeParamName = typeParameterToName(typeParam, context).escapedText; add(typeParamName, typeParam.symbol); } }, ); } function pushFakeScope(kind: "params" | "typeParams", addAll: (addSymbol: (name: __String, symbol: Symbol) => void) => void) { // We only ever need to look two declarations upward. Debug.assert(context.enclosingDeclaration); let existingFakeScope: Node | undefined; if (getNodeLinks(context.enclosingDeclaration).fakeScopeForSignatureDeclaration === kind) { existingFakeScope = context.enclosingDeclaration; } else if (context.enclosingDeclaration.parent && getNodeLinks(context.enclosingDeclaration.parent).fakeScopeForSignatureDeclaration === kind) { existingFakeScope = context.enclosingDeclaration.parent; } Debug.assertOptionalNode(existingFakeScope, isBlock); const locals = existingFakeScope?.locals ?? createSymbolTable(); let newLocals: __String[] | undefined; let oldLocals: { name: __String; oldSymbol: Symbol; }[] | undefined; addAll((name, symbol) => { // Add cleanup information only if we don't own the fake scope if (existingFakeScope) { const oldSymbol = locals.get(name); if (!oldSymbol) { newLocals = append(newLocals, name); } else { oldLocals = append(oldLocals, { name, oldSymbol }); } } locals.set(name, symbol); }); if (!existingFakeScope) { // Use a Block for this; the type of the node doesn't matter so long as it // has locals, and this is cheaper/easier than using a function-ish Node. const fakeScope = factory.createBlock(emptyArray); getNodeLinks(fakeScope).fakeScopeForSignatureDeclaration = kind; fakeScope.locals = locals; setParent(fakeScope, context.enclosingDeclaration); context.enclosingDeclaration = fakeScope; } else { // We did not create the current scope, so we have to clean it up return function undo() { forEach(newLocals, s => locals.delete(s)); forEach(oldLocals, s => locals.set(s.name, s.oldSymbol)); }; } } } return () => { cleanupParams?.(); cleanupTypeParams?.(); cleanupContext(); context.enclosingDeclaration = oldEnclosingDecl; context.mapper = oldMapper; }; } function tryGetThisParameterDeclaration(signature: Signature, context: NodeBuilderContext) { if (signature.thisParameter) { return symbolToParameterDeclaration(signature.thisParameter, context); } if (signature.declaration && isInJSFile(signature.declaration)) { const thisTag = getJSDocThisTag(signature.declaration); if (thisTag && thisTag.typeExpression) { return factory.createParameterDeclaration( /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, "this", /*questionToken*/ undefined, typeToTypeNodeHelper(getTypeFromTypeNode(context, thisTag.typeExpression), context), ); } } } function typeParameterToDeclarationWithConstraint(type: TypeParameter, context: NodeBuilderContext, constraintNode: TypeNode | undefined): TypeParameterDeclaration { const restoreFlags = saveRestoreFlags(context); context.flags &= ~NodeBuilderFlags.WriteTypeParametersInQualifiedName; // Avoids potential infinite loop when building for a claimspace with a generic const modifiers = factory.createModifiersFromModifierFlags(getTypeParameterModifiers(type)); const name = typeParameterToName(type, context); const defaultParameter = getDefaultFromTypeParameter(type); const defaultParameterNode = defaultParameter && typeToTypeNodeHelper(defaultParameter, context); restoreFlags(); return factory.createTypeParameterDeclaration(modifiers, name, constraintNode, defaultParameterNode); } function typeToTypeNodeHelperWithPossibleReusableTypeNode(type: Type, typeNode: TypeNode | undefined, context: NodeBuilderContext) { return typeNode && tryReuseExistingNonParameterTypeNode(context, typeNode, type) || typeToTypeNodeHelper(type, context); } function typeParameterToDeclaration(type: TypeParameter, context: NodeBuilderContext, constraint = getConstraintOfTypeParameter(type)): TypeParameterDeclaration { const constraintNode = constraint && typeToTypeNodeHelperWithPossibleReusableTypeNode(constraint, getConstraintDeclaration(type), context); return typeParameterToDeclarationWithConstraint(type, context, constraintNode); } function typePredicateToTypePredicateNodeHelper(typePredicate: TypePredicate, context: NodeBuilderContext): TypePredicateNode { const assertsModifier = typePredicate.kind === TypePredicateKind.AssertsThis || typePredicate.kind === TypePredicateKind.AssertsIdentifier ? factory.createToken(SyntaxKind.AssertsKeyword) : undefined; const parameterName = typePredicate.kind === TypePredicateKind.Identifier || typePredicate.kind === TypePredicateKind.AssertsIdentifier ? setEmitFlags(factory.createIdentifier(typePredicate.parameterName), EmitFlags.NoAsciiEscaping) : factory.createThisTypeNode(); const typeNode = typePredicate.type && typeToTypeNodeHelper(typePredicate.type, context); return factory.createTypePredicateNode(assertsModifier, parameterName, typeNode); } function getEffectiveParameterDeclaration(parameterSymbol: Symbol): ParameterDeclaration | JSDocParameterTag | undefined { const parameterDeclaration: ParameterDeclaration | JSDocParameterTag | undefined = getDeclarationOfKind(parameterSymbol, SyntaxKind.Parameter); if (parameterDeclaration) { return parameterDeclaration; } if (!isTransientSymbol(parameterSymbol)) { return getDeclarationOfKind(parameterSymbol, SyntaxKind.JSDocParameterTag); } } function symbolToParameterDeclaration(parameterSymbol: Symbol, context: NodeBuilderContext, preserveModifierFlags?: boolean): ParameterDeclaration { const parameterDeclaration = getEffectiveParameterDeclaration(parameterSymbol); const parameterType = getTypeOfSymbol(parameterSymbol); const parameterTypeNode = serializeTypeForDeclaration(context, parameterDeclaration, parameterType, parameterSymbol); const modifiers = !(context.flags & NodeBuilderFlags.OmitParameterModifiers) && preserveModifierFlags && parameterDeclaration && canHaveModifiers(parameterDeclaration) ? map(getModifiers(parameterDeclaration), factory.cloneNode) : undefined; const isRest = parameterDeclaration && isRestParameter(parameterDeclaration) || getCheckFlags(parameterSymbol) & CheckFlags.RestParameter; const dotDotDotToken = isRest ? factory.createToken(SyntaxKind.DotDotDotToken) : undefined; const name = parameterToParameterDeclarationName(parameterSymbol, parameterDeclaration, context); const isOptional = parameterDeclaration && isOptionalParameter(parameterDeclaration) || getCheckFlags(parameterSymbol) & CheckFlags.OptionalParameter; const questionToken = isOptional ? factory.createToken(SyntaxKind.QuestionToken) : undefined; const parameterNode = factory.createParameterDeclaration( modifiers, dotDotDotToken, name, questionToken, parameterTypeNode, /*initializer*/ undefined, ); context.approximateLength += symbolName(parameterSymbol).length + 3; return parameterNode; } function parameterToParameterDeclarationName(parameterSymbol: Symbol, parameterDeclaration: ParameterDeclaration | JSDocParameterTag | undefined, context: NodeBuilderContext) { return parameterDeclaration ? parameterDeclaration.name ? parameterDeclaration.name.kind === SyntaxKind.Identifier ? setEmitFlags(factory.cloneNode(parameterDeclaration.name), EmitFlags.NoAsciiEscaping) : parameterDeclaration.name.kind === SyntaxKind.QualifiedName ? setEmitFlags(factory.cloneNode(parameterDeclaration.name.right), EmitFlags.NoAsciiEscaping) : cloneBindingName(parameterDeclaration.name) : symbolName(parameterSymbol) : symbolName(parameterSymbol); function cloneBindingName(node: BindingName): BindingName { return elideInitializerAndSetEmitFlags(node) as BindingName; function elideInitializerAndSetEmitFlags(node: Node): Node { if (context.tracker.canTrackSymbol && isComputedPropertyName(node) && isLateBindableName(node)) { trackComputedName(node.expression, context.enclosingDeclaration, context); } let visited = visitEachChildWorker(node, elideInitializerAndSetEmitFlags, /*context*/ undefined, /*nodesVisitor*/ undefined, elideInitializerAndSetEmitFlags); if (isBindingElement(visited)) { visited = factory.updateBindingElement( visited, visited.dotDotDotToken, visited.propertyName, visited.name, /*initializer*/ undefined, ); } if (!nodeIsSynthesized(visited)) { visited = factory.cloneNode(visited); } return setEmitFlags(visited, EmitFlags.SingleLine | EmitFlags.NoAsciiEscaping); } } } function trackComputedName(accessExpression: EntityNameOrEntityNameExpression, enclosingDeclaration: Node | undefined, context: NodeBuilderContext) { if (!context.tracker.canTrackSymbol) return; // get symbol of the first identifier of the entityName const firstIdentifier = getFirstIdentifier(accessExpression); const name = resolveName(firstIdentifier, firstIdentifier.escapedText, SymbolFlags.Value | SymbolFlags.ExportValue, /*nameNotFoundMessage*/ undefined, /*isUse*/ true); if (name) { context.tracker.trackSymbol(name, enclosingDeclaration, SymbolFlags.Value); } } function lookupSymbolChain(symbol: Symbol, context: NodeBuilderContext, meaning: SymbolFlags, yieldModuleSymbol?: boolean) { context.tracker.trackSymbol(symbol, context.enclosingDeclaration, meaning); return lookupSymbolChainWorker(symbol, context, meaning, yieldModuleSymbol); } function lookupSymbolChainWorker(symbol: Symbol, context: NodeBuilderContext, meaning: SymbolFlags, yieldModuleSymbol?: boolean) { // Try to get qualified name if the symbol is not a type parameter and there is an enclosing declaration. let chain: Symbol[]; const isTypeParameter = symbol.flags & SymbolFlags.TypeParameter; if (!isTypeParameter && (context.enclosingDeclaration || context.flags & NodeBuilderFlags.UseFullyQualifiedType) && !(context.internalFlags & InternalNodeBuilderFlags.DoNotIncludeSymbolChain)) { chain = Debug.checkDefined(getSymbolChain(symbol, meaning, /*endOfChain*/ true)); Debug.assert(chain && chain.length > 0); } else { chain = [symbol]; } return chain; /** @param endOfChain Set to false for recursive calls; non-recursive calls should always output something. */ function getSymbolChain(symbol: Symbol, meaning: SymbolFlags, endOfChain: boolean): Symbol[] | undefined { let accessibleSymbolChain = getAccessibleSymbolChain(symbol, context.enclosingDeclaration, meaning, !!(context.flags & NodeBuilderFlags.UseOnlyExternalAliasing)); let parentSpecifiers: (string | undefined)[]; if ( !accessibleSymbolChain || needsQualification(accessibleSymbolChain[0], context.enclosingDeclaration, accessibleSymbolChain.length === 1 ? meaning : getQualifiedLeftMeaning(meaning)) ) { // Go up and add our parent. const parents = getContainersOfSymbol(accessibleSymbolChain ? accessibleSymbolChain[0] : symbol, context.enclosingDeclaration, meaning); if (length(parents)) { parentSpecifiers = parents!.map(symbol => some(symbol.declarations, hasNonGlobalAugmentationExternalModuleSymbol) ? getSpecifierForModuleSymbol(symbol, context) : undefined ); const indices = parents!.map((_, i) => i); indices.sort(sortByBestName); const sortedParents = indices.map(i => parents![i]); for (const parent of sortedParents) { const parentChain = getSymbolChain(parent, getQualifiedLeftMeaning(meaning), /*endOfChain*/ false); if (parentChain) { if ( parent.exports && parent.exports.get(InternalSymbolName.ExportEquals) && getSymbolIfSameReference(parent.exports.get(InternalSymbolName.ExportEquals)!, symbol) ) { // parentChain root _is_ symbol - symbol is a module export=, so it kinda looks like it's own parent // No need to lookup an alias for the symbol in itself accessibleSymbolChain = parentChain; break; } accessibleSymbolChain = parentChain.concat(accessibleSymbolChain || [getAliasForSymbolInContainer(parent, symbol) || symbol]); break; } } } } if (accessibleSymbolChain) { return accessibleSymbolChain; } if ( // If this is the last part of outputting the symbol, always output. The cases apply only to parent symbols. endOfChain || // If a parent symbol is an anonymous type, don't write it. !(symbol.flags & (SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral)) ) { // If a parent symbol is an external module, don't write it. (We prefer just `x` vs `"foo/bar".x`.) if (!endOfChain && !yieldModuleSymbol && !!forEach(symbol.declarations, hasNonGlobalAugmentationExternalModuleSymbol)) { return; } return [symbol]; } function sortByBestName(a: number, b: number) { const specifierA = parentSpecifiers[a]; const specifierB = parentSpecifiers[b]; if (specifierA && specifierB) { const isBRelative = pathIsRelative(specifierB); if (pathIsRelative(specifierA) === isBRelative) { // Both relative or both non-relative, sort by number of parts return moduleSpecifiers.countPathComponents(specifierA) - moduleSpecifiers.countPathComponents(specifierB); } if (isBRelative) { // A is non-relative, B is relative: prefer A return -1; } // A is relative, B is non-relative: prefer B return 1; } return 0; } } } function typeParametersToTypeParameterDeclarations(symbol: Symbol, context: NodeBuilderContext) { let typeParameterNodes: NodeArray | undefined; const targetSymbol = getTargetSymbol(symbol); if (targetSymbol.flags & (SymbolFlags.Class | SymbolFlags.Interface | SymbolFlags.TypeAlias)) { typeParameterNodes = factory.createNodeArray(map(getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol), tp => typeParameterToDeclaration(tp, context))); } return typeParameterNodes; } function lookupTypeParameterNodes(chain: Symbol[], index: number, context: NodeBuilderContext) { Debug.assert(chain && 0 <= index && index < chain.length); const symbol = chain[index]; const symbolId = getSymbolId(symbol); if (context.typeParameterSymbolList?.has(symbolId)) { return undefined; } if (context.mustCreateTypeParameterSymbolList) { context.mustCreateTypeParameterSymbolList = false; context.typeParameterSymbolList = new Set(context.typeParameterSymbolList); } context.typeParameterSymbolList!.add(symbolId); let typeParameterNodes: readonly TypeNode[] | readonly TypeParameterDeclaration[] | undefined; if (context.flags & NodeBuilderFlags.WriteTypeParametersInQualifiedName && index < (chain.length - 1)) { const parentSymbol = symbol; const nextSymbol = chain[index + 1]; if (getCheckFlags(nextSymbol) & CheckFlags.Instantiated) { const params = getTypeParametersOfClassOrInterface( parentSymbol.flags & SymbolFlags.Alias ? resolveAlias(parentSymbol) : parentSymbol, ); // NOTE: cast to TransientSymbol should be safe because only TransientSymbol can have CheckFlags.Instantiated typeParameterNodes = mapToTypeNodes(map(params, t => getMappedType(t, (nextSymbol as TransientSymbol).links.mapper!)), context); } else { typeParameterNodes = typeParametersToTypeParameterDeclarations(symbol, context); } } return typeParameterNodes; } /** * Given A[B][C][D], finds A[B] */ function getTopmostIndexedAccessType(top: IndexedAccessTypeNode): IndexedAccessTypeNode { if (isIndexedAccessTypeNode(top.objectType)) { return getTopmostIndexedAccessType(top.objectType); } return top; } function getSpecifierForModuleSymbol(symbol: Symbol, context: NodeBuilderContext, overrideImportMode?: ResolutionMode) { let file = getDeclarationOfKind(symbol, SyntaxKind.SourceFile); if (!file) { const equivalentFileSymbol = firstDefined(symbol.declarations, d => getFileSymbolIfFileSymbolExportEqualsContainer(d, symbol)); if (equivalentFileSymbol) { file = getDeclarationOfKind(equivalentFileSymbol, SyntaxKind.SourceFile); } } if (file && file.moduleName !== undefined) { // Use the amd name if it is available return file.moduleName; } if (!file) { if (ambientModuleSymbolRegex.test(symbol.escapedName as string)) { return (symbol.escapedName as string).substring(1, (symbol.escapedName as string).length - 1); } } if (!context.enclosingFile || !context.tracker.moduleResolverHost) { // If there's no context declaration, we can't lookup a non-ambient specifier, so we just use the symbol name if (ambientModuleSymbolRegex.test(symbol.escapedName as string)) { return (symbol.escapedName as string).substring(1, (symbol.escapedName as string).length - 1); } return getSourceFileOfNode(getNonAugmentationDeclaration(symbol)!).fileName; // A resolver may not be provided for baselines and errors - in those cases we use the fileName in full } const enclosingDeclaration = getOriginalNode(context.enclosingDeclaration); const originalModuleSpecifier = canHaveModuleSpecifier(enclosingDeclaration) ? tryGetModuleSpecifierFromDeclaration(enclosingDeclaration) : undefined; const contextFile = context.enclosingFile; const resolutionMode = overrideImportMode || originalModuleSpecifier && host.getModeForUsageLocation(contextFile, originalModuleSpecifier) || contextFile && host.getDefaultResolutionModeForFile(contextFile); const cacheKey = createModeAwareCacheKey(contextFile.path, resolutionMode); const links = getSymbolLinks(symbol); let specifier = links.specifierCache && links.specifierCache.get(cacheKey); if (!specifier) { const isBundle = !!compilerOptions.outFile; // For declaration bundles, we need to generate absolute paths relative to the common source dir for imports, // just like how the declaration emitter does for the ambient module declarations - we can easily accomplish this // using the `baseUrl` compiler option (which we would otherwise never use in declaration emit) and a non-relative // specifier preference const { moduleResolverHost } = context.tracker; const specifierCompilerOptions = isBundle ? { ...compilerOptions, baseUrl: moduleResolverHost.getCommonSourceDirectory() } : compilerOptions; specifier = first(moduleSpecifiers.getModuleSpecifiers( symbol, checker, specifierCompilerOptions, contextFile, moduleResolverHost, { importModuleSpecifierPreference: isBundle ? "non-relative" : "project-relative", importModuleSpecifierEnding: isBundle ? "minimal" : resolutionMode === ModuleKind.ESNext ? "js" : undefined, }, { overrideImportMode }, )); links.specifierCache ??= new Map(); links.specifierCache.set(cacheKey, specifier); } return specifier; } function symbolToEntityNameNode(symbol: Symbol): EntityName { const identifier = factory.createIdentifier(unescapeLeadingUnderscores(symbol.escapedName)); return symbol.parent ? factory.createQualifiedName(symbolToEntityNameNode(symbol.parent), identifier) : identifier; } function symbolToTypeNode(symbol: Symbol, context: NodeBuilderContext, meaning: SymbolFlags, overrideTypeArguments?: readonly TypeNode[]): TypeNode { const chain = lookupSymbolChain(symbol, context, meaning, !(context.flags & NodeBuilderFlags.UseAliasDefinedOutsideCurrentScope)); // If we're using aliases outside the current scope, dont bother with the module const isTypeOf = meaning === SymbolFlags.Value; if (some(chain[0].declarations, hasNonGlobalAugmentationExternalModuleSymbol)) { // module is root, must use `ImportTypeNode` const nonRootParts = chain.length > 1 ? createAccessFromSymbolChain(chain, chain.length - 1, 1) : undefined; const typeParameterNodes = overrideTypeArguments || lookupTypeParameterNodes(chain, 0, context); const contextFile = getSourceFileOfNode(getOriginalNode(context.enclosingDeclaration)); const targetFile = getSourceFileOfModule(chain[0]); let specifier: string | undefined; let attributes: ImportAttributes | undefined; if (getEmitModuleResolutionKind(compilerOptions) === ModuleResolutionKind.Node16 || getEmitModuleResolutionKind(compilerOptions) === ModuleResolutionKind.NodeNext) { // An `import` type directed at an esm format file is only going to resolve in esm mode - set the esm mode assertion if (targetFile?.impliedNodeFormat === ModuleKind.ESNext && targetFile.impliedNodeFormat !== contextFile?.impliedNodeFormat) { specifier = getSpecifierForModuleSymbol(chain[0], context, ModuleKind.ESNext); attributes = factory.createImportAttributes( factory.createNodeArray([ factory.createImportAttribute( factory.createStringLiteral("resolution-mode"), factory.createStringLiteral("import"), ), ]), ); } } if (!specifier) { specifier = getSpecifierForModuleSymbol(chain[0], context); } if (!(context.flags & NodeBuilderFlags.AllowNodeModulesRelativePaths) && getEmitModuleResolutionKind(compilerOptions) !== ModuleResolutionKind.Classic && specifier.includes("/node_modules/")) { const oldSpecifier = specifier; if (getEmitModuleResolutionKind(compilerOptions) === ModuleResolutionKind.Node16 || getEmitModuleResolutionKind(compilerOptions) === ModuleResolutionKind.NodeNext) { // We might be able to write a portable import type using a mode override; try specifier generation again, but with a different mode set const swappedMode = contextFile?.impliedNodeFormat === ModuleKind.ESNext ? ModuleKind.CommonJS : ModuleKind.ESNext; specifier = getSpecifierForModuleSymbol(chain[0], context, swappedMode); if (specifier.includes("/node_modules/")) { // Still unreachable :( specifier = oldSpecifier; } else { attributes = factory.createImportAttributes( factory.createNodeArray([ factory.createImportAttribute( factory.createStringLiteral("resolution-mode"), factory.createStringLiteral(swappedMode === ModuleKind.ESNext ? "import" : "require"), ), ]), ); } } if (!attributes) { // If ultimately we can only name the symbol with a reference that dives into a `node_modules` folder, we should error // since declaration files with these kinds of references are liable to fail when published :( context.encounteredError = true; if (context.tracker.reportLikelyUnsafeImportRequiredError) { context.tracker.reportLikelyUnsafeImportRequiredError(oldSpecifier); } } } const lit = factory.createLiteralTypeNode(factory.createStringLiteral(specifier)); context.approximateLength += specifier.length + 10; // specifier + import("") if (!nonRootParts || isEntityName(nonRootParts)) { if (nonRootParts) { const lastId = isIdentifier(nonRootParts) ? nonRootParts : nonRootParts.right; setIdentifierTypeArguments(lastId, /*typeArguments*/ undefined); } return factory.createImportTypeNode(lit, attributes, nonRootParts as EntityName, typeParameterNodes as readonly TypeNode[], isTypeOf); } else { const splitNode = getTopmostIndexedAccessType(nonRootParts); const qualifier = (splitNode.objectType as TypeReferenceNode).typeName; return factory.createIndexedAccessTypeNode(factory.createImportTypeNode(lit, attributes, qualifier, typeParameterNodes as readonly TypeNode[], isTypeOf), splitNode.indexType); } } const entityName = createAccessFromSymbolChain(chain, chain.length - 1, 0); if (isIndexedAccessTypeNode(entityName)) { return entityName; // Indexed accesses can never be `typeof` } if (isTypeOf) { return factory.createTypeQueryNode(entityName); } else { const lastId = isIdentifier(entityName) ? entityName : entityName.right; const lastTypeArgs = getIdentifierTypeArguments(lastId); setIdentifierTypeArguments(lastId, /*typeArguments*/ undefined); return factory.createTypeReferenceNode(entityName, lastTypeArgs as NodeArray); } function createAccessFromSymbolChain(chain: Symbol[], index: number, stopper: number): EntityName | IndexedAccessTypeNode { const typeParameterNodes = index === (chain.length - 1) ? overrideTypeArguments : lookupTypeParameterNodes(chain, index, context); const symbol = chain[index]; const parent = chain[index - 1]; let symbolName: string | undefined; if (index === 0) { context.flags |= NodeBuilderFlags.InInitialEntityName; symbolName = getNameOfSymbolAsWritten(symbol, context); context.approximateLength += (symbolName ? symbolName.length : 0) + 1; context.flags ^= NodeBuilderFlags.InInitialEntityName; } else { if (parent && getExportsOfSymbol(parent)) { const exports = getExportsOfSymbol(parent); forEachEntry(exports, (ex, name) => { if (getSymbolIfSameReference(ex, symbol) && !isLateBoundName(name) && name !== InternalSymbolName.ExportEquals) { symbolName = unescapeLeadingUnderscores(name); return true; } }); } } if (symbolName === undefined) { const name = firstDefined(symbol.declarations, getNameOfDeclaration); if (name && isComputedPropertyName(name) && isEntityName(name.expression)) { const LHS = createAccessFromSymbolChain(chain, index - 1, stopper); if (isEntityName(LHS)) { return factory.createIndexedAccessTypeNode(factory.createParenthesizedType(factory.createTypeQueryNode(LHS)), factory.createTypeQueryNode(name.expression)); } return LHS; } symbolName = getNameOfSymbolAsWritten(symbol, context); } context.approximateLength += symbolName.length + 1; if ( !(context.flags & NodeBuilderFlags.ForbidIndexedAccessSymbolReferences) && parent && getMembersOfSymbol(parent) && getMembersOfSymbol(parent).get(symbol.escapedName) && getSymbolIfSameReference(getMembersOfSymbol(parent).get(symbol.escapedName)!, symbol) ) { // Should use an indexed access const LHS = createAccessFromSymbolChain(chain, index - 1, stopper); if (isIndexedAccessTypeNode(LHS)) { return factory.createIndexedAccessTypeNode(LHS, factory.createLiteralTypeNode(factory.createStringLiteral(symbolName))); } else { return factory.createIndexedAccessTypeNode(factory.createTypeReferenceNode(LHS, typeParameterNodes as readonly TypeNode[]), factory.createLiteralTypeNode(factory.createStringLiteral(symbolName))); } } const identifier = setEmitFlags(factory.createIdentifier(symbolName), EmitFlags.NoAsciiEscaping); if (typeParameterNodes) setIdentifierTypeArguments(identifier, factory.createNodeArray(typeParameterNodes)); identifier.symbol = symbol; if (index > stopper) { const LHS = createAccessFromSymbolChain(chain, index - 1, stopper); if (!isEntityName(LHS)) { return Debug.fail("Impossible construct - an export of an indexed access cannot be reachable"); } return factory.createQualifiedName(LHS, identifier); } return identifier; } } function typeParameterShadowsOtherTypeParameterInScope(escapedName: __String, context: NodeBuilderContext, type: TypeParameter) { const result = resolveName(context.enclosingDeclaration, escapedName, SymbolFlags.Type, /*nameNotFoundMessage*/ undefined, /*isUse*/ false); if (result && result.flags & SymbolFlags.TypeParameter) { return result !== type.symbol; } return false; } function typeParameterToName(type: TypeParameter, context: NodeBuilderContext) { if (context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams && context.typeParameterNames) { const cached = context.typeParameterNames.get(getTypeId(type)); if (cached) { return cached; } } let result = symbolToName(type.symbol, context, SymbolFlags.Type, /*expectsIdentifier*/ true); if (!(result.kind & SyntaxKind.Identifier)) { return factory.createIdentifier("(Missing type parameter)"); } const decl = type.symbol?.declarations?.[0]; if (decl && isTypeParameterDeclaration(decl)) { result = setTextRange(context, result, decl.name); } if (context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams) { const rawtext = result.escapedText as string; let i = context.typeParameterNamesByTextNextNameCount?.get(rawtext) || 0; let text = rawtext; while (context.typeParameterNamesByText?.has(text) || typeParameterShadowsOtherTypeParameterInScope(text as __String, context, type)) { i++; text = `${rawtext}_${i}`; } if (text !== rawtext) { const typeArguments = getIdentifierTypeArguments(result); result = factory.createIdentifier(text); setIdentifierTypeArguments(result, typeArguments); } if (context.mustCreateTypeParametersNamesLookups) { context.mustCreateTypeParametersNamesLookups = false; context.typeParameterNames = new Map(context.typeParameterNames); context.typeParameterNamesByTextNextNameCount = new Map(context.typeParameterNamesByTextNextNameCount); context.typeParameterNamesByText = new Set(context.typeParameterNamesByText); } // avoiding iterations of the above loop turns out to be worth it when `i` starts to get large, so we cache the max // `i` we've used thus far, to save work later context.typeParameterNamesByTextNextNameCount!.set(rawtext, i); context.typeParameterNames!.set(getTypeId(type), result); context.typeParameterNamesByText!.add(text); } return result; } function symbolToName(symbol: Symbol, context: NodeBuilderContext, meaning: SymbolFlags, expectsIdentifier: true): Identifier; function symbolToName(symbol: Symbol, context: NodeBuilderContext, meaning: SymbolFlags, expectsIdentifier: false): EntityName; function symbolToName(symbol: Symbol, context: NodeBuilderContext, meaning: SymbolFlags, expectsIdentifier: boolean): EntityName { const chain = lookupSymbolChain(symbol, context, meaning); if ( expectsIdentifier && chain.length !== 1 && !context.encounteredError && !(context.flags & NodeBuilderFlags.AllowQualifiedNameInPlaceOfIdentifier) ) { context.encounteredError = true; } return createEntityNameFromSymbolChain(chain, chain.length - 1); function createEntityNameFromSymbolChain(chain: Symbol[], index: number): EntityName { const typeParameterNodes = lookupTypeParameterNodes(chain, index, context); const symbol = chain[index]; if (index === 0) { context.flags |= NodeBuilderFlags.InInitialEntityName; } const symbolName = getNameOfSymbolAsWritten(symbol, context); if (index === 0) { context.flags ^= NodeBuilderFlags.InInitialEntityName; } const identifier = setEmitFlags(factory.createIdentifier(symbolName), EmitFlags.NoAsciiEscaping); if (typeParameterNodes) setIdentifierTypeArguments(identifier, factory.createNodeArray(typeParameterNodes)); identifier.symbol = symbol; return index > 0 ? factory.createQualifiedName(createEntityNameFromSymbolChain(chain, index - 1), identifier) : identifier; } } function symbolToExpression(symbol: Symbol, context: NodeBuilderContext, meaning: SymbolFlags) { const chain = lookupSymbolChain(symbol, context, meaning); return createExpressionFromSymbolChain(chain, chain.length - 1); function createExpressionFromSymbolChain(chain: Symbol[], index: number): Expression { const typeParameterNodes = lookupTypeParameterNodes(chain, index, context); const symbol = chain[index]; if (index === 0) { context.flags |= NodeBuilderFlags.InInitialEntityName; } let symbolName = getNameOfSymbolAsWritten(symbol, context); if (index === 0) { context.flags ^= NodeBuilderFlags.InInitialEntityName; } let firstChar = symbolName.charCodeAt(0); if (isSingleOrDoubleQuote(firstChar) && some(symbol.declarations, hasNonGlobalAugmentationExternalModuleSymbol)) { return factory.createStringLiteral(getSpecifierForModuleSymbol(symbol, context)); } if (index === 0 || canUsePropertyAccess(symbolName, languageVersion)) { const identifier = setEmitFlags(factory.createIdentifier(symbolName), EmitFlags.NoAsciiEscaping); if (typeParameterNodes) setIdentifierTypeArguments(identifier, factory.createNodeArray(typeParameterNodes)); identifier.symbol = symbol; return index > 0 ? factory.createPropertyAccessExpression(createExpressionFromSymbolChain(chain, index - 1), identifier) : identifier; } else { if (firstChar === CharacterCodes.openBracket) { symbolName = symbolName.substring(1, symbolName.length - 1); firstChar = symbolName.charCodeAt(0); } let expression: Expression | undefined; if (isSingleOrDoubleQuote(firstChar) && !(symbol.flags & SymbolFlags.EnumMember)) { expression = factory.createStringLiteral(stripQuotes(symbolName).replace(/\\./g, s => s.substring(1)), firstChar === CharacterCodes.singleQuote); } else if (("" + +symbolName) === symbolName) { expression = factory.createNumericLiteral(+symbolName); } if (!expression) { const identifier = setEmitFlags(factory.createIdentifier(symbolName), EmitFlags.NoAsciiEscaping); if (typeParameterNodes) setIdentifierTypeArguments(identifier, factory.createNodeArray(typeParameterNodes)); identifier.symbol = symbol; expression = identifier; } return factory.createElementAccessExpression(createExpressionFromSymbolChain(chain, index - 1), expression); } } } function isStringNamed(d: Declaration) { const name = getNameOfDeclaration(d); if (!name) { return false; } if (isComputedPropertyName(name)) { const type = checkExpression(name.expression); return !!(type.flags & TypeFlags.StringLike); } if (isElementAccessExpression(name)) { const type = checkExpression(name.argumentExpression); return !!(type.flags & TypeFlags.StringLike); } return isStringLiteral(name); } function isSingleQuotedStringNamed(d: Declaration) { const name = getNameOfDeclaration(d); return !!(name && isStringLiteral(name) && (name.singleQuote || !nodeIsSynthesized(name) && startsWith(getTextOfNode(name, /*includeTrivia*/ false), "'"))); } function getPropertyNameNodeForSymbol(symbol: Symbol, context: NodeBuilderContext) { const stringNamed = !!length(symbol.declarations) && every(symbol.declarations, isStringNamed); const singleQuote = !!length(symbol.declarations) && every(symbol.declarations, isSingleQuotedStringNamed); const isMethod = !!(symbol.flags & SymbolFlags.Method); const fromNameType = getPropertyNameNodeForSymbolFromNameType(symbol, context, singleQuote, stringNamed, isMethod); if (fromNameType) { return fromNameType; } const rawName = unescapeLeadingUnderscores(symbol.escapedName); return createPropertyNameNodeForIdentifierOrLiteral(rawName, getEmitScriptTarget(compilerOptions), singleQuote, stringNamed, isMethod); } // See getNameForSymbolFromNameType for a stringy equivalent function getPropertyNameNodeForSymbolFromNameType(symbol: Symbol, context: NodeBuilderContext, singleQuote: boolean, stringNamed: boolean, isMethod: boolean) { const nameType = getSymbolLinks(symbol).nameType; if (nameType) { if (nameType.flags & TypeFlags.StringOrNumberLiteral) { const name = "" + (nameType as StringLiteralType | NumberLiteralType).value; if (!isIdentifierText(name, getEmitScriptTarget(compilerOptions)) && (stringNamed || !isNumericLiteralName(name))) { return factory.createStringLiteral(name, !!singleQuote); } if (isNumericLiteralName(name) && startsWith(name, "-")) { return factory.createComputedPropertyName(factory.createPrefixUnaryExpression(SyntaxKind.MinusToken, factory.createNumericLiteral(-name))); } return createPropertyNameNodeForIdentifierOrLiteral(name, getEmitScriptTarget(compilerOptions), singleQuote, stringNamed, isMethod); } if (nameType.flags & TypeFlags.UniqueESSymbol) { return factory.createComputedPropertyName(symbolToExpression((nameType as UniqueESSymbolType).symbol, context, SymbolFlags.Value)); } } } function cloneNodeBuilderContext(context: NodeBuilderContext) { // Make type parameters created within this context not consume the name outside this context // The symbol serializer ends up creating many sibling scopes that all need "separate" contexts when // it comes to naming things - within a normal `typeToTypeNode` call, the node builder only ever descends // through the type tree, so the only cases where we could have used distinct sibling scopes was when there // were multiple generic overloads with similar generated type parameter names // The effect: // When we write out // export const x: (x: T) => T // export const y: (x: T) => T // we write it out like that, rather than as // export const x: (x: T) => T // export const y: (x: T_1) => T_1 const oldMustCreateTypeParameterSymbolList = context.mustCreateTypeParameterSymbolList; const oldMustCreateTypeParametersNamesLookups = context.mustCreateTypeParametersNamesLookups; context.mustCreateTypeParameterSymbolList = true; context.mustCreateTypeParametersNamesLookups = true; const oldTypeParameterNames = context.typeParameterNames; const oldTypeParameterNamesByText = context.typeParameterNamesByText; const oldTypeParameterNamesByTextNextNameCount = context.typeParameterNamesByTextNextNameCount; const oldTypeParameterSymbolList = context.typeParameterSymbolList; return () => { context.typeParameterNames = oldTypeParameterNames; context.typeParameterNamesByText = oldTypeParameterNamesByText; context.typeParameterNamesByTextNextNameCount = oldTypeParameterNamesByTextNextNameCount; context.typeParameterSymbolList = oldTypeParameterSymbolList; context.mustCreateTypeParameterSymbolList = oldMustCreateTypeParameterSymbolList; context.mustCreateTypeParametersNamesLookups = oldMustCreateTypeParametersNamesLookups; }; } function getDeclarationWithTypeAnnotation(symbol: Symbol, enclosingDeclaration?: Node | undefined) { return symbol.declarations && find(symbol.declarations, s => !!getNonlocalEffectiveTypeAnnotationNode(s) && (!enclosingDeclaration || !!findAncestor(s, n => n === enclosingDeclaration))); } function existingTypeNodeIsNotReferenceOrIsReferenceWithCompatibleTypeArgumentCount(existing: TypeNode, type: Type) { // In JS, you can say something like `Foo` and get a `Foo` implicitly - we don't want to preserve that original `Foo` in these cases, though. if (!(getObjectFlags(type) & ObjectFlags.Reference)) return true; if (!isTypeReferenceNode(existing)) return true; // `type` is a reference type, and `existing` is a type reference node, but we still need to make sure they refer to the _same_ target type // before we go comparing their type argument counts. void getTypeFromTypeReference(existing); // call to ensure symbol is resolved const symbol = getNodeLinks(existing).resolvedSymbol; const existingTarget = symbol && getDeclaredTypeOfSymbol(symbol); if (!existingTarget || existingTarget !== (type as TypeReference).target) return true; return length(existing.typeArguments) >= getMinTypeArgumentCount((type as TypeReference).target.typeParameters); } function getEnclosingDeclarationIgnoringFakeScope(enclosingDeclaration: Node) { while (getNodeLinks(enclosingDeclaration).fakeScopeForSignatureDeclaration) { enclosingDeclaration = enclosingDeclaration.parent; } return enclosingDeclaration; } /** * Unlike `typeToTypeNodeHelper`, this handles setting up the `AllowUniqueESSymbolType` flag * so a `unique symbol` is returned when appropriate for the input symbol, rather than `typeof sym` * @param context - The node builder context. Any reused nodes are checked to be pulled from within the scope of the context's enclosingDeclaration. * @param declaration - The preferred declaration to pull existing type nodes from (the symbol will be used as a fallback to find any annotated declaration) * @param type - The type to write; an existing annotation must match this type if it's used, otherwise this is the type serialized as a new type node * @param symbol - The symbol is used both to find an existing annotation if declaration is not provided, and to determine if `unique symbol` should be printed */ function serializeTypeForDeclaration(context: NodeBuilderContext, declaration: Declaration | undefined, type: Type, symbol: Symbol) { const addUndefinedForParameter = declaration && (isParameter(declaration) || isJSDocParameterTag(declaration)) && requiresAddingImplicitUndefined(declaration, context.enclosingDeclaration); const enclosingDeclaration = context.enclosingDeclaration; const restoreFlags = saveRestoreFlags(context); if (declaration && hasInferredType(declaration) && !(context.internalFlags & InternalNodeBuilderFlags.NoSyntacticPrinter)) { syntacticNodeBuilder.serializeTypeOfDeclaration(declaration, context); } context.internalFlags |= InternalNodeBuilderFlags.NoSyntacticPrinter; if (enclosingDeclaration && (!isErrorType(type) || (context.internalFlags & InternalNodeBuilderFlags.AllowUnresolvedNames))) { const declWithExistingAnnotation = declaration && getNonlocalEffectiveTypeAnnotationNode(declaration) ? declaration : getDeclarationWithTypeAnnotation(symbol); if (declWithExistingAnnotation && !isFunctionLikeDeclaration(declWithExistingAnnotation) && !isGetAccessorDeclaration(declWithExistingAnnotation)) { // try to reuse the existing annotation const existing = getNonlocalEffectiveTypeAnnotationNode(declWithExistingAnnotation)!; // explicitly add `| undefined` to optional mapped properties whose type contains `undefined` (and not `missing`) const addUndefined = addUndefinedForParameter || !!(symbol.flags & SymbolFlags.Property && symbol.flags & SymbolFlags.Optional && isOptionalDeclaration(declWithExistingAnnotation) && (symbol as MappedSymbol).links?.mappedType && containsNonMissingUndefinedType(type)); const result = !isTypePredicateNode(existing) && tryReuseExistingTypeNode(context, existing, type, declWithExistingAnnotation, addUndefined); if (result) { restoreFlags(); return result; } } } if ( type.flags & TypeFlags.UniqueESSymbol && type.symbol === symbol && (!context.enclosingDeclaration || some(symbol.declarations, d => getSourceFileOfNode(d) === getSourceFileOfNode(context.enclosingDeclaration!))) ) { context.flags |= NodeBuilderFlags.AllowUniqueESSymbolType; } const decl = declaration ?? symbol.valueDeclaration ?? symbol.declarations?.[0]; const expr = decl && isDeclarationWithPossibleInnerTypeNodeReuse(decl) ? getPossibleTypeNodeReuseExpression(decl) : undefined; const result = expressionOrTypeToTypeNode(context, expr, type, addUndefinedForParameter); restoreFlags(); return result; } function typeNodeIsEquivalentToType(annotatedDeclaration: Node | undefined, type: Type, typeFromTypeNode: Type) { if (typeFromTypeNode === type) { return true; } if (annotatedDeclaration && (isParameter(annotatedDeclaration) || isPropertySignature(annotatedDeclaration) || isPropertyDeclaration(annotatedDeclaration)) && annotatedDeclaration.questionToken) { return getTypeWithFacts(type, TypeFacts.NEUndefined) === typeFromTypeNode; } return false; } function serializeReturnTypeForSignature(context: NodeBuilderContext, signature: Signature) { const suppressAny = context.flags & NodeBuilderFlags.SuppressAnyReturnType; const restoreFlags = saveRestoreFlags(context); if (suppressAny) context.flags &= ~NodeBuilderFlags.SuppressAnyReturnType; // suppress only toplevel `any`s let returnTypeNode: TypeNode | undefined; const returnType = getReturnTypeOfSignature(signature); if (returnType && !(suppressAny && isTypeAny(returnType))) { if (signature.declaration && !(context.internalFlags & InternalNodeBuilderFlags.NoSyntacticPrinter)) { syntacticNodeBuilder.serializeReturnTypeForSignature(signature.declaration, context); } context.internalFlags |= InternalNodeBuilderFlags.NoSyntacticPrinter; returnTypeNode = serializeReturnTypeForSignatureWorker(context, signature); } else if (!suppressAny) { returnTypeNode = factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); } restoreFlags(); return returnTypeNode; } function serializeReturnTypeForSignatureWorker(context: NodeBuilderContext, signature: Signature) { const typePredicate = getTypePredicateOfSignature(signature); const type = getReturnTypeOfSignature(signature); if (context.enclosingDeclaration && (!isErrorType(type) || (context.internalFlags & InternalNodeBuilderFlags.AllowUnresolvedNames)) && signature.declaration && !nodeIsSynthesized(signature.declaration)) { const annotation = getNonlocalEffectiveReturnTypeAnnotationNode(signature.declaration); if (annotation) { const result = tryReuseExistingTypeNode(context, annotation, type, context.enclosingDeclaration); if (result) { return result; } } } if (typePredicate) { return typePredicateToTypePredicateNodeHelper(typePredicate, context); } const expr = signature.declaration && getPossibleTypeNodeReuseExpression(signature.declaration); return expressionOrTypeToTypeNode(context, expr, type); } function trackExistingEntityName(node: T, context: NodeBuilderContext) { let introducesError = false; const leftmost = getFirstIdentifier(node); if (isInJSFile(node) && (isExportsIdentifier(leftmost) || isModuleExportsAccessExpression(leftmost.parent) || (isQualifiedName(leftmost.parent) && isModuleIdentifier(leftmost.parent.left) && isExportsIdentifier(leftmost.parent.right)))) { introducesError = true; return { introducesError, node }; } const meaning = getMeaningOfEntityNameReference(node); let sym: Symbol | undefined; if (isThisIdentifier(leftmost)) { // `this` isn't a bindable identifier - skip resolution, find a relevant `this` symbol directly and avoid exhaustive scope traversal sym = getSymbolOfDeclaration(getThisContainer(leftmost, /*includeArrowFunctions*/ false, /*includeClassComputedPropertyName*/ false)); if (isSymbolAccessible(sym, leftmost, meaning, /*shouldComputeAliasesToMakeVisible*/ false).accessibility !== SymbolAccessibility.Accessible) { introducesError = true; context.tracker.reportInaccessibleThisError(); } return { introducesError, node: attachSymbolToLeftmostIdentifier(node) as T }; } sym = resolveEntityName(leftmost, meaning, /*ignoreErrors*/ true, /*dontResolveAlias*/ true); if ( context.enclosingDeclaration && !(sym && sym.flags & SymbolFlags.TypeParameter) ) { sym = getExportSymbolOfValueSymbolIfExported(sym); // Some declarations may be transplanted to a new location. // When this happens we need to make sure that the name has the same meaning at both locations // We also check for the unknownSymbol because when we create a fake scope some parameters may actually not be usable // either because they are the expanded rest parameter, // or because they are the newly added parameters from the tuple, which might have different meanings in the original context const symAtLocation = resolveEntityName(leftmost, meaning, /*ignoreErrors*/ true, /*dontResolveAlias*/ true, context.enclosingDeclaration); if ( // Check for unusable parameters symbols symAtLocation === unknownSymbol || // If the symbol is not found, but was not found in the original scope either we probably have an error, don't reuse the node (symAtLocation === undefined && sym !== undefined) || // If the symbol is found both in declaration scope and in current scope then it shoudl point to the same reference (symAtLocation && sym && !getSymbolIfSameReference(getExportSymbolOfValueSymbolIfExported(symAtLocation), sym)) ) { // In isolated declaration we will not do rest parameter expansion so there is no need to report on these. if (symAtLocation !== unknownSymbol) { context.tracker.reportInferenceFallback(node); } introducesError = true; return { introducesError, node, sym }; } else { sym = symAtLocation; } } if (sym) { // If a parameter is resolvable in the current context it is also visible, so no need to go to symbol accesibility if ( sym.flags & SymbolFlags.FunctionScopedVariable && sym.valueDeclaration ) { if (isPartOfParameterDeclaration(sym.valueDeclaration) || isJSDocParameterTag(sym.valueDeclaration)) { return { introducesError, node: attachSymbolToLeftmostIdentifier(node) as T }; } } if ( !(sym.flags & SymbolFlags.TypeParameter) && // Type parameters are visible in the current context if they are are resolvable !isDeclarationName(node) && isSymbolAccessible(sym, context.enclosingDeclaration, meaning, /*shouldComputeAliasesToMakeVisible*/ false).accessibility !== SymbolAccessibility.Accessible ) { context.tracker.reportInferenceFallback(node); introducesError = true; } else { context.tracker.trackSymbol(sym, context.enclosingDeclaration, meaning); } return { introducesError, node: attachSymbolToLeftmostIdentifier(node) as T }; } return { introducesError, node }; /** * Attaches a `.symbol` member to an identifier, cloning it to do so, so symbol information * is smuggled out for symbol display information. */ function attachSymbolToLeftmostIdentifier(node: Node): Node { if (node === leftmost) { const type = getDeclaredTypeOfSymbol(sym!); const name = sym!.flags & SymbolFlags.TypeParameter ? typeParameterToName(type, context) : factory.cloneNode(node as Identifier); name.symbol = sym!; // for quickinfo, which uses identifier symbol information return setTextRange(context, setEmitFlags(name, EmitFlags.NoAsciiEscaping), node); } const updated = visitEachChildWorker(node, c => attachSymbolToLeftmostIdentifier(c), /*context*/ undefined); if (updated !== node) { setTextRange(context, updated, node); } return updated; } } function serializeTypeName(context: NodeBuilderContext, node: EntityName, isTypeOf?: boolean, typeArguments?: readonly TypeNode[]) { const meaning = isTypeOf ? SymbolFlags.Value : SymbolFlags.Type; const symbol = resolveEntityName(node, meaning, /*ignoreErrors*/ true); if (!symbol) return undefined; const resolvedSymbol = symbol.flags & SymbolFlags.Alias ? resolveAlias(symbol) : symbol; if (isSymbolAccessible(symbol, context.enclosingDeclaration, meaning, /*shouldComputeAliasesToMakeVisible*/ false).accessibility !== SymbolAccessibility.Accessible) return undefined; return symbolToTypeNode(resolvedSymbol, context, meaning, typeArguments); } function canReuseTypeNode(context: NodeBuilderContext, existing: TypeNode) { if (isInJSFile(existing)) { if (isLiteralImportTypeNode(existing)) { // Ensure resolvedSymbol is present void getTypeFromImportTypeNode(existing); const nodeSymbol = getNodeLinks(existing).resolvedSymbol; return ( !nodeSymbol || !( // The import type resolved using jsdoc fallback logic (!existing.isTypeOf && !(nodeSymbol.flags & SymbolFlags.Type)) || // The import type had type arguments autofilled by js fallback logic !(length(existing.typeArguments) >= getMinTypeArgumentCount(getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(nodeSymbol))) ) ); } } if (isThisTypeNode(existing)) { if (context.mapper === undefined) return true; const type = getTypeFromTypeNode(context, existing, /*noMappedTypes*/ true); return !!type; } if (isTypeReferenceNode(existing)) { if (isConstTypeReference(existing)) return false; const type = getTypeFromTypeReference(existing); const symbol = getNodeLinks(existing).resolvedSymbol; if (!symbol) return false; if (symbol.flags & SymbolFlags.TypeParameter) { const type = getDeclaredTypeOfSymbol(symbol); if (context.mapper && getMappedType(type, context.mapper) !== type) { return false; } } if (isInJSDoc(existing)) { return existingTypeNodeIsNotReferenceOrIsReferenceWithCompatibleTypeArgumentCount(existing, type) && !getIntendedTypeFromJSDocTypeReference(existing) // We should probably allow the reuse of JSDoc reference types such as String Number etc && (symbol.flags & SymbolFlags.Type); // JSDoc type annotations can reference values (meaning typeof value) as well as types. We only reuse type nodes } } if ( isTypeOperatorNode(existing) && existing.operator === SyntaxKind.UniqueKeyword && existing.type.kind === SyntaxKind.SymbolKeyword ) { const effectiveEnclosingContext = context.enclosingDeclaration && getEnclosingDeclarationIgnoringFakeScope(context.enclosingDeclaration); return !!findAncestor(existing, n => n === effectiveEnclosingContext); } return true; } function serializeExistingTypeNode(context: NodeBuilderContext, typeNode: TypeNode) { const type = getTypeFromTypeNode(context, typeNode); return typeToTypeNodeHelper(type, context); } /** * Do you mean to call this directly? You probably should use `tryReuseExistingTypeNode` instead, * which performs sanity checking on the type before doing this. */ function tryReuseExistingTypeNodeHelper(context: NodeBuilderContext, existing: TypeNode) { if (cancellationToken && cancellationToken.throwIfCancellationRequested) { cancellationToken.throwIfCancellationRequested(); } let hadError = false; const { finalizeBoundary, startRecoveryScope } = createRecoveryBoundary(); const transformed = visitNode(existing, visitExistingNodeTreeSymbols, isTypeNode); if (!finalizeBoundary()) { return undefined; } context.approximateLength += existing.end - existing.pos; return transformed; function visitExistingNodeTreeSymbols(node: Node): Node | undefined { // If there was an error in a sibling node bail early, the result will be discarded anyway if (hadError) return node; const recover = startRecoveryScope(); const onExitNewScope = isNewScopeNode(node) ? onEnterNewScope(node) : undefined; const result = visitExistingNodeTreeSymbolsWorker(node); onExitNewScope?.(); // If there was an error, maybe we can recover by serializing the actual type of the node if (hadError) { if (isTypeNode(node) && !isTypePredicateNode(node)) { recover(); return serializeExistingTypeNode(context, node); } return node; } // We want to clone the subtree, so when we mark it up with __pos and __end in quickfixes, // we don't get odd behavior because of reused nodes. We also need to clone to _remove_ // the position information if the node comes from a different file than the one the node builder // is set to build for (even though we are reusing the node structure, the position information // would make the printer print invalid spans for literals and identifiers, and the formatter would // choke on the mismatched positonal spans between a parent and an injected child from another file). return result ? setTextRange(context, result, node) : undefined; } function createRecoveryBoundary() { let trackedSymbols: TrackedSymbol[]; let unreportedErrors: (() => void)[]; const oldTracker = context.tracker; const oldTrackedSymbols = context.trackedSymbols; context.trackedSymbols = undefined; const oldEncounteredError = context.encounteredError; context.tracker = new SymbolTrackerImpl(context, { ...oldTracker.inner, reportCyclicStructureError() { markError(() => oldTracker.reportCyclicStructureError()); }, reportInaccessibleThisError() { markError(() => oldTracker.reportInaccessibleThisError()); }, reportInaccessibleUniqueSymbolError() { markError(() => oldTracker.reportInaccessibleUniqueSymbolError()); }, reportLikelyUnsafeImportRequiredError(specifier) { markError(() => oldTracker.reportLikelyUnsafeImportRequiredError(specifier)); }, reportNonSerializableProperty(name) { markError(() => oldTracker.reportNonSerializableProperty(name)); }, trackSymbol(sym, decl, meaning) { (trackedSymbols ??= []).push([sym, decl, meaning]); return false; }, moduleResolverHost: context.tracker.moduleResolverHost, }, context.tracker.moduleResolverHost); return { startRecoveryScope, finalizeBoundary, }; function markError(unreportedError: () => void) { hadError = true; (unreportedErrors ??= []).push(unreportedError); } function startRecoveryScope() { const trackedSymbolsTop = trackedSymbols?.length ?? 0; const unreportedErrorsTop = unreportedErrors?.length ?? 0; return () => { hadError = false; // Reset the tracked symbols to before the error if (trackedSymbols) { trackedSymbols.length = trackedSymbolsTop; } if (unreportedErrors) { unreportedErrors.length = unreportedErrorsTop; } }; } function finalizeBoundary() { context.tracker = oldTracker; context.trackedSymbols = oldTrackedSymbols; context.encounteredError = oldEncounteredError; unreportedErrors?.forEach(fn => fn()); if (hadError) { return false; } trackedSymbols?.forEach( ([symbol, enclosingDeclaration, meaning]) => context.tracker.trackSymbol( symbol, enclosingDeclaration, meaning, ), ); return true; } } function onEnterNewScope(node: IntroducesNewScopeNode | ConditionalTypeNode) { return enterNewScope(context, node, getParametersInScope(node), getTypeParametersInScope(node)); } function tryVisitSimpleTypeNode(node: TypeNode): TypeNode | undefined { const innerNode = skipTypeParentheses(node); switch (innerNode.kind) { case SyntaxKind.TypeReference: return tryVisitTypeReference(innerNode as TypeReferenceNode); case SyntaxKind.TypeQuery: return tryVisitTypeQuery(innerNode as TypeQueryNode); case SyntaxKind.IndexedAccessType: return tryVisitIndexedAccess(innerNode as IndexedAccessTypeNode); case SyntaxKind.TypeOperator: const typeOperatorNode = innerNode as TypeOperatorNode; if (typeOperatorNode.operator === SyntaxKind.KeyOfKeyword) { return tryVisitKeyOf(typeOperatorNode); } } return visitNode(node, visitExistingNodeTreeSymbols, isTypeNode); } function tryVisitIndexedAccess(node: IndexedAccessTypeNode): TypeNode | undefined { const resultObjectType = tryVisitSimpleTypeNode(node.objectType); if (resultObjectType === undefined) { return undefined; } return factory.updateIndexedAccessTypeNode(node, resultObjectType, visitNode(node.indexType, visitExistingNodeTreeSymbols, isTypeNode)!); } function tryVisitKeyOf(node: TypeOperatorNode): TypeNode | undefined { Debug.assertEqual(node.operator, SyntaxKind.KeyOfKeyword); const type = tryVisitSimpleTypeNode(node.type); if (type === undefined) { return undefined; } return factory.updateTypeOperatorNode(node, type); } function tryVisitTypeQuery(node: TypeQueryNode): TypeNode | undefined { const { introducesError, node: exprName } = trackExistingEntityName(node.exprName, context); if (!introducesError) { return factory.updateTypeQueryNode( node, exprName, visitNodes(node.typeArguments, visitExistingNodeTreeSymbols, isTypeNode), ); } const serializedName = serializeTypeName(context, node.exprName, /*isTypeOf*/ true); if (serializedName) { return setTextRange(context, serializedName, node.exprName); } } function tryVisitTypeReference(node: TypeReferenceNode): TypeNode | undefined { if (canReuseTypeNode(context, node)) { const { introducesError, node: newName } = trackExistingEntityName(node.typeName, context); const typeArguments = visitNodes(node.typeArguments, visitExistingNodeTreeSymbols, isTypeNode); if (!introducesError) { const updated = factory.updateTypeReferenceNode( node, newName, typeArguments, ); return setTextRange(context, updated, node); } else { const serializedName = serializeTypeName(context, node.typeName, /*isTypeOf*/ false, typeArguments); if (serializedName) { return setTextRange(context, serializedName, node.typeName); } } } } function visitExistingNodeTreeSymbolsWorker(node: Node): Node | undefined { if (isJSDocTypeExpression(node)) { // Unwrap JSDocTypeExpressions return visitNode(node.type, visitExistingNodeTreeSymbols, isTypeNode); } // We don't _actually_ support jsdoc namepath types, emit `any` instead if (isJSDocAllType(node) || node.kind === SyntaxKind.JSDocNamepathType) { return factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); } if (isJSDocUnknownType(node)) { return factory.createKeywordTypeNode(SyntaxKind.UnknownKeyword); } if (isJSDocNullableType(node)) { return factory.createUnionTypeNode([visitNode(node.type, visitExistingNodeTreeSymbols, isTypeNode)!, factory.createLiteralTypeNode(factory.createNull())]); } if (isJSDocOptionalType(node)) { return factory.createUnionTypeNode([visitNode(node.type, visitExistingNodeTreeSymbols, isTypeNode)!, factory.createKeywordTypeNode(SyntaxKind.UndefinedKeyword)]); } if (isJSDocNonNullableType(node)) { return visitNode(node.type, visitExistingNodeTreeSymbols); } if (isJSDocVariadicType(node)) { return factory.createArrayTypeNode(visitNode(node.type, visitExistingNodeTreeSymbols, isTypeNode)!); } if (isJSDocTypeLiteral(node)) { return factory.createTypeLiteralNode(map(node.jsDocPropertyTags, t => { const name = visitNode(isIdentifier(t.name) ? t.name : t.name.right, visitExistingNodeTreeSymbols, isIdentifier)!; const typeViaParent = getTypeOfPropertyOfType(getTypeFromTypeNode(context, node), name.escapedText); const overrideTypeNode = typeViaParent && t.typeExpression && getTypeFromTypeNode(context, t.typeExpression.type) !== typeViaParent ? typeToTypeNodeHelper(typeViaParent, context) : undefined; return factory.createPropertySignature( /*modifiers*/ undefined, name, t.isBracketed || t.typeExpression && isJSDocOptionalType(t.typeExpression.type) ? factory.createToken(SyntaxKind.QuestionToken) : undefined, overrideTypeNode || (t.typeExpression && visitNode(t.typeExpression.type, visitExistingNodeTreeSymbols, isTypeNode)) || factory.createKeywordTypeNode(SyntaxKind.AnyKeyword), ); })); } if (isTypeReferenceNode(node) && isIdentifier(node.typeName) && node.typeName.escapedText === "") { return setOriginalNode(factory.createKeywordTypeNode(SyntaxKind.AnyKeyword), node); } if ((isExpressionWithTypeArguments(node) || isTypeReferenceNode(node)) && isJSDocIndexSignature(node)) { return factory.createTypeLiteralNode([factory.createIndexSignature( /*modifiers*/ undefined, [factory.createParameterDeclaration( /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, "x", /*questionToken*/ undefined, visitNode(node.typeArguments![0], visitExistingNodeTreeSymbols, isTypeNode), )], visitNode(node.typeArguments![1], visitExistingNodeTreeSymbols, isTypeNode), )]); } if (isJSDocFunctionType(node)) { if (isJSDocConstructSignature(node)) { let newTypeNode: TypeNode | undefined; return factory.createConstructorTypeNode( /*modifiers*/ undefined, visitNodes(node.typeParameters, visitExistingNodeTreeSymbols, isTypeParameterDeclaration), mapDefined(node.parameters, (p, i) => p.name && isIdentifier(p.name) && p.name.escapedText === "new" ? (newTypeNode = p.type, undefined) : factory.createParameterDeclaration( /*modifiers*/ undefined, getEffectiveDotDotDotForParameter(p), setTextRange(context, factory.createIdentifier(getNameForJSDocFunctionParameter(p, i)), p), factory.cloneNode(p.questionToken), visitNode(p.type, visitExistingNodeTreeSymbols, isTypeNode), /*initializer*/ undefined, )), visitNode(newTypeNode || node.type, visitExistingNodeTreeSymbols, isTypeNode) || factory.createKeywordTypeNode(SyntaxKind.AnyKeyword), ); } else { return factory.createFunctionTypeNode( visitNodes(node.typeParameters, visitExistingNodeTreeSymbols, isTypeParameterDeclaration), map(node.parameters, (p, i) => factory.createParameterDeclaration( /*modifiers*/ undefined, getEffectiveDotDotDotForParameter(p), setTextRange(context, factory.createIdentifier(getNameForJSDocFunctionParameter(p, i)), p), factory.cloneNode(p.questionToken), visitNode(p.type, visitExistingNodeTreeSymbols, isTypeNode), /*initializer*/ undefined, )), visitNode(node.type, visitExistingNodeTreeSymbols, isTypeNode) || factory.createKeywordTypeNode(SyntaxKind.AnyKeyword), ); } } if (isThisTypeNode(node)) { if (canReuseTypeNode(context, node)) { return node; } hadError = true; return node; } if (isTypeParameterDeclaration(node)) { return factory.updateTypeParameterDeclaration( node, visitNodes(node.modifiers, visitExistingNodeTreeSymbols, isModifier), setTextRange(context, typeParameterToName(getDeclaredTypeOfSymbol(getSymbolOfDeclaration(node)), context), node), visitNode(node.constraint, visitExistingNodeTreeSymbols, isTypeNode), visitNode(node.default, visitExistingNodeTreeSymbols, isTypeNode), ); } if (isIndexedAccessTypeNode(node)) { const result = tryVisitIndexedAccess(node); if (!result) { hadError = true; return node; } return result; } if (isTypeReferenceNode(node)) { const result = tryVisitTypeReference(node); if (result) { return result; } hadError = true; return node; } if (isLiteralImportTypeNode(node)) { const nodeSymbol = getNodeLinks(node).resolvedSymbol; if ( isInJSDoc(node) && nodeSymbol && ( // The import type resolved using jsdoc fallback logic (!node.isTypeOf && !(nodeSymbol.flags & SymbolFlags.Type)) || // The import type had type arguments autofilled by js fallback logic !(length(node.typeArguments) >= getMinTypeArgumentCount(getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(nodeSymbol))) ) ) { return setTextRange(context, typeToTypeNodeHelper(getTypeFromTypeNode(context, node), context), node); } return factory.updateImportTypeNode( node, factory.updateLiteralTypeNode(node.argument, rewriteModuleSpecifier(node, node.argument.literal)), visitNode(node.attributes, visitExistingNodeTreeSymbols, isImportAttributes), visitNode(node.qualifier, visitExistingNodeTreeSymbols, isEntityName), visitNodes(node.typeArguments, visitExistingNodeTreeSymbols, isTypeNode), node.isTypeOf, ); } if (isNamedDeclaration(node) && node.name.kind === SyntaxKind.ComputedPropertyName && !isLateBindableName(node.name)) { if (!hasDynamicName(node)) { return visitEachChild(node, visitExistingNodeTreeSymbols); } if (!(context.internalFlags & InternalNodeBuilderFlags.AllowUnresolvedNames && isEntityNameExpression(node.name.expression) && checkComputedPropertyName(node.name).flags & TypeFlags.Any)) { return undefined; } } if ( (isFunctionLike(node) && !node.type) || (isPropertyDeclaration(node) && !node.type && !node.initializer) || (isPropertySignature(node) && !node.type && !node.initializer) || (isParameter(node) && !node.type && !node.initializer) ) { let visited = visitEachChild(node, visitExistingNodeTreeSymbols); if (visited === node) { visited = setTextRange(context, factory.cloneNode(node), node); } (visited as Mutable).type = factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); if (isParameter(node)) { (visited as Mutable).modifiers = undefined; } return visited; } if (isTypeQueryNode(node)) { const result = tryVisitTypeQuery(node); if (!result) { hadError = true; return node; } return result; } if (isComputedPropertyName(node) && isEntityNameExpression(node.expression)) { const { node: result, introducesError } = trackExistingEntityName(node.expression, context); if (!introducesError) { return factory.updateComputedPropertyName(node, result); } else { const type = getWidenedType(getRegularTypeOfExpression(node.expression)); const computedPropertyNameType = typeToTypeNodeHelper(type, context); let literal; if (isLiteralTypeNode(computedPropertyNameType)) { literal = computedPropertyNameType.literal; } else { const evaluated = evaluateEntityNameExpression(node.expression); const literalNode = typeof evaluated.value === "string" ? factory.createStringLiteral(evaluated.value, /*isSingleQuote*/ undefined) : typeof evaluated.value === "number" ? factory.createNumericLiteral(evaluated.value, /*numericLiteralFlags*/ 0) : undefined; if (!literalNode) { if (isImportTypeNode(computedPropertyNameType)) { trackComputedName(node.expression, context.enclosingDeclaration, context); } return node; } literal = literalNode; } if (literal.kind === SyntaxKind.StringLiteral && isIdentifierText(literal.text, getEmitScriptTarget(compilerOptions))) { return factory.createIdentifier(literal.text); } if (literal.kind === SyntaxKind.NumericLiteral && !literal.text.startsWith("-")) { return literal; } return factory.updateComputedPropertyName(node, literal); } } if (isTypePredicateNode(node)) { let parameterName; if (isIdentifier(node.parameterName)) { const { node: result, introducesError } = trackExistingEntityName(node.parameterName, context); // Should not usually happen the only case is when a type predicate comes from a JSDoc type annotation with it's own parameter symbol definition. // /** @type {(v: unknown) => v is undefined} */ // const isUndef = v => v === undefined; hadError = hadError || introducesError; parameterName = result; } else { parameterName = factory.cloneNode(node.parameterName); } return factory.updateTypePredicateNode(node, factory.cloneNode(node.assertsModifier), parameterName, visitNode(node.type, visitExistingNodeTreeSymbols, isTypeNode)); } if (isTupleTypeNode(node) || isTypeLiteralNode(node) || isMappedTypeNode(node)) { const visited = visitEachChild(node, visitExistingNodeTreeSymbols); const clone = setTextRange(context, visited === node ? factory.cloneNode(node) : visited, node); const flags = getEmitFlags(clone); setEmitFlags(clone, flags | (context.flags & NodeBuilderFlags.MultilineObjectLiterals && isTypeLiteralNode(node) ? 0 : EmitFlags.SingleLine)); return clone; } if (isStringLiteral(node) && !!(context.flags & NodeBuilderFlags.UseSingleQuotesForStringLiteralType) && !node.singleQuote) { const clone = factory.cloneNode(node); (clone as Mutable).singleQuote = true; return clone; } if (isConditionalTypeNode(node)) { const checkType = visitNode(node.checkType, visitExistingNodeTreeSymbols, isTypeNode)!; const disposeScope = onEnterNewScope(node); const extendType = visitNode(node.extendsType, visitExistingNodeTreeSymbols, isTypeNode)!; const trueType = visitNode(node.trueType, visitExistingNodeTreeSymbols, isTypeNode)!; disposeScope(); const falseType = visitNode(node.falseType, visitExistingNodeTreeSymbols, isTypeNode)!; return factory.updateConditionalTypeNode( node, checkType, extendType, trueType, falseType, ); } if (isTypeOperatorNode(node)) { if (node.operator === SyntaxKind.UniqueKeyword && node.type.kind === SyntaxKind.SymbolKeyword) { if (!canReuseTypeNode(context, node)) { hadError = true; return node; } } else if (node.operator === SyntaxKind.KeyOfKeyword) { const result = tryVisitKeyOf(node); if (!result) { hadError = true; return node; } return result; } } return visitEachChild(node, visitExistingNodeTreeSymbols); function visitEachChild(node: T, visitor: Visitor): T; function visitEachChild(node: T | undefined, visitor: Visitor): T | undefined; function visitEachChild(node: T | undefined, visitor: Visitor): T | undefined { const nonlocalNode = !context.enclosingFile || context.enclosingFile !== getSourceFileOfNode(node); return visitEachChildWorker(node, visitor, /*context*/ undefined, nonlocalNode ? visitNodesWithoutCopyingPositions : undefined); } function visitNodesWithoutCopyingPositions( nodes: NodeArray | undefined, visitor: Visitor, test?: (node: Node) => boolean, start?: number, count?: number, ): NodeArray | undefined { let result = visitNodes(nodes, visitor, test, start, count); if (result) { if (result.pos !== -1 || result.end !== -1) { if (result === nodes) { result = factory.createNodeArray(nodes.slice(), nodes.hasTrailingComma); } setTextRangePosEnd(result, -1, -1); } } return result; } function getEffectiveDotDotDotForParameter(p: ParameterDeclaration) { return p.dotDotDotToken || (p.type && isJSDocVariadicType(p.type) ? factory.createToken(SyntaxKind.DotDotDotToken) : undefined); } /** Note that `new:T` parameters are not handled, but should be before calling this function. */ function getNameForJSDocFunctionParameter(p: ParameterDeclaration, index: number) { return p.name && isIdentifier(p.name) && p.name.escapedText === "this" ? "this" : getEffectiveDotDotDotForParameter(p) ? `args` : `arg${index}`; } function rewriteModuleSpecifier(parent: ImportTypeNode, lit: StringLiteral) { if (context.bundled || context.enclosingFile !== getSourceFileOfNode(lit)) { let name = lit.text; const nodeSymbol = getNodeLinks(node).resolvedSymbol; const meaning = parent.isTypeOf ? SymbolFlags.Value : SymbolFlags.Type; const parentSymbol = nodeSymbol && isSymbolAccessible(nodeSymbol, context.enclosingDeclaration, meaning, /*shouldComputeAliasesToMakeVisible*/ false).accessibility === SymbolAccessibility.Accessible && lookupSymbolChain(nodeSymbol, context, meaning, /*yieldModuleSymbol*/ true)[0]; if (parentSymbol && isExternalModuleSymbol(parentSymbol)) { name = getSpecifierForModuleSymbol(parentSymbol, context); } else { const targetFile = getExternalModuleFileFromDeclaration(parent); if (targetFile) { name = getSpecifierForModuleSymbol(targetFile.symbol, context); } } if (name.includes("/node_modules/")) { context.encounteredError = true; if (context.tracker.reportLikelyUnsafeImportRequiredError) { context.tracker.reportLikelyUnsafeImportRequiredError(name); } } if (name !== lit.text) { return setOriginalNode(factory.createStringLiteral(name), lit); } } return visitNode(lit, visitExistingNodeTreeSymbols, isStringLiteral)!; } } } function symbolTableToDeclarationStatements(symbolTable: SymbolTable, context: NodeBuilderContext): Statement[] { const serializePropertySymbolForClass = makeSerializePropertySymbol(factory.createPropertyDeclaration, SyntaxKind.MethodDeclaration, /*useAccessors*/ true); const serializePropertySymbolForInterfaceWorker = makeSerializePropertySymbol((mods, name, question, type) => factory.createPropertySignature(mods, name, question, type), SyntaxKind.MethodSignature, /*useAccessors*/ false); // TODO: Use `setOriginalNode` on original declaration names where possible so these declarations see some kind of // declaration mapping // We save the enclosing declaration off here so it's not adjusted by well-meaning declaration // emit codepaths which want to apply more specific contexts (so we can still refer to the root real declaration // we're trying to emit from later on) const enclosingDeclaration = context.enclosingDeclaration!; let results: Statement[] = []; const visitedSymbols = new Set(); const deferredPrivatesStack: Map[] = []; const oldcontext = context; context = { ...oldcontext, usedSymbolNames: new Set(oldcontext.usedSymbolNames), remappedSymbolNames: new Map(), remappedSymbolReferences: new Map(oldcontext.remappedSymbolReferences?.entries()), tracker: undefined!, }; const tracker: SymbolTracker = { ...oldcontext.tracker.inner, trackSymbol: (sym, decl, meaning) => { if (context.remappedSymbolNames?.has(getSymbolId(sym))) return false; // If the context has a remapped name for the symbol, it *should* mean it's been made visible const accessibleResult = isSymbolAccessible(sym, decl, meaning, /*shouldComputeAliasesToMakeVisible*/ false); if (accessibleResult.accessibility === SymbolAccessibility.Accessible) { // Lookup the root symbol of the chain of refs we'll use to access it and serialize it const chain = lookupSymbolChainWorker(sym, context, meaning); if (!(sym.flags & SymbolFlags.Property)) { // Only include referenced privates in the same file. Weird JS aliases may expose privates // from other files - assume JS transforms will make those available via expected means const root = chain[0]; const contextFile = getSourceFileOfNode(oldcontext.enclosingDeclaration); if (some(root.declarations, d => getSourceFileOfNode(d) === contextFile)) { includePrivateSymbol(root); } } } else if (oldcontext.tracker.inner?.trackSymbol) { return oldcontext.tracker.inner.trackSymbol(sym, decl, meaning); } return false; }, }; context.tracker = new SymbolTrackerImpl(context, tracker, oldcontext.tracker.moduleResolverHost); forEachEntry(symbolTable, (symbol, name) => { const baseName = unescapeLeadingUnderscores(name); void getInternalSymbolName(symbol, baseName); // Called to cache values into `usedSymbolNames` and `remappedSymbolNames` }); let addingDeclare = !context.bundled; const exportEquals = symbolTable.get(InternalSymbolName.ExportEquals); if (exportEquals && symbolTable.size > 1 && exportEquals.flags & (SymbolFlags.Alias | SymbolFlags.Module)) { symbolTable = createSymbolTable(); // Remove extraneous elements from root symbol table (they'll be mixed back in when the target of the `export=` is looked up) symbolTable.set(InternalSymbolName.ExportEquals, exportEquals); } visitSymbolTable(symbolTable); return mergeRedundantStatements(results); function isIdentifierAndNotUndefined(node: Node | undefined): node is Identifier { return !!node && node.kind === SyntaxKind.Identifier; } function getNamesOfDeclaration(statement: Statement): Identifier[] { if (isVariableStatement(statement)) { return filter(map(statement.declarationList.declarations, getNameOfDeclaration), isIdentifierAndNotUndefined); } return filter([getNameOfDeclaration(statement as DeclarationStatement)], isIdentifierAndNotUndefined); } function flattenExportAssignedNamespace(statements: Statement[]) { const exportAssignment = find(statements, isExportAssignment); const nsIndex = findIndex(statements, isModuleDeclaration); let ns = nsIndex !== -1 ? statements[nsIndex] as ModuleDeclaration : undefined; if ( ns && exportAssignment && exportAssignment.isExportEquals && isIdentifier(exportAssignment.expression) && isIdentifier(ns.name) && idText(ns.name) === idText(exportAssignment.expression) && ns.body && isModuleBlock(ns.body) ) { // Pass 0: Correct situations where a module has both an `export = ns` and multiple top-level exports by stripping the export modifiers from // the top-level exports and exporting them in the targeted ns, as can occur when a js file has both typedefs and `module.export` assignments const excessExports = filter(statements, s => !!(getEffectiveModifierFlags(s) & ModifierFlags.Export)); const name = ns.name; let body = ns.body; if (length(excessExports)) { ns = factory.updateModuleDeclaration( ns, ns.modifiers, ns.name, body = factory.updateModuleBlock( body, factory.createNodeArray([ ...ns.body.statements, factory.createExportDeclaration( /*modifiers*/ undefined, /*isTypeOnly*/ false, factory.createNamedExports(map(flatMap(excessExports, e => getNamesOfDeclaration(e)), id => factory.createExportSpecifier(/*isTypeOnly*/ false, /*propertyName*/ undefined, id))), /*moduleSpecifier*/ undefined, ), ]), ), ); statements = [...statements.slice(0, nsIndex), ns, ...statements.slice(nsIndex + 1)]; } // Pass 1: Flatten `export namespace _exports {} export = _exports;` so long as the `export=` only points at a single namespace declaration if (!find(statements, s => s !== ns && nodeHasName(s, name))) { results = []; // If the namespace contains no export assignments or declarations, and no declarations flagged with `export`, then _everything_ is exported - // to respect this as the top level, we need to add an `export` modifier to everything const mixinExportFlag = !some(body.statements, s => hasSyntacticModifier(s, ModifierFlags.Export) || isExportAssignment(s) || isExportDeclaration(s)); forEach(body.statements, s => { addResult(s, mixinExportFlag ? ModifierFlags.Export : ModifierFlags.None); // Recalculates the ambient (and export, if applicable from above) flag }); statements = [...filter(statements, s => s !== ns && s !== exportAssignment), ...results]; } } return statements; } function mergeExportDeclarations(statements: Statement[]) { // Pass 2: Combine all `export {}` declarations const exports = filter(statements, d => isExportDeclaration(d) && !d.moduleSpecifier && !!d.exportClause && isNamedExports(d.exportClause)) as ExportDeclaration[]; if (length(exports) > 1) { const nonExports = filter(statements, d => !isExportDeclaration(d) || !!d.moduleSpecifier || !d.exportClause); statements = [ ...nonExports, factory.createExportDeclaration( /*modifiers*/ undefined, /*isTypeOnly*/ false, factory.createNamedExports(flatMap(exports, e => cast(e.exportClause, isNamedExports).elements)), /*moduleSpecifier*/ undefined, ), ]; } // Pass 2b: Also combine all `export {} from "..."` declarations as needed const reexports = filter(statements, d => isExportDeclaration(d) && !!d.moduleSpecifier && !!d.exportClause && isNamedExports(d.exportClause)) as ExportDeclaration[]; if (length(reexports) > 1) { const groups = group(reexports, decl => isStringLiteral(decl.moduleSpecifier!) ? ">" + decl.moduleSpecifier.text : ">"); if (groups.length !== reexports.length) { for (const group of groups) { if (group.length > 1) { // remove group members from statements and then merge group members and add back to statements statements = [ ...filter(statements, s => !group.includes(s as ExportDeclaration)), factory.createExportDeclaration( /*modifiers*/ undefined, /*isTypeOnly*/ false, factory.createNamedExports(flatMap(group, e => cast(e.exportClause, isNamedExports).elements)), group[0].moduleSpecifier, ), ]; } } } } return statements; } function inlineExportModifiers(statements: Statement[]) { // Pass 3: Move all `export {}`'s to `export` modifiers where possible const index = findIndex(statements, d => isExportDeclaration(d) && !d.moduleSpecifier && !d.attributes && !!d.exportClause && isNamedExports(d.exportClause)); if (index >= 0) { const exportDecl = statements[index] as ExportDeclaration & { readonly exportClause: NamedExports; }; const replacements = mapDefined(exportDecl.exportClause.elements, e => { if (!e.propertyName && e.name.kind !== SyntaxKind.StringLiteral) { // export {name} - look thru `statements` for `name`, and if all results can take an `export` modifier, do so and filter it const name = e.name; const indices = indicesOf(statements); const associatedIndices = filter(indices, i => nodeHasName(statements[i], name)); if (length(associatedIndices) && every(associatedIndices, i => canHaveExportModifier(statements[i]))) { for (const index of associatedIndices) { statements[index] = addExportModifier(statements[index] as Extract); } return undefined; } } return e; }); if (!length(replacements)) { // all clauses removed, remove the export declaration orderedRemoveItemAt(statements, index); } else { // some items filtered, others not - update the export declaration statements[index] = factory.updateExportDeclaration( exportDecl, exportDecl.modifiers, exportDecl.isTypeOnly, factory.updateNamedExports( exportDecl.exportClause, replacements, ), exportDecl.moduleSpecifier, exportDecl.attributes, ); } } return statements; } function mergeRedundantStatements(statements: Statement[]) { statements = flattenExportAssignedNamespace(statements); statements = mergeExportDeclarations(statements); statements = inlineExportModifiers(statements); // Not a cleanup, but as a final step: If there is a mix of `export` and non-`export` declarations, but no `export =` or `export {}` add a `export {};` so // declaration privacy is respected. if ( enclosingDeclaration && ((isSourceFile(enclosingDeclaration) && isExternalOrCommonJsModule(enclosingDeclaration)) || isModuleDeclaration(enclosingDeclaration)) && (!some(statements, isExternalModuleIndicator) || (!hasScopeMarker(statements) && some(statements, needsScopeMarker))) ) { statements.push(createEmptyExports(factory)); } return statements; } function addExportModifier(node: Extract) { const flags = (getEffectiveModifierFlags(node) | ModifierFlags.Export) & ~ModifierFlags.Ambient; return factory.replaceModifiers(node, flags); } function removeExportModifier(node: Extract) { const flags = getEffectiveModifierFlags(node) & ~ModifierFlags.Export; return factory.replaceModifiers(node, flags); } function visitSymbolTable(symbolTable: SymbolTable, suppressNewPrivateContext?: boolean, propertyAsAlias?: boolean) { if (!suppressNewPrivateContext) { deferredPrivatesStack.push(new Map()); } symbolTable.forEach((symbol: Symbol) => { serializeSymbol(symbol, /*isPrivate*/ false, !!propertyAsAlias); }); if (!suppressNewPrivateContext) { // deferredPrivates will be filled up by visiting the symbol table // And will continue to iterate as elements are added while visited `deferredPrivates` // (As that's how a map iterator is defined to work) deferredPrivatesStack[deferredPrivatesStack.length - 1].forEach((symbol: Symbol) => { serializeSymbol(symbol, /*isPrivate*/ true, !!propertyAsAlias); }); deferredPrivatesStack.pop(); } } function serializeSymbol(symbol: Symbol, isPrivate: boolean, propertyAsAlias: boolean): void { void getPropertiesOfType(getTypeOfSymbol(symbol)); // resolve symbol's type and properties, which should trigger any required merges // cache visited list based on merged symbol, since we want to use the unmerged top-level symbol, but // still skip reserializing it if we encounter the merged product later on const visitedSym = getMergedSymbol(symbol); if (visitedSymbols.has(getSymbolId(visitedSym))) { return; // Already printed } visitedSymbols.add(getSymbolId(visitedSym)); // Only actually serialize symbols within the correct enclosing declaration, otherwise do nothing with the out-of-context symbol const skipMembershipCheck = !isPrivate; // We only call this on exported symbols when we know they're in the correct scope if (skipMembershipCheck || (!!length(symbol.declarations) && some(symbol.declarations, d => !!findAncestor(d, n => n === enclosingDeclaration)))) { const scopeCleanup = cloneNodeBuilderContext(context); serializeSymbolWorker(symbol, isPrivate, propertyAsAlias); scopeCleanup(); } } // Synthesize declarations for a symbol - might be an Interface, a Class, a Namespace, a Type, a Variable (const, let, or var), an Alias // or a merge of some number of those. // An interesting challenge is ensuring that when classes merge with namespaces and interfaces, is keeping // each symbol in only one of the representations // Also, synthesizing a default export of some kind // If it's an alias: emit `export default ref` // If it's a property: emit `export default _default` with a `_default` prop // If it's a class/interface/function: emit a class/interface/function with a `default` modifier // These forms can merge, eg (`export default 12; export default interface A {}`) function serializeSymbolWorker(symbol: Symbol, isPrivate: boolean, propertyAsAlias: boolean, escapedSymbolName = symbol.escapedName): void { const symbolName = unescapeLeadingUnderscores(escapedSymbolName); const isDefault = escapedSymbolName === InternalSymbolName.Default; if (isPrivate && !(context.flags & NodeBuilderFlags.AllowAnonymousIdentifier) && isStringANonContextualKeyword(symbolName) && !isDefault) { // Oh no. We cannot use this symbol's name as it's name... It's likely some jsdoc had an invalid name like `export` or `default` :( context.encounteredError = true; // TODO: Issue error via symbol tracker? return; // If we need to emit a private with a keyword name, we're done for, since something else will try to refer to it by that name } let needsPostExportDefault = isDefault && !!( symbol.flags & SymbolFlags.ExportDoesNotSupportDefaultModifier || (symbol.flags & SymbolFlags.Function && length(getPropertiesOfType(getTypeOfSymbol(symbol)))) ) && !(symbol.flags & SymbolFlags.Alias); // An alias symbol should preclude needing to make an alias ourselves let needsExportDeclaration = !needsPostExportDefault && !isPrivate && isStringANonContextualKeyword(symbolName) && !isDefault; // `serializeVariableOrProperty` will handle adding the export declaration if it is run (since `getInternalSymbolName` will create the name mapping), so we need to ensuer we unset `needsExportDeclaration` if it is if (needsPostExportDefault || needsExportDeclaration) { isPrivate = true; } const modifierFlags = (!isPrivate ? ModifierFlags.Export : 0) | (isDefault && !needsPostExportDefault ? ModifierFlags.Default : 0); const isConstMergedWithNS = symbol.flags & SymbolFlags.Module && symbol.flags & (SymbolFlags.BlockScopedVariable | SymbolFlags.FunctionScopedVariable | SymbolFlags.Property) && escapedSymbolName !== InternalSymbolName.ExportEquals; const isConstMergedWithNSPrintableAsSignatureMerge = isConstMergedWithNS && isTypeRepresentableAsFunctionNamespaceMerge(getTypeOfSymbol(symbol), symbol); if (symbol.flags & (SymbolFlags.Function | SymbolFlags.Method) || isConstMergedWithNSPrintableAsSignatureMerge) { serializeAsFunctionNamespaceMerge(getTypeOfSymbol(symbol), symbol, getInternalSymbolName(symbol, symbolName), modifierFlags); } if (symbol.flags & SymbolFlags.TypeAlias) { serializeTypeAlias(symbol, symbolName, modifierFlags); } // Need to skip over export= symbols below - json source files get a single `Property` flagged // symbol of name `export=` which needs to be handled like an alias. It's not great, but it is what it is. if ( symbol.flags & (SymbolFlags.BlockScopedVariable | SymbolFlags.FunctionScopedVariable | SymbolFlags.Property | SymbolFlags.Accessor) && escapedSymbolName !== InternalSymbolName.ExportEquals && !(symbol.flags & SymbolFlags.Prototype) && !(symbol.flags & SymbolFlags.Class) && !(symbol.flags & SymbolFlags.Method) && !isConstMergedWithNSPrintableAsSignatureMerge ) { if (propertyAsAlias) { const createdExport = serializeMaybeAliasAssignment(symbol); if (createdExport) { needsExportDeclaration = false; needsPostExportDefault = false; } } else { const type = getTypeOfSymbol(symbol); const localName = getInternalSymbolName(symbol, symbolName); if (type.symbol && type.symbol !== symbol && type.symbol.flags & SymbolFlags.Function && some(type.symbol.declarations, isFunctionExpressionOrArrowFunction) && (type.symbol.members?.size || type.symbol.exports?.size)) { // assignment of a anonymous expando/class-like function, the func/ns/merge branch below won't trigger, // and the assignment form has to reference the unreachable anonymous type so will error. // Instead, serialize the type's symbol, but with the current symbol's name, rather than the anonymous one. if (!context.remappedSymbolReferences) { context.remappedSymbolReferences = new Map(); } context.remappedSymbolReferences.set(getSymbolId(type.symbol), symbol); // save name remapping as local name for target symbol serializeSymbolWorker(type.symbol, isPrivate, propertyAsAlias, escapedSymbolName); context.remappedSymbolReferences.delete(getSymbolId(type.symbol)); } else if (!(symbol.flags & SymbolFlags.Function) && isTypeRepresentableAsFunctionNamespaceMerge(type, symbol)) { // If the type looks like a function declaration + ns could represent it, and it's type is sourced locally, rewrite it into a function declaration + ns serializeAsFunctionNamespaceMerge(type, symbol, localName, modifierFlags); } else { // A Class + Property merge is made for a `module.exports.Member = class {}`, and it doesn't serialize well as either a class _or_ a property symbol - in fact, _it behaves like an alias!_ // `var` is `FunctionScopedVariable`, `const` and `let` are `BlockScopedVariable`, and `module.exports.thing =` is `Property` const flags = !(symbol.flags & SymbolFlags.BlockScopedVariable) ? symbol.parent?.valueDeclaration && isSourceFile(symbol.parent?.valueDeclaration) ? NodeFlags.Const // exports are immutable in es6, which is what we emulate and check; so it's safe to mark all exports as `const` (there's no difference to consumers, but it allows unique symbol type declarations) : undefined : isConstantVariable(symbol) ? NodeFlags.Const : NodeFlags.Let; const name = (needsPostExportDefault || !(symbol.flags & SymbolFlags.Property)) ? localName : getUnusedName(localName, symbol); let textRange: Node | undefined = symbol.declarations && find(symbol.declarations, d => isVariableDeclaration(d)); if (textRange && isVariableDeclarationList(textRange.parent) && textRange.parent.declarations.length === 1) { textRange = textRange.parent.parent; } const propertyAccessRequire = symbol.declarations?.find(isPropertyAccessExpression); if ( propertyAccessRequire && isBinaryExpression(propertyAccessRequire.parent) && isIdentifier(propertyAccessRequire.parent.right) && type.symbol?.valueDeclaration && isSourceFile(type.symbol.valueDeclaration) ) { const alias = localName === propertyAccessRequire.parent.right.escapedText ? undefined : propertyAccessRequire.parent.right; addResult( factory.createExportDeclaration( /*modifiers*/ undefined, /*isTypeOnly*/ false, factory.createNamedExports([factory.createExportSpecifier(/*isTypeOnly*/ false, alias, localName)]), ), ModifierFlags.None, ); context.tracker.trackSymbol(type.symbol, context.enclosingDeclaration, SymbolFlags.Value); } else { const statement = setTextRange( context, factory.createVariableStatement( /*modifiers*/ undefined, factory.createVariableDeclarationList([ factory.createVariableDeclaration(name, /*exclamationToken*/ undefined, serializeTypeForDeclaration(context, /*declaration*/ undefined, type, symbol)), ], flags), ), textRange, ); addResult(statement, name !== localName ? modifierFlags & ~ModifierFlags.Export : modifierFlags); if (name !== localName && !isPrivate) { // We rename the variable declaration we generate for Property symbols since they may have a name which // conflicts with a local declaration. For example, given input: // ``` // function g() {} // module.exports.g = g // ``` // In such a situation, we have a local variable named `g`, and a separate exported variable named `g`. // Naively, we would emit // ``` // function g() {} // export const g: typeof g; // ``` // That's obviously incorrect - the `g` in the type annotation needs to refer to the local `g`, but // the export declaration shadows it. // To work around that, we instead write // ``` // function g() {} // const g_1: typeof g; // export { g_1 as g }; // ``` // To create an export named `g` that does _not_ shadow the local `g` addResult( factory.createExportDeclaration( /*modifiers*/ undefined, /*isTypeOnly*/ false, factory.createNamedExports([factory.createExportSpecifier(/*isTypeOnly*/ false, name, localName)]), ), ModifierFlags.None, ); needsExportDeclaration = false; needsPostExportDefault = false; } } } } } if (symbol.flags & SymbolFlags.Enum) { serializeEnum(symbol, symbolName, modifierFlags); } if (symbol.flags & SymbolFlags.Class) { if ( symbol.flags & SymbolFlags.Property && symbol.valueDeclaration && isBinaryExpression(symbol.valueDeclaration.parent) && isClassExpression(symbol.valueDeclaration.parent.right) ) { // Looks like a `module.exports.Sub = class {}` - if we serialize `symbol` as a class, the result will have no members, // since the classiness is actually from the target of the effective alias the symbol is. yes. A BlockScopedVariable|Class|Property // _really_ acts like an Alias, and none of a BlockScopedVariable, Class, or Property. This is the travesty of JS binding today. serializeAsAlias(symbol, getInternalSymbolName(symbol, symbolName), modifierFlags); } else { serializeAsClass(symbol, getInternalSymbolName(symbol, symbolName), modifierFlags); } } if ((symbol.flags & (SymbolFlags.ValueModule | SymbolFlags.NamespaceModule) && (!isConstMergedWithNS || isTypeOnlyNamespace(symbol))) || isConstMergedWithNSPrintableAsSignatureMerge) { serializeModule(symbol, symbolName, modifierFlags); } // The class meaning serialization should handle serializing all interface members if (symbol.flags & SymbolFlags.Interface && !(symbol.flags & SymbolFlags.Class)) { serializeInterface(symbol, symbolName, modifierFlags); } if (symbol.flags & SymbolFlags.Alias) { serializeAsAlias(symbol, getInternalSymbolName(symbol, symbolName), modifierFlags); } if (symbol.flags & SymbolFlags.Property && symbol.escapedName === InternalSymbolName.ExportEquals) { serializeMaybeAliasAssignment(symbol); } if (symbol.flags & SymbolFlags.ExportStar) { // synthesize export * from "moduleReference" // Straightforward - only one thing to do - make an export declaration if (symbol.declarations) { for (const node of symbol.declarations) { const resolvedModule = resolveExternalModuleName(node, (node as ExportDeclaration).moduleSpecifier!); if (!resolvedModule) continue; addResult(factory.createExportDeclaration(/*modifiers*/ undefined, /*isTypeOnly*/ (node as ExportDeclaration).isTypeOnly, /*exportClause*/ undefined, factory.createStringLiteral(getSpecifierForModuleSymbol(resolvedModule, context))), ModifierFlags.None); } } } if (needsPostExportDefault) { addResult(factory.createExportAssignment(/*modifiers*/ undefined, /*isExportEquals*/ false, factory.createIdentifier(getInternalSymbolName(symbol, symbolName))), ModifierFlags.None); } else if (needsExportDeclaration) { addResult( factory.createExportDeclaration( /*modifiers*/ undefined, /*isTypeOnly*/ false, factory.createNamedExports([factory.createExportSpecifier(/*isTypeOnly*/ false, getInternalSymbolName(symbol, symbolName), symbolName)]), ), ModifierFlags.None, ); } } function includePrivateSymbol(symbol: Symbol) { if (some(symbol.declarations, isPartOfParameterDeclaration)) return; Debug.assertIsDefined(deferredPrivatesStack[deferredPrivatesStack.length - 1]); getUnusedName(unescapeLeadingUnderscores(symbol.escapedName), symbol); // Call to cache unique name for symbol // Blanket moving (import) aliases into the root private context should work, since imports are not valid within namespaces // (so they must have been in the root to begin with if they were real imports) cjs `require` aliases (an upcoming feature) // will throw a wrench in this, since those may have been nested, but we'll need to synthesize them in the outer scope // anyway, as that's the only place the import they translate to is valid. In such a case, we might need to use a unique name // for the moved import; which hopefully the above `getUnusedName` call should produce. const isExternalImportAlias = !!(symbol.flags & SymbolFlags.Alias) && !some(symbol.declarations, d => !!findAncestor(d, isExportDeclaration) || isNamespaceExport(d) || (isImportEqualsDeclaration(d) && !isExternalModuleReference(d.moduleReference))); deferredPrivatesStack[isExternalImportAlias ? 0 : (deferredPrivatesStack.length - 1)].set(getSymbolId(symbol), symbol); } function isExportingScope(enclosingDeclaration: Node) { return ((isSourceFile(enclosingDeclaration) && (isExternalOrCommonJsModule(enclosingDeclaration) || isJsonSourceFile(enclosingDeclaration))) || (isAmbientModule(enclosingDeclaration) && !isGlobalScopeAugmentation(enclosingDeclaration))); } // Prepends a `declare` and/or `export` modifier if the context requires it, and then adds `node` to `result` and returns `node` function addResult(node: Statement, additionalModifierFlags: ModifierFlags) { if (canHaveModifiers(node)) { let newModifierFlags: ModifierFlags = ModifierFlags.None; const enclosingDeclaration = context.enclosingDeclaration && (isJSDocTypeAlias(context.enclosingDeclaration) ? getSourceFileOfNode(context.enclosingDeclaration) : context.enclosingDeclaration); if ( additionalModifierFlags & ModifierFlags.Export && enclosingDeclaration && (isExportingScope(enclosingDeclaration) || isModuleDeclaration(enclosingDeclaration)) && canHaveExportModifier(node) ) { // Classes, namespaces, variables, functions, interfaces, and types should all be `export`ed in a module context if not private newModifierFlags |= ModifierFlags.Export; } if ( addingDeclare && !(newModifierFlags & ModifierFlags.Export) && (!enclosingDeclaration || !(enclosingDeclaration.flags & NodeFlags.Ambient)) && (isEnumDeclaration(node) || isVariableStatement(node) || isFunctionDeclaration(node) || isClassDeclaration(node) || isModuleDeclaration(node)) ) { // Classes, namespaces, variables, enums, and functions all need `declare` modifiers to be valid in a declaration file top-level scope newModifierFlags |= ModifierFlags.Ambient; } if ((additionalModifierFlags & ModifierFlags.Default) && (isClassDeclaration(node) || isInterfaceDeclaration(node) || isFunctionDeclaration(node))) { newModifierFlags |= ModifierFlags.Default; } if (newModifierFlags) { node = factory.replaceModifiers(node, newModifierFlags | getEffectiveModifierFlags(node)); } } results.push(node); } function serializeTypeAlias(symbol: Symbol, symbolName: string, modifierFlags: ModifierFlags) { const aliasType = getDeclaredTypeOfTypeAlias(symbol); const typeParams = getSymbolLinks(symbol).typeParameters; const typeParamDecls = map(typeParams, p => typeParameterToDeclaration(p, context)); const jsdocAliasDecl = symbol.declarations?.find(isJSDocTypeAlias); const commentText = getTextOfJSDocComment(jsdocAliasDecl ? jsdocAliasDecl.comment || jsdocAliasDecl.parent.comment : undefined); const restoreFlags = saveRestoreFlags(context); context.flags |= NodeBuilderFlags.InTypeAlias; const oldEnclosingDecl = context.enclosingDeclaration; context.enclosingDeclaration = jsdocAliasDecl; const typeNode = jsdocAliasDecl && jsdocAliasDecl.typeExpression && isJSDocTypeExpression(jsdocAliasDecl.typeExpression) && tryReuseExistingNonParameterTypeNode(context, jsdocAliasDecl.typeExpression.type, aliasType, /*host*/ undefined) || typeToTypeNodeHelper(aliasType, context); addResult( setSyntheticLeadingComments( factory.createTypeAliasDeclaration(/*modifiers*/ undefined, getInternalSymbolName(symbol, symbolName), typeParamDecls, typeNode), !commentText ? [] : [{ kind: SyntaxKind.MultiLineCommentTrivia, text: "*\n * " + commentText.replace(/\n/g, "\n * ") + "\n ", pos: -1, end: -1, hasTrailingNewLine: true }], ), modifierFlags, ); restoreFlags(); context.enclosingDeclaration = oldEnclosingDecl; } function serializeInterface(symbol: Symbol, symbolName: string, modifierFlags: ModifierFlags) { const interfaceType = getDeclaredTypeOfClassOrInterface(symbol); const localParams = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol); const typeParamDecls = map(localParams, p => typeParameterToDeclaration(p, context)); const baseTypes = getBaseTypes(interfaceType); const baseType = length(baseTypes) ? getIntersectionType(baseTypes) : undefined; const members = flatMap(getPropertiesOfType(interfaceType), p => serializePropertySymbolForInterface(p, baseType)); const callSignatures = serializeSignatures(SignatureKind.Call, interfaceType, baseType, SyntaxKind.CallSignature) as CallSignatureDeclaration[]; const constructSignatures = serializeSignatures(SignatureKind.Construct, interfaceType, baseType, SyntaxKind.ConstructSignature) as ConstructSignatureDeclaration[]; const indexSignatures = serializeIndexSignatures(interfaceType, baseType); const heritageClauses = !length(baseTypes) ? undefined : [factory.createHeritageClause(SyntaxKind.ExtendsKeyword, mapDefined(baseTypes, b => trySerializeAsTypeReference(b, SymbolFlags.Value)))]; addResult( factory.createInterfaceDeclaration( /*modifiers*/ undefined, getInternalSymbolName(symbol, symbolName), typeParamDecls, heritageClauses, [...indexSignatures, ...constructSignatures, ...callSignatures, ...members], ), modifierFlags, ); } function getNamespaceMembersForSerialization(symbol: Symbol) { let exports = arrayFrom(getExportsOfSymbol(symbol).values()); const merged = getMergedSymbol(symbol); if (merged !== symbol) { const membersSet = new Set(exports); for (const exported of getExportsOfSymbol(merged).values()) { if (!(getSymbolFlags(resolveSymbol(exported)) & SymbolFlags.Value)) { membersSet.add(exported); } } exports = arrayFrom(membersSet); } return filter(exports, m => isNamespaceMember(m) && isIdentifierText(m.escapedName as string, ScriptTarget.ESNext)); } function isTypeOnlyNamespace(symbol: Symbol) { return every(getNamespaceMembersForSerialization(symbol), m => !(getSymbolFlags(resolveSymbol(m)) & SymbolFlags.Value)); } function serializeModule(symbol: Symbol, symbolName: string, modifierFlags: ModifierFlags) { const members = getNamespaceMembersForSerialization(symbol); // Split NS members up by declaration - members whose parent symbol is the ns symbol vs those whose is not (but were added in later via merging) const locationMap = arrayToMultiMap(members, m => m.parent && m.parent === symbol ? "real" : "merged"); const realMembers = locationMap.get("real") || emptyArray; const mergedMembers = locationMap.get("merged") || emptyArray; // TODO: `suppressNewPrivateContext` is questionable -we need to simply be emitting privates in whatever scope they were declared in, rather // than whatever scope we traverse to them in. That's a bit of a complex rewrite, since we're not _actually_ tracking privates at all in advance, // so we don't even have placeholders to fill in. if (length(realMembers)) { const localName = getInternalSymbolName(symbol, symbolName); serializeAsNamespaceDeclaration(realMembers, localName, modifierFlags, !!(symbol.flags & (SymbolFlags.Function | SymbolFlags.Assignment))); } if (length(mergedMembers)) { const containingFile = getSourceFileOfNode(context.enclosingDeclaration); const localName = getInternalSymbolName(symbol, symbolName); const nsBody = factory.createModuleBlock([factory.createExportDeclaration( /*modifiers*/ undefined, /*isTypeOnly*/ false, factory.createNamedExports(mapDefined(filter(mergedMembers, n => n.escapedName !== InternalSymbolName.ExportEquals), s => { const name = unescapeLeadingUnderscores(s.escapedName); const localName = getInternalSymbolName(s, name); const aliasDecl = s.declarations && getDeclarationOfAliasSymbol(s); if (containingFile && (aliasDecl ? containingFile !== getSourceFileOfNode(aliasDecl) : !some(s.declarations, d => getSourceFileOfNode(d) === containingFile))) { context.tracker?.reportNonlocalAugmentation?.(containingFile, symbol, s); return undefined; } const target = aliasDecl && getTargetOfAliasDeclaration(aliasDecl, /*dontRecursivelyResolve*/ true); includePrivateSymbol(target || s); const targetName = target ? getInternalSymbolName(target, unescapeLeadingUnderscores(target.escapedName)) : localName; return factory.createExportSpecifier(/*isTypeOnly*/ false, name === targetName ? undefined : targetName, name); })), )]); addResult( factory.createModuleDeclaration( /*modifiers*/ undefined, factory.createIdentifier(localName), nsBody, NodeFlags.Namespace, ), ModifierFlags.None, ); } } function serializeEnum(symbol: Symbol, symbolName: string, modifierFlags: ModifierFlags) { addResult( factory.createEnumDeclaration( factory.createModifiersFromModifierFlags(isConstEnumSymbol(symbol) ? ModifierFlags.Const : 0), getInternalSymbolName(symbol, symbolName), map(filter(getPropertiesOfType(getTypeOfSymbol(symbol)), p => !!(p.flags & SymbolFlags.EnumMember)), p => { // TODO: Handle computed names // I hate that to get the initialized value we need to walk back to the declarations here; but there's no // other way to get the possible const value of an enum member that I'm aware of, as the value is cached // _on the declaration_, not on the declaration's symbol... const initializedValue = p.declarations && p.declarations[0] && isEnumMember(p.declarations[0]) ? getConstantValue(p.declarations[0]) : undefined; return factory.createEnumMember( unescapeLeadingUnderscores(p.escapedName), initializedValue === undefined ? undefined : typeof initializedValue === "string" ? factory.createStringLiteral(initializedValue) : factory.createNumericLiteral(initializedValue), ); }), ), modifierFlags, ); } function serializeAsFunctionNamespaceMerge(type: Type, symbol: Symbol, localName: string, modifierFlags: ModifierFlags) { const signatures = getSignaturesOfType(type, SignatureKind.Call); for (const sig of signatures) { // Each overload becomes a separate function declaration, in order const decl = signatureToSignatureDeclarationHelper(sig, SyntaxKind.FunctionDeclaration, context, { name: factory.createIdentifier(localName) }) as FunctionDeclaration; addResult(setTextRange(context, decl, getSignatureTextRangeLocation(sig)), modifierFlags); } // Module symbol emit will take care of module-y members, provided it has exports if (!(symbol.flags & (SymbolFlags.ValueModule | SymbolFlags.NamespaceModule) && !!symbol.exports && !!symbol.exports.size)) { const props = filter(getPropertiesOfType(type), isNamespaceMember); serializeAsNamespaceDeclaration(props, localName, modifierFlags, /*suppressNewPrivateContext*/ true); } } function getSignatureTextRangeLocation(signature: Signature) { if (signature.declaration && signature.declaration.parent) { if (isBinaryExpression(signature.declaration.parent) && getAssignmentDeclarationKind(signature.declaration.parent) === AssignmentDeclarationKind.Property) { return signature.declaration.parent; } // for expressions assigned to `var`s, use the `var` as the text range if (isVariableDeclaration(signature.declaration.parent) && signature.declaration.parent.parent) { return signature.declaration.parent.parent; } } return signature.declaration; } function serializeAsNamespaceDeclaration(props: readonly Symbol[], localName: string, modifierFlags: ModifierFlags, suppressNewPrivateContext: boolean) { if (length(props)) { const localVsRemoteMap = arrayToMultiMap(props, p => !length(p.declarations) || some(p.declarations, d => getSourceFileOfNode(d) === getSourceFileOfNode(context.enclosingDeclaration!)) ? "local" : "remote"); const localProps = localVsRemoteMap.get("local") || emptyArray; // handle remote props first - we need to make an `import` declaration that points at the module containing each remote // prop in the outermost scope (TODO: a namespace within a namespace would need to be appropriately handled by this) // Example: // import Foo_1 = require("./exporter"); // export namespace ns { // import Foo = Foo_1.Foo; // export { Foo }; // export const c: number; // } // This is needed because in JS, statements like `const x = require("./f")` support both type and value lookup, even if they're // normally just value lookup (so it functions kinda like an alias even when it's not an alias) // _Usually_, we'll simply print the top-level as an alias instead of a `var` in such situations, however is is theoretically // possible to encounter a situation where a type has members from both the current file and other files - in those situations, // emit akin to the above would be needed. // Add a namespace // Create namespace as non-synthetic so it is usable as an enclosing declaration let fakespace = parseNodeFactory.createModuleDeclaration(/*modifiers*/ undefined, factory.createIdentifier(localName), factory.createModuleBlock([]), NodeFlags.Namespace); setParent(fakespace, enclosingDeclaration as SourceFile | NamespaceDeclaration); fakespace.locals = createSymbolTable(props); fakespace.symbol = props[0].parent!; const oldResults = results; results = []; const oldAddingDeclare = addingDeclare; addingDeclare = false; const subcontext = { ...context, enclosingDeclaration: fakespace }; const oldContext = context; context = subcontext; // TODO: implement handling for the localVsRemoteMap.get("remote") - should be difficult to trigger (see comment above), as only interesting cross-file js merges should make this possible visitSymbolTable(createSymbolTable(localProps), suppressNewPrivateContext, /*propertyAsAlias*/ true); context = oldContext; addingDeclare = oldAddingDeclare; const declarations = results; results = oldResults; // replace namespace with synthetic version const defaultReplaced = map(declarations, d => isExportAssignment(d) && !d.isExportEquals && isIdentifier(d.expression) ? factory.createExportDeclaration( /*modifiers*/ undefined, /*isTypeOnly*/ false, factory.createNamedExports([factory.createExportSpecifier(/*isTypeOnly*/ false, d.expression, factory.createIdentifier(InternalSymbolName.Default))]), ) : d); const exportModifierStripped = every(defaultReplaced, d => hasSyntacticModifier(d, ModifierFlags.Export)) ? map(defaultReplaced as Extract[], removeExportModifier) : defaultReplaced; fakespace = factory.updateModuleDeclaration( fakespace, fakespace.modifiers, fakespace.name, factory.createModuleBlock(exportModifierStripped), ); addResult(fakespace, modifierFlags); // namespaces can never be default exported } } function isNamespaceMember(p: Symbol) { return !!(p.flags & (SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias)) || !(p.flags & SymbolFlags.Prototype || p.escapedName === "prototype" || p.valueDeclaration && isStatic(p.valueDeclaration) && isClassLike(p.valueDeclaration.parent)); } function sanitizeJSDocImplements(clauses: readonly ExpressionWithTypeArguments[]): ExpressionWithTypeArguments[] | undefined { const result = mapDefined(clauses, e => { const oldEnclosing = context.enclosingDeclaration; context.enclosingDeclaration = e; let expr = e.expression; if (isEntityNameExpression(expr)) { if (isIdentifier(expr) && idText(expr) === "") { return cleanup(/*result*/ undefined); // Empty heritage clause, should be an error, but prefer emitting no heritage clauses to reemitting the empty one } let introducesError: boolean; ({ introducesError, node: expr } = trackExistingEntityName(expr, context)); if (introducesError) { return cleanup(/*result*/ undefined); } } return cleanup(factory.createExpressionWithTypeArguments( expr, map(e.typeArguments, a => tryReuseExistingNonParameterTypeNode(context, a, getTypeFromTypeNode(context, a)) || typeToTypeNodeHelper(getTypeFromTypeNode(context, a), context)), )); function cleanup(result: T): T { context.enclosingDeclaration = oldEnclosing; return result; } }); if (result.length === clauses.length) { return result; } return undefined; } function serializeAsClass(symbol: Symbol, localName: string, modifierFlags: ModifierFlags) { const originalDecl = symbol.declarations?.find(isClassLike); const oldEnclosing = context.enclosingDeclaration; context.enclosingDeclaration = originalDecl || oldEnclosing; const localParams = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol); const typeParamDecls = map(localParams, p => typeParameterToDeclaration(p, context)); const classType = getTypeWithThisArgument(getDeclaredTypeOfClassOrInterface(symbol)) as InterfaceType; const baseTypes = getBaseTypes(classType); const originalImplements = originalDecl && getEffectiveImplementsTypeNodes(originalDecl); const implementsExpressions = originalImplements && sanitizeJSDocImplements(originalImplements) || mapDefined(getImplementsTypes(classType), serializeImplementedType); const staticType = getTypeOfSymbol(symbol); const isClass = !!staticType.symbol?.valueDeclaration && isClassLike(staticType.symbol.valueDeclaration); const staticBaseType = isClass ? getBaseConstructorTypeOfClass(staticType as InterfaceType) : anyType; const heritageClauses = [ ...!length(baseTypes) ? [] : [factory.createHeritageClause(SyntaxKind.ExtendsKeyword, map(baseTypes, b => serializeBaseType(b, staticBaseType, localName)))], ...!length(implementsExpressions) ? [] : [factory.createHeritageClause(SyntaxKind.ImplementsKeyword, implementsExpressions)], ]; const symbolProps = getNonInheritedProperties(classType, baseTypes, getPropertiesOfType(classType)); const publicSymbolProps = filter(symbolProps, s => { // `valueDeclaration` could be undefined if inherited from // a union/intersection base type, but inherited properties // don't matter here. const valueDecl = s.valueDeclaration; return !!valueDecl && !(isNamedDeclaration(valueDecl) && isPrivateIdentifier(valueDecl.name)); }); const hasPrivateIdentifier = some(symbolProps, s => { // `valueDeclaration` could be undefined if inherited from // a union/intersection base type, but inherited properties // don't matter here. const valueDecl = s.valueDeclaration; return !!valueDecl && isNamedDeclaration(valueDecl) && isPrivateIdentifier(valueDecl.name); }); // Boil down all private properties into a single one. const privateProperties = hasPrivateIdentifier ? [factory.createPropertyDeclaration( /*modifiers*/ undefined, factory.createPrivateIdentifier("#private"), /*questionOrExclamationToken*/ undefined, /*type*/ undefined, /*initializer*/ undefined, )] : emptyArray; const publicProperties = flatMap(publicSymbolProps, p => serializePropertySymbolForClass(p, /*isStatic*/ false, baseTypes[0])); // Consider static members empty if symbol also has function or module meaning - function namespacey emit will handle statics const staticMembers = flatMap( filter(getPropertiesOfType(staticType), p => !(p.flags & SymbolFlags.Prototype) && p.escapedName !== "prototype" && !isNamespaceMember(p)), p => serializePropertySymbolForClass(p, /*isStatic*/ true, staticBaseType), ); // When we encounter an `X.prototype.y` assignment in a JS file, we bind `X` as a class regardless as to whether // the value is ever initialized with a class or function-like value. For cases where `X` could never be // created via `new`, we will inject a `private constructor()` declaration to indicate it is not createable. const isNonConstructableClassLikeInJsFile = !isClass && !!symbol.valueDeclaration && isInJSFile(symbol.valueDeclaration) && !some(getSignaturesOfType(staticType, SignatureKind.Construct)); const constructors = isNonConstructableClassLikeInJsFile ? [factory.createConstructorDeclaration(factory.createModifiersFromModifierFlags(ModifierFlags.Private), [], /*body*/ undefined)] : serializeSignatures(SignatureKind.Construct, staticType, staticBaseType, SyntaxKind.Constructor) as ConstructorDeclaration[]; const indexSignatures = serializeIndexSignatures(classType, baseTypes[0]); context.enclosingDeclaration = oldEnclosing; addResult( setTextRange( context, factory.createClassDeclaration( /*modifiers*/ undefined, localName, typeParamDecls, heritageClauses, [...indexSignatures, ...staticMembers, ...constructors, ...publicProperties, ...privateProperties], ), symbol.declarations && filter(symbol.declarations, d => isClassDeclaration(d) || isClassExpression(d))[0], ), modifierFlags, ); } function getSomeTargetNameFromDeclarations(declarations: Declaration[] | undefined) { return firstDefined(declarations, d => { if (isImportSpecifier(d) || isExportSpecifier(d)) { return moduleExportNameTextUnescaped(d.propertyName || d.name); } if (isBinaryExpression(d) || isExportAssignment(d)) { const expression = isExportAssignment(d) ? d.expression : d.right; if (isPropertyAccessExpression(expression)) { return idText(expression.name); } } if (isAliasSymbolDeclaration(d)) { // This is... heuristic, at best. But it's probably better than always printing the name of the shorthand ambient module. const name = getNameOfDeclaration(d); if (name && isIdentifier(name)) { return idText(name); } } return undefined; }); } function serializeAsAlias(symbol: Symbol, localName: string, modifierFlags: ModifierFlags) { // synthesize an alias, eg `export { symbolName as Name }` // need to mark the alias `symbol` points at // as something we need to serialize as a private declaration as well const node = getDeclarationOfAliasSymbol(symbol); if (!node) return Debug.fail(); const target = getMergedSymbol(getTargetOfAliasDeclaration(node, /*dontRecursivelyResolve*/ true)); if (!target) { return; } // If `target` refers to a shorthand module symbol, the name we're trying to pull out isn;t recoverable from the target symbol // In such a scenario, we must fall back to looking for an alias declaration on `symbol` and pulling the target name from that let verbatimTargetName = isShorthandAmbientModuleSymbol(target) && getSomeTargetNameFromDeclarations(symbol.declarations) || unescapeLeadingUnderscores(target.escapedName); if (verbatimTargetName === InternalSymbolName.ExportEquals && allowSyntheticDefaultImports) { // target refers to an `export=` symbol that was hoisted into a synthetic default - rename here to match verbatimTargetName = InternalSymbolName.Default; } const targetName = getInternalSymbolName(target, verbatimTargetName); includePrivateSymbol(target); // the target may be within the same scope - attempt to serialize it first switch (node.kind) { case SyntaxKind.BindingElement: if (node.parent?.parent?.kind === SyntaxKind.VariableDeclaration) { // const { SomeClass } = require('./lib'); const specifier = getSpecifierForModuleSymbol(target.parent || target, context); // './lib' const { propertyName } = node as BindingElement; addResult( factory.createImportDeclaration( /*modifiers*/ undefined, factory.createImportClause( /*isTypeOnly*/ false, /*name*/ undefined, factory.createNamedImports([factory.createImportSpecifier( /*isTypeOnly*/ false, propertyName && isIdentifier(propertyName) ? factory.createIdentifier(idText(propertyName)) : undefined, factory.createIdentifier(localName), )]), ), factory.createStringLiteral(specifier), /*attributes*/ undefined, ), ModifierFlags.None, ); break; } // We don't know how to serialize this (nested?) binding element Debug.failBadSyntaxKind(node.parent?.parent || node, "Unhandled binding element grandparent kind in declaration serialization"); break; case SyntaxKind.ShorthandPropertyAssignment: if (node.parent?.parent?.kind === SyntaxKind.BinaryExpression) { // module.exports = { SomeClass } serializeExportSpecifier( unescapeLeadingUnderscores(symbol.escapedName), targetName, ); } break; case SyntaxKind.VariableDeclaration: // commonjs require: const x = require('y') if (isPropertyAccessExpression((node as VariableDeclaration).initializer!)) { // const x = require('y').z const initializer = (node as VariableDeclaration).initializer! as PropertyAccessExpression; // require('y').z const uniqueName = factory.createUniqueName(localName); // _x const specifier = getSpecifierForModuleSymbol(target.parent || target, context); // 'y' // import _x = require('y'); addResult( factory.createImportEqualsDeclaration( /*modifiers*/ undefined, /*isTypeOnly*/ false, uniqueName, factory.createExternalModuleReference(factory.createStringLiteral(specifier)), ), ModifierFlags.None, ); // import x = _x.z addResult( factory.createImportEqualsDeclaration( /*modifiers*/ undefined, /*isTypeOnly*/ false, factory.createIdentifier(localName), factory.createQualifiedName(uniqueName, initializer.name as Identifier), ), modifierFlags, ); break; } // else fall through and treat commonjs require just like import= case SyntaxKind.ImportEqualsDeclaration: // This _specifically_ only exists to handle json declarations - where we make aliases, but since // we emit no declarations for the json document, must not refer to it in the declarations if (target.escapedName === InternalSymbolName.ExportEquals && some(target.declarations, d => isSourceFile(d) && isJsonSourceFile(d))) { serializeMaybeAliasAssignment(symbol); break; } // Could be a local `import localName = ns.member` or // an external `import localName = require("whatever")` const isLocalImport = !(target.flags & SymbolFlags.ValueModule) && !isVariableDeclaration(node); addResult( factory.createImportEqualsDeclaration( /*modifiers*/ undefined, /*isTypeOnly*/ false, factory.createIdentifier(localName), isLocalImport ? symbolToName(target, context, SymbolFlags.All, /*expectsIdentifier*/ false) : factory.createExternalModuleReference(factory.createStringLiteral(getSpecifierForModuleSymbol(target, context))), ), isLocalImport ? modifierFlags : ModifierFlags.None, ); break; case SyntaxKind.NamespaceExportDeclaration: // export as namespace foo // TODO: Not part of a file's local or export symbol tables // Is bound into file.symbol.globalExports instead, which we don't currently traverse addResult(factory.createNamespaceExportDeclaration(idText((node as NamespaceExportDeclaration).name)), ModifierFlags.None); break; case SyntaxKind.ImportClause: { const generatedSpecifier = getSpecifierForModuleSymbol(target.parent || target, context); // generate specifier (even though we're reusing and existing one) for ambient module reference include side effects const specifier = context.bundled ? factory.createStringLiteral(generatedSpecifier) : (node as ImportClause).parent.moduleSpecifier; const attributes = isImportDeclaration(node.parent) ? node.parent.attributes : undefined; const isTypeOnly = isJSDocImportTag((node as ImportClause).parent); addResult( factory.createImportDeclaration( /*modifiers*/ undefined, factory.createImportClause(isTypeOnly, factory.createIdentifier(localName), /*namedBindings*/ undefined), specifier, attributes, ), ModifierFlags.None, ); break; } case SyntaxKind.NamespaceImport: { const generatedSpecifier = getSpecifierForModuleSymbol(target.parent || target, context); // generate specifier (even though we're reusing and existing one) for ambient module reference include side effects const specifier = context.bundled ? factory.createStringLiteral(generatedSpecifier) : (node as NamespaceImport).parent.parent.moduleSpecifier; const isTypeOnly = isJSDocImportTag((node as NamespaceImport).parent.parent); addResult( factory.createImportDeclaration( /*modifiers*/ undefined, factory.createImportClause(isTypeOnly, /*name*/ undefined, factory.createNamespaceImport(factory.createIdentifier(localName))), specifier, (node as ImportClause).parent.attributes, ), ModifierFlags.None, ); break; } case SyntaxKind.NamespaceExport: addResult( factory.createExportDeclaration( /*modifiers*/ undefined, /*isTypeOnly*/ false, factory.createNamespaceExport(factory.createIdentifier(localName)), factory.createStringLiteral(getSpecifierForModuleSymbol(target, context)), ), ModifierFlags.None, ); break; case SyntaxKind.ImportSpecifier: { const generatedSpecifier = getSpecifierForModuleSymbol(target.parent || target, context); // generate specifier (even though we're reusing and existing one) for ambient module reference include side effects const specifier = context.bundled ? factory.createStringLiteral(generatedSpecifier) : (node as ImportSpecifier).parent.parent.parent.moduleSpecifier; const isTypeOnly = isJSDocImportTag((node as ImportSpecifier).parent.parent.parent); addResult( factory.createImportDeclaration( /*modifiers*/ undefined, factory.createImportClause( isTypeOnly, /*name*/ undefined, factory.createNamedImports([ factory.createImportSpecifier( /*isTypeOnly*/ false, localName !== verbatimTargetName ? factory.createIdentifier(verbatimTargetName) : undefined, factory.createIdentifier(localName), ), ]), ), specifier, (node as ImportSpecifier).parent.parent.parent.attributes, ), ModifierFlags.None, ); break; } case SyntaxKind.ExportSpecifier: // does not use localName because the symbol name in this case refers to the name in the exports table, // which we must exactly preserve const specifier = (node.parent.parent as ExportDeclaration).moduleSpecifier; if (specifier) { const propertyName = (node as ExportSpecifier).propertyName; if (propertyName && moduleExportNameIsDefault(propertyName)) { verbatimTargetName = InternalSymbolName.Default; } } // targetName is only used when the target is local, as otherwise the target is an alias that points at // another file serializeExportSpecifier( unescapeLeadingUnderscores(symbol.escapedName), specifier ? verbatimTargetName : targetName, specifier && isStringLiteralLike(specifier) ? factory.createStringLiteral(specifier.text) : undefined, ); break; case SyntaxKind.ExportAssignment: serializeMaybeAliasAssignment(symbol); break; case SyntaxKind.BinaryExpression: case SyntaxKind.PropertyAccessExpression: case SyntaxKind.ElementAccessExpression: // Could be best encoded as though an export specifier or as though an export assignment // If name is default or export=, do an export assignment // Otherwise do an export specifier if (symbol.escapedName === InternalSymbolName.Default || symbol.escapedName === InternalSymbolName.ExportEquals) { serializeMaybeAliasAssignment(symbol); } else { serializeExportSpecifier(localName, targetName); } break; default: return Debug.failBadSyntaxKind(node, "Unhandled alias declaration kind in symbol serializer!"); } } function serializeExportSpecifier(localName: string, targetName: string, specifier?: Expression) { addResult( factory.createExportDeclaration( /*modifiers*/ undefined, /*isTypeOnly*/ false, factory.createNamedExports([factory.createExportSpecifier(/*isTypeOnly*/ false, localName !== targetName ? targetName : undefined, localName)]), specifier, ), ModifierFlags.None, ); } /** * Returns `true` if an export assignment or declaration was produced for the symbol */ function serializeMaybeAliasAssignment(symbol: Symbol): boolean { if (symbol.flags & SymbolFlags.Prototype) { return false; } const name = unescapeLeadingUnderscores(symbol.escapedName); const isExportEquals = name === InternalSymbolName.ExportEquals; const isDefault = name === InternalSymbolName.Default; const isExportAssignmentCompatibleSymbolName = isExportEquals || isDefault; // synthesize export = ref // ref should refer to either be a locally scoped symbol which we need to emit, or // a reference to another namespace/module which we may need to emit an `import` statement for const aliasDecl = symbol.declarations && getDeclarationOfAliasSymbol(symbol); // serialize what the alias points to, preserve the declaration's initializer const target = aliasDecl && getTargetOfAliasDeclaration(aliasDecl, /*dontRecursivelyResolve*/ true); // If the target resolves and resolves to a thing defined in this file, emit as an alias, otherwise emit as a const if (target && length(target.declarations) && some(target.declarations, d => getSourceFileOfNode(d) === getSourceFileOfNode(enclosingDeclaration))) { // In case `target` refers to a namespace member, look at the declaration and serialize the leftmost symbol in it // eg, `namespace A { export class B {} }; exports = A.B;` // Technically, this is all that's required in the case where the assignment is an entity name expression const expr = aliasDecl && ((isExportAssignment(aliasDecl) || isBinaryExpression(aliasDecl)) ? getExportAssignmentExpression(aliasDecl) : getPropertyAssignmentAliasLikeExpression(aliasDecl as ShorthandPropertyAssignment | PropertyAssignment | PropertyAccessExpression)); const first = expr && isEntityNameExpression(expr) ? getFirstNonModuleExportsIdentifier(expr) : undefined; const referenced = first && resolveEntityName(first, SymbolFlags.All, /*ignoreErrors*/ true, /*dontResolveAlias*/ true, enclosingDeclaration); if (referenced || target) { includePrivateSymbol(referenced || target); } // We disable the context's symbol tracker for the duration of this name serialization // as, by virtue of being here, the name is required to print something, and we don't want to // issue a visibility error on it. Only anonymous classes that an alias points at _would_ issue // a visibility error here (as they're not visible within any scope), but we want to hoist them // into the containing scope anyway, so we want to skip the visibility checks. const prevDisableTrackSymbol = context.tracker.disableTrackSymbol; context.tracker.disableTrackSymbol = true; if (isExportAssignmentCompatibleSymbolName) { results.push(factory.createExportAssignment( /*modifiers*/ undefined, isExportEquals, symbolToExpression(target, context, SymbolFlags.All), )); } else { if (first === expr && first) { // serialize as `export {target as name}` serializeExportSpecifier(name, idText(first)); } else if (expr && isClassExpression(expr)) { serializeExportSpecifier(name, getInternalSymbolName(target, symbolName(target))); } else { // serialize as `import _Ref = t.arg.et; export { _Ref as name }` const varName = getUnusedName(name, symbol); addResult( factory.createImportEqualsDeclaration( /*modifiers*/ undefined, /*isTypeOnly*/ false, factory.createIdentifier(varName), symbolToName(target, context, SymbolFlags.All, /*expectsIdentifier*/ false), ), ModifierFlags.None, ); serializeExportSpecifier(name, varName); } } context.tracker.disableTrackSymbol = prevDisableTrackSymbol; return true; } else { // serialize as an anonymous property declaration const varName = getUnusedName(name, symbol); // We have to use `getWidenedType` here since the object within a json file is unwidened within the file // (Unwidened types can only exist in expression contexts and should never be serialized) const typeToSerialize = getWidenedType(getTypeOfSymbol(getMergedSymbol(symbol))); if (isTypeRepresentableAsFunctionNamespaceMerge(typeToSerialize, symbol)) { // If there are no index signatures and `typeToSerialize` is an object type, emit as a namespace instead of a const serializeAsFunctionNamespaceMerge(typeToSerialize, symbol, varName, isExportAssignmentCompatibleSymbolName ? ModifierFlags.None : ModifierFlags.Export); } else { const flags = context.enclosingDeclaration?.kind === SyntaxKind.ModuleDeclaration && (!(symbol.flags & SymbolFlags.Accessor) || symbol.flags & SymbolFlags.SetAccessor) ? NodeFlags.Let : NodeFlags.Const; const statement = factory.createVariableStatement( /*modifiers*/ undefined, factory.createVariableDeclarationList([ factory.createVariableDeclaration(varName, /*exclamationToken*/ undefined, serializeTypeForDeclaration(context, /*declaration*/ undefined, typeToSerialize, symbol)), ], flags), ); // Inlined JSON types exported with [module.]exports= will already emit an export=, so should use `declare`. // Otherwise, the type itself should be exported. addResult( statement, target && target.flags & SymbolFlags.Property && target.escapedName === InternalSymbolName.ExportEquals ? ModifierFlags.Ambient : name === varName ? ModifierFlags.Export : ModifierFlags.None, ); } if (isExportAssignmentCompatibleSymbolName) { results.push(factory.createExportAssignment( /*modifiers*/ undefined, isExportEquals, factory.createIdentifier(varName), )); return true; } else if (name !== varName) { serializeExportSpecifier(name, varName); return true; } return false; } } function isTypeRepresentableAsFunctionNamespaceMerge(typeToSerialize: Type, hostSymbol: Symbol) { // Only object types which are not constructable, or indexable, whose members all come from the // context source file, and whose property names are all valid identifiers and not late-bound, _and_ // whose input is not type annotated (if the input symbol has an annotation we can reuse, we should prefer it) const ctxSrc = getSourceFileOfNode(context.enclosingDeclaration); return getObjectFlags(typeToSerialize) & (ObjectFlags.Anonymous | ObjectFlags.Mapped) && !some(typeToSerialize.symbol?.declarations, isTypeNode) && // If the type comes straight from a type node, we shouldn't try to break it up !length(getIndexInfosOfType(typeToSerialize)) && !isClassInstanceSide(typeToSerialize) && // While a class instance is potentially representable as a NS, prefer printing a reference to the instance type and serializing the class !!(length(filter(getPropertiesOfType(typeToSerialize), isNamespaceMember)) || length(getSignaturesOfType(typeToSerialize, SignatureKind.Call))) && !length(getSignaturesOfType(typeToSerialize, SignatureKind.Construct)) && // TODO: could probably serialize as function + ns + class, now that that's OK !getDeclarationWithTypeAnnotation(hostSymbol, enclosingDeclaration) && !(typeToSerialize.symbol && some(typeToSerialize.symbol.declarations, d => getSourceFileOfNode(d) !== ctxSrc)) && !some(getPropertiesOfType(typeToSerialize), p => isLateBoundName(p.escapedName)) && !some(getPropertiesOfType(typeToSerialize), p => some(p.declarations, d => getSourceFileOfNode(d) !== ctxSrc)) && every(getPropertiesOfType(typeToSerialize), p => { if (!isIdentifierText(symbolName(p), languageVersion)) { return false; } if (!(p.flags & SymbolFlags.Accessor)) { return true; } return getNonMissingTypeOfSymbol(p) === getWriteTypeOfSymbol(p); }); } function makeSerializePropertySymbol( createProperty: ( modifiers: readonly Modifier[] | undefined, name: string | PropertyName, questionOrExclamationToken: QuestionToken | undefined, type: TypeNode | undefined, initializer: Expression | undefined, ) => T, methodKind: SignatureDeclaration["kind"], useAccessors: true, ): (p: Symbol, isStatic: boolean, baseType: Type | undefined) => T | AccessorDeclaration | (T | AccessorDeclaration)[]; function makeSerializePropertySymbol( createProperty: ( modifiers: readonly Modifier[] | undefined, name: string | PropertyName, questionOrExclamationToken: QuestionToken | undefined, type: TypeNode | undefined, initializer: Expression | undefined, ) => T, methodKind: SignatureDeclaration["kind"], useAccessors: false, ): (p: Symbol, isStatic: boolean, baseType: Type | undefined) => T | T[]; function makeSerializePropertySymbol( createProperty: ( modifiers: readonly Modifier[] | undefined, name: string | PropertyName, questionOrExclamationToken: QuestionToken | undefined, type: TypeNode | undefined, initializer: Expression | undefined, ) => T, methodKind: SignatureDeclaration["kind"], useAccessors: boolean, ): (p: Symbol, isStatic: boolean, baseType: Type | undefined) => T | AccessorDeclaration | (T | AccessorDeclaration)[] { return function serializePropertySymbol(p: Symbol, isStatic: boolean, baseType: Type | undefined): T | AccessorDeclaration | (T | AccessorDeclaration)[] { const modifierFlags = getDeclarationModifierFlagsFromSymbol(p); const isPrivate = !!(modifierFlags & ModifierFlags.Private); if (isStatic && (p.flags & (SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias))) { // Only value-only-meaning symbols can be correctly encoded as class statics, type/namespace/alias meaning symbols // need to be merged namespace members return []; } if ( p.flags & SymbolFlags.Prototype || p.escapedName === "constructor" || (baseType && getPropertyOfType(baseType, p.escapedName) && isReadonlySymbol(getPropertyOfType(baseType, p.escapedName)!) === isReadonlySymbol(p) && (p.flags & SymbolFlags.Optional) === (getPropertyOfType(baseType, p.escapedName)!.flags & SymbolFlags.Optional) && isTypeIdenticalTo(getTypeOfSymbol(p), getTypeOfPropertyOfType(baseType, p.escapedName)!)) ) { return []; } const flag = (modifierFlags & ~ModifierFlags.Async) | (isStatic ? ModifierFlags.Static : 0); const name = getPropertyNameNodeForSymbol(p, context); const firstPropertyLikeDecl = p.declarations?.find(or(isPropertyDeclaration, isAccessor, isVariableDeclaration, isPropertySignature, isBinaryExpression, isPropertyAccessExpression)); if (p.flags & SymbolFlags.Accessor && useAccessors) { const result: AccessorDeclaration[] = []; if (p.flags & SymbolFlags.SetAccessor) { const setter = p.declarations && forEach(p.declarations, d => { if (d.kind === SyntaxKind.SetAccessor) { return d as SetAccessorDeclaration; } if (isCallExpression(d) && isBindableObjectDefinePropertyCall(d)) { return forEach(d.arguments[2].properties, propDecl => { const id = getNameOfDeclaration(propDecl); if (!!id && isIdentifier(id) && idText(id) === "set") { return propDecl; } }); } }); Debug.assert(!!setter); const paramSymbol = isFunctionLikeDeclaration(setter) ? getSignatureFromDeclaration(setter).parameters[0] : undefined; result.push(setTextRange( context, factory.createSetAccessorDeclaration( factory.createModifiersFromModifierFlags(flag), name, [factory.createParameterDeclaration( /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, paramSymbol ? parameterToParameterDeclarationName(paramSymbol, getEffectiveParameterDeclaration(paramSymbol), context) : "value", /*questionToken*/ undefined, isPrivate ? undefined : serializeTypeForDeclaration(context, /*declaration*/ undefined, getWriteTypeOfSymbol(p), p), )], /*body*/ undefined, ), p.declarations?.find(isSetAccessor) || firstPropertyLikeDecl, )); } if (p.flags & SymbolFlags.GetAccessor) { const isPrivate = modifierFlags & ModifierFlags.Private; result.push(setTextRange( context, factory.createGetAccessorDeclaration( factory.createModifiersFromModifierFlags(flag), name, [], isPrivate ? undefined : serializeTypeForDeclaration(context, /*declaration*/ undefined, getTypeOfSymbol(p), p), /*body*/ undefined, ), p.declarations?.find(isGetAccessor) || firstPropertyLikeDecl, )); } return result; } // This is an else/if as accessors and properties can't merge in TS, but might in JS // If this happens, we assume the accessor takes priority, as it imposes more constraints else if (p.flags & (SymbolFlags.Property | SymbolFlags.Variable | SymbolFlags.Accessor)) { return setTextRange( context, createProperty( factory.createModifiersFromModifierFlags((isReadonlySymbol(p) ? ModifierFlags.Readonly : 0) | flag), name, p.flags & SymbolFlags.Optional ? factory.createToken(SyntaxKind.QuestionToken) : undefined, isPrivate ? undefined : serializeTypeForDeclaration(context, /*declaration*/ undefined, getWriteTypeOfSymbol(p), p), // TODO: https://github.com/microsoft/TypeScript/pull/32372#discussion_r328386357 // interface members can't have initializers, however class members _can_ /*initializer*/ undefined, ), p.declarations?.find(or(isPropertyDeclaration, isVariableDeclaration)) || firstPropertyLikeDecl, ); } if (p.flags & (SymbolFlags.Method | SymbolFlags.Function)) { const type = getTypeOfSymbol(p); const signatures = getSignaturesOfType(type, SignatureKind.Call); if (flag & ModifierFlags.Private) { return setTextRange( context, createProperty( factory.createModifiersFromModifierFlags((isReadonlySymbol(p) ? ModifierFlags.Readonly : 0) | flag), name, p.flags & SymbolFlags.Optional ? factory.createToken(SyntaxKind.QuestionToken) : undefined, /*type*/ undefined, /*initializer*/ undefined, ), p.declarations?.find(isFunctionLikeDeclaration) || signatures[0] && signatures[0].declaration || p.declarations && p.declarations[0], ); } const results = []; for (const sig of signatures) { // Each overload becomes a separate method declaration, in order const decl = signatureToSignatureDeclarationHelper( sig, methodKind, context, { name, questionToken: p.flags & SymbolFlags.Optional ? factory.createToken(SyntaxKind.QuestionToken) : undefined, modifiers: flag ? factory.createModifiersFromModifierFlags(flag) : undefined, }, ); const location = sig.declaration && isPrototypePropertyAssignment(sig.declaration.parent) ? sig.declaration.parent : sig.declaration; results.push(setTextRange(context, decl, location)); } return results as unknown as T[]; } // The `Constructor`'s symbol isn't in the class's properties lists, obviously, since it's a signature on the static return Debug.fail(`Unhandled class member kind! ${(p as any).__debugFlags || p.flags}`); }; } function serializePropertySymbolForInterface(p: Symbol, baseType: Type | undefined) { return serializePropertySymbolForInterfaceWorker(p, /*isStatic*/ false, baseType); } function serializeSignatures(kind: SignatureKind, input: Type, baseType: Type | undefined, outputKind: SignatureDeclaration["kind"]) { const signatures = getSignaturesOfType(input, kind); if (kind === SignatureKind.Construct) { if (!baseType && every(signatures, s => length(s.parameters) === 0)) { return []; // No base type, every constructor is empty - elide the extraneous `constructor()` } if (baseType) { // If there is a base type, if every signature in the class is identical to a signature in the baseType, elide all the declarations const baseSigs = getSignaturesOfType(baseType, SignatureKind.Construct); if (!length(baseSigs) && every(signatures, s => length(s.parameters) === 0)) { return []; // Base had no explicit signatures, if all our signatures are also implicit, return an empty list } if (baseSigs.length === signatures.length) { let failed = false; for (let i = 0; i < baseSigs.length; i++) { if (!compareSignaturesIdentical(signatures[i], baseSigs[i], /*partialMatch*/ false, /*ignoreThisTypes*/ false, /*ignoreReturnTypes*/ true, compareTypesIdentical)) { failed = true; break; } } if (!failed) { return []; // Every signature was identical - elide constructor list as it is inherited } } } let privateProtected: ModifierFlags = 0; for (const s of signatures) { if (s.declaration) { privateProtected |= getSelectedEffectiveModifierFlags(s.declaration, ModifierFlags.Private | ModifierFlags.Protected); } } if (privateProtected) { return [setTextRange( context, factory.createConstructorDeclaration( factory.createModifiersFromModifierFlags(privateProtected), /*parameters*/ [], /*body*/ undefined, ), signatures[0].declaration, )]; } } const results = []; for (const sig of signatures) { // Each overload becomes a separate constructor declaration, in order const decl = signatureToSignatureDeclarationHelper(sig, outputKind, context); results.push(setTextRange(context, decl, sig.declaration)); } return results; } function serializeIndexSignatures(input: Type, baseType: Type | undefined) { const results: IndexSignatureDeclaration[] = []; for (const info of getIndexInfosOfType(input)) { if (baseType) { const baseInfo = getIndexInfoOfType(baseType, info.keyType); if (baseInfo) { if (isTypeIdenticalTo(info.type, baseInfo.type)) { continue; // elide identical index signatures } } } results.push(indexInfoToIndexSignatureDeclarationHelper(info, context, /*typeNode*/ undefined)); } return results; } function serializeBaseType(t: Type, staticType: Type, rootName: string) { const ref = trySerializeAsTypeReference(t, SymbolFlags.Value); if (ref) { return ref; } const tempName = getUnusedName(`${rootName}_base`); const statement = factory.createVariableStatement( /*modifiers*/ undefined, factory.createVariableDeclarationList([ factory.createVariableDeclaration(tempName, /*exclamationToken*/ undefined, typeToTypeNodeHelper(staticType, context)), ], NodeFlags.Const), ); addResult(statement, ModifierFlags.None); return factory.createExpressionWithTypeArguments(factory.createIdentifier(tempName), /*typeArguments*/ undefined); } function trySerializeAsTypeReference(t: Type, flags: SymbolFlags) { let typeArgs: TypeNode[] | undefined; let reference: Expression | undefined; // We don't use `isValueSymbolAccessible` below. since that considers alternative containers (like modules) // which we can't write out in a syntactically valid way as an expression if ((t as TypeReference).target && isSymbolAccessibleByFlags((t as TypeReference).target.symbol, enclosingDeclaration, flags)) { typeArgs = map(getTypeArguments(t as TypeReference), t => typeToTypeNodeHelper(t, context)); reference = symbolToExpression((t as TypeReference).target.symbol, context, SymbolFlags.Type); } else if (t.symbol && isSymbolAccessibleByFlags(t.symbol, enclosingDeclaration, flags)) { reference = symbolToExpression(t.symbol, context, SymbolFlags.Type); } if (reference) { return factory.createExpressionWithTypeArguments(reference, typeArgs); } } function serializeImplementedType(t: Type) { const ref = trySerializeAsTypeReference(t, SymbolFlags.Type); if (ref) { return ref; } if (t.symbol) { return factory.createExpressionWithTypeArguments(symbolToExpression(t.symbol, context, SymbolFlags.Type), /*typeArguments*/ undefined); } } function getUnusedName(input: string, symbol?: Symbol): string { const id = symbol ? getSymbolId(symbol) : undefined; if (id) { if (context.remappedSymbolNames!.has(id)) { return context.remappedSymbolNames!.get(id)!; } } if (symbol) { input = getNameCandidateWorker(symbol, input); } let i = 0; const original = input; while (context.usedSymbolNames?.has(input)) { i++; input = `${original}_${i}`; } context.usedSymbolNames?.add(input); if (id) { context.remappedSymbolNames!.set(id, input); } return input; } function getNameCandidateWorker(symbol: Symbol, localName: string) { if (localName === InternalSymbolName.Default || localName === InternalSymbolName.Class || localName === InternalSymbolName.Function) { const restoreFlags = saveRestoreFlags(context); context.flags |= NodeBuilderFlags.InInitialEntityName; const nameCandidate = getNameOfSymbolAsWritten(symbol, context); restoreFlags(); localName = nameCandidate.length > 0 && isSingleOrDoubleQuote(nameCandidate.charCodeAt(0)) ? stripQuotes(nameCandidate) : nameCandidate; } if (localName === InternalSymbolName.Default) { localName = "_default"; } else if (localName === InternalSymbolName.ExportEquals) { localName = "_exports"; } localName = isIdentifierText(localName, languageVersion) && !isStringANonContextualKeyword(localName) ? localName : "_" + localName.replace(/[^a-z0-9]/gi, "_"); return localName; } function getInternalSymbolName(symbol: Symbol, localName: string) { const id = getSymbolId(symbol); if (context.remappedSymbolNames!.has(id)) { return context.remappedSymbolNames!.get(id)!; } localName = getNameCandidateWorker(symbol, localName); // The result of this is going to be used as the symbol's name - lock it in, so `getUnusedName` will also pick it up context.remappedSymbolNames!.set(id, localName); return localName; } } } function typePredicateToString(typePredicate: TypePredicate, enclosingDeclaration?: Node, flags: TypeFormatFlags = TypeFormatFlags.UseAliasDefinedOutsideCurrentScope, writer?: EmitTextWriter): string { return writer ? typePredicateToStringWorker(writer).getText() : usingSingleLineStringWriter(typePredicateToStringWorker); function typePredicateToStringWorker(writer: EmitTextWriter) { const nodeBuilderFlags = toNodeBuilderFlags(flags) | NodeBuilderFlags.IgnoreErrors | NodeBuilderFlags.WriteTypeParametersInQualifiedName; const predicate = nodeBuilder.typePredicateToTypePredicateNode(typePredicate, enclosingDeclaration, nodeBuilderFlags)!; // TODO: GH#18217 const printer = createPrinterWithRemoveComments(); const sourceFile = enclosingDeclaration && getSourceFileOfNode(enclosingDeclaration); printer.writeNode(EmitHint.Unspecified, predicate, /*sourceFile*/ sourceFile, writer); return writer; } } function formatUnionTypes(types: readonly Type[]): Type[] { const result: Type[] = []; let flags = 0 as TypeFlags; for (let i = 0; i < types.length; i++) { const t = types[i]; flags |= t.flags; if (!(t.flags & TypeFlags.Nullable)) { if (t.flags & (TypeFlags.BooleanLiteral | TypeFlags.EnumLike)) { const baseType = t.flags & TypeFlags.BooleanLiteral ? booleanType : getBaseTypeOfEnumLikeType(t as LiteralType); if (baseType.flags & TypeFlags.Union) { const count = (baseType as UnionType).types.length; if (i + count <= types.length && getRegularTypeOfLiteralType(types[i + count - 1]) === getRegularTypeOfLiteralType((baseType as UnionType).types[count - 1])) { result.push(baseType); i += count - 1; continue; } } } result.push(t); } } if (flags & TypeFlags.Null) result.push(nullType); if (flags & TypeFlags.Undefined) result.push(undefinedType); return result || types; } function visibilityToString(flags: ModifierFlags): string { if (flags === ModifierFlags.Private) { return "private"; } if (flags === ModifierFlags.Protected) { return "protected"; } return "public"; } function getTypeAliasForTypeLiteral(type: Type): Symbol | undefined { if (type.symbol && type.symbol.flags & SymbolFlags.TypeLiteral && type.symbol.declarations) { const node = walkUpParenthesizedTypes(type.symbol.declarations[0].parent); if (isTypeAliasDeclaration(node)) { return getSymbolOfDeclaration(node); } } return undefined; } function isTopLevelInExternalModuleAugmentation(node: Node): boolean { return node && node.parent && node.parent.kind === SyntaxKind.ModuleBlock && isExternalModuleAugmentation(node.parent.parent); } function isDefaultBindingContext(location: Node) { return location.kind === SyntaxKind.SourceFile || isAmbientModule(location); } function getNameOfSymbolFromNameType(symbol: Symbol, context?: NodeBuilderContext) { const nameType = getSymbolLinks(symbol).nameType; if (nameType) { if (nameType.flags & TypeFlags.StringOrNumberLiteral) { const name = "" + (nameType as StringLiteralType | NumberLiteralType).value; if (!isIdentifierText(name, getEmitScriptTarget(compilerOptions)) && !isNumericLiteralName(name)) { return `"${escapeString(name, CharacterCodes.doubleQuote)}"`; } if (isNumericLiteralName(name) && startsWith(name, "-")) { return `[${name}]`; } return name; } if (nameType.flags & TypeFlags.UniqueESSymbol) { return `[${getNameOfSymbolAsWritten((nameType as UniqueESSymbolType).symbol, context)}]`; } } } /** * Gets a human-readable name for a symbol. * Should *not* be used for the right-hand side of a `.` -- use `symbolName(symbol)` for that instead. * * Unlike `symbolName(symbol)`, this will include quotes if the name is from a string literal. * It will also use a representation of a number as written instead of a decimal form, e.g. `0o11` instead of `9`. */ function getNameOfSymbolAsWritten(symbol: Symbol, context?: NodeBuilderContext): string { if (context?.remappedSymbolReferences?.has(getSymbolId(symbol))) { symbol = context.remappedSymbolReferences.get(getSymbolId(symbol))!; } if ( context && symbol.escapedName === InternalSymbolName.Default && !(context.flags & NodeBuilderFlags.UseAliasDefinedOutsideCurrentScope) && // If it's not the first part of an entity name, it must print as `default` (!(context.flags & NodeBuilderFlags.InInitialEntityName) || // if the symbol is synthesized, it will only be referenced externally it must print as `default` !symbol.declarations || // if not in the same binding context (source file, module declaration), it must print as `default` (context.enclosingDeclaration && findAncestor(symbol.declarations[0], isDefaultBindingContext) !== findAncestor(context.enclosingDeclaration, isDefaultBindingContext))) ) { return "default"; } if (symbol.declarations && symbol.declarations.length) { let declaration = firstDefined(symbol.declarations, d => getNameOfDeclaration(d) ? d : undefined); // Try using a declaration with a name, first const name = declaration && getNameOfDeclaration(declaration); if (declaration && name) { if (isCallExpression(declaration) && isBindableObjectDefinePropertyCall(declaration)) { return symbolName(symbol); } if (isComputedPropertyName(name) && !(getCheckFlags(symbol) & CheckFlags.Late)) { const nameType = getSymbolLinks(symbol).nameType; if (nameType && nameType.flags & TypeFlags.StringOrNumberLiteral) { // Computed property name isn't late bound, but has a well-known name type - use name type to generate a symbol name const result = getNameOfSymbolFromNameType(symbol, context); if (result !== undefined) { return result; } } } return declarationNameToString(name); } if (!declaration) { declaration = symbol.declarations[0]; // Declaration may be nameless, but we'll try anyway } if (declaration.parent && declaration.parent.kind === SyntaxKind.VariableDeclaration) { return declarationNameToString((declaration.parent as VariableDeclaration).name); } switch (declaration.kind) { case SyntaxKind.ClassExpression: case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: if (context && !context.encounteredError && !(context.flags & NodeBuilderFlags.AllowAnonymousIdentifier)) { context.encounteredError = true; } return declaration.kind === SyntaxKind.ClassExpression ? "(Anonymous class)" : "(Anonymous function)"; } } const name = getNameOfSymbolFromNameType(symbol, context); return name !== undefined ? name : symbolName(symbol); } function isDeclarationVisible(node: Node): boolean { if (node) { const links = getNodeLinks(node); if (links.isVisible === undefined) { links.isVisible = !!determineIfDeclarationIsVisible(); } return links.isVisible; } return false; function determineIfDeclarationIsVisible() { switch (node.kind) { case SyntaxKind.JSDocCallbackTag: case SyntaxKind.JSDocTypedefTag: case SyntaxKind.JSDocEnumTag: // Top-level jsdoc type aliases are considered exported // First parent is comment node, second is hosting declaration or token; we only care about those tokens or declarations whose parent is a source file return !!(node.parent && node.parent.parent && node.parent.parent.parent && isSourceFile(node.parent.parent.parent)); case SyntaxKind.BindingElement: return isDeclarationVisible(node.parent.parent); case SyntaxKind.VariableDeclaration: if ( isBindingPattern((node as VariableDeclaration).name) && !((node as VariableDeclaration).name as BindingPattern).elements.length ) { // If the binding pattern is empty, this variable declaration is not visible return false; } // falls through case SyntaxKind.ModuleDeclaration: case SyntaxKind.ClassDeclaration: case SyntaxKind.InterfaceDeclaration: case SyntaxKind.TypeAliasDeclaration: case SyntaxKind.FunctionDeclaration: case SyntaxKind.EnumDeclaration: case SyntaxKind.ImportEqualsDeclaration: // external module augmentation is always visible if (isExternalModuleAugmentation(node)) { return true; } const parent = getDeclarationContainer(node); // If the node is not exported or it is not ambient module element (except import declaration) if ( !(getCombinedModifierFlagsCached(node as Declaration) & ModifierFlags.Export) && !(node.kind !== SyntaxKind.ImportEqualsDeclaration && parent.kind !== SyntaxKind.SourceFile && parent.flags & NodeFlags.Ambient) ) { return isGlobalSourceFile(parent); } // Exported members/ambient module elements (exception import declaration) are visible if parent is visible return isDeclarationVisible(parent); case SyntaxKind.PropertyDeclaration: case SyntaxKind.PropertySignature: case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: case SyntaxKind.MethodDeclaration: case SyntaxKind.MethodSignature: if (hasEffectiveModifier(node, ModifierFlags.Private | ModifierFlags.Protected)) { // Private/protected properties/methods are not visible return false; } // Public properties/methods are visible if its parents are visible, so: // falls through case SyntaxKind.Constructor: case SyntaxKind.ConstructSignature: case SyntaxKind.CallSignature: case SyntaxKind.IndexSignature: case SyntaxKind.Parameter: case SyntaxKind.ModuleBlock: case SyntaxKind.FunctionType: case SyntaxKind.ConstructorType: case SyntaxKind.TypeLiteral: case SyntaxKind.TypeReference: case SyntaxKind.ArrayType: case SyntaxKind.TupleType: case SyntaxKind.UnionType: case SyntaxKind.IntersectionType: case SyntaxKind.ParenthesizedType: case SyntaxKind.NamedTupleMember: return isDeclarationVisible(node.parent); // Default binding, import specifier and namespace import is visible // only on demand so by default it is not visible case SyntaxKind.ImportClause: case SyntaxKind.NamespaceImport: case SyntaxKind.ImportSpecifier: return false; // Type parameters are always visible case SyntaxKind.TypeParameter: // Source file and namespace export are always visible // falls through case SyntaxKind.SourceFile: case SyntaxKind.NamespaceExportDeclaration: return true; // Export assignments do not create name bindings outside the module case SyntaxKind.ExportAssignment: return false; default: return false; } } } function collectLinkedAliases(node: ModuleExportName, setVisibility?: boolean): Node[] | undefined { let exportSymbol: Symbol | undefined; if (node.kind !== SyntaxKind.StringLiteral && node.parent && node.parent.kind === SyntaxKind.ExportAssignment) { exportSymbol = resolveName(node, node, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias, /*nameNotFoundMessage*/ undefined, /*isUse*/ false); } else if (node.parent.kind === SyntaxKind.ExportSpecifier) { exportSymbol = getTargetOfExportSpecifier(node.parent as ExportSpecifier, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias); } let result: Node[] | undefined; let visited: Set | undefined; if (exportSymbol) { visited = new Set(); visited.add(getSymbolId(exportSymbol)); buildVisibleNodeList(exportSymbol.declarations); } return result; function buildVisibleNodeList(declarations: Declaration[] | undefined) { forEach(declarations, declaration => { const resultNode = getAnyImportSyntax(declaration) || declaration; if (setVisibility) { getNodeLinks(declaration).isVisible = true; } else { result = result || []; pushIfUnique(result, resultNode); } if (isInternalModuleImportEqualsDeclaration(declaration)) { // Add the referenced top container visible const internalModuleReference = declaration.moduleReference as Identifier | QualifiedName; const firstIdentifier = getFirstIdentifier(internalModuleReference); const importSymbol = resolveName(declaration, firstIdentifier.escapedText, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace, /*nameNotFoundMessage*/ undefined, /*isUse*/ false); if (importSymbol && visited) { if (tryAddToSet(visited, getSymbolId(importSymbol))) { buildVisibleNodeList(importSymbol.declarations); } } } }); } } /** * Push an entry on the type resolution stack. If an entry with the given target and the given property name * is already on the stack, and no entries in between already have a type, then a circularity has occurred. * In this case, the result values of the existing entry and all entries pushed after it are changed to false, * and the value false is returned. Otherwise, the new entry is just pushed onto the stack, and true is returned. * In order to see if the same query has already been done before, the target object and the propertyName both * must match the one passed in. * * @param target The symbol, type, or signature whose type is being queried * @param propertyName The property name that should be used to query the target for its type */ function pushTypeResolution(target: TypeSystemEntity, propertyName: TypeSystemPropertyName): boolean { const resolutionCycleStartIndex = findResolutionCycleStartIndex(target, propertyName); if (resolutionCycleStartIndex >= 0) { // A cycle was found const { length } = resolutionTargets; for (let i = resolutionCycleStartIndex; i < length; i++) { resolutionResults[i] = false; } return false; } resolutionTargets.push(target); resolutionResults.push(/*items*/ true); resolutionPropertyNames.push(propertyName); return true; } function findResolutionCycleStartIndex(target: TypeSystemEntity, propertyName: TypeSystemPropertyName): number { for (let i = resolutionTargets.length - 1; i >= resolutionStart; i--) { if (resolutionTargetHasProperty(resolutionTargets[i], resolutionPropertyNames[i])) { return -1; } if (resolutionTargets[i] === target && resolutionPropertyNames[i] === propertyName) { return i; } } return -1; } function resolutionTargetHasProperty(target: TypeSystemEntity, propertyName: TypeSystemPropertyName): boolean { switch (propertyName) { case TypeSystemPropertyName.Type: return !!getSymbolLinks(target as Symbol).type; case TypeSystemPropertyName.DeclaredType: return !!getSymbolLinks(target as Symbol).declaredType; case TypeSystemPropertyName.ResolvedBaseConstructorType: return !!(target as InterfaceType).resolvedBaseConstructorType; case TypeSystemPropertyName.ResolvedReturnType: return !!(target as Signature).resolvedReturnType; case TypeSystemPropertyName.ImmediateBaseConstraint: return !!(target as Type).immediateBaseConstraint; case TypeSystemPropertyName.ResolvedTypeArguments: return !!(target as TypeReference).resolvedTypeArguments; case TypeSystemPropertyName.ResolvedBaseTypes: return !!(target as InterfaceType).baseTypesResolved; case TypeSystemPropertyName.WriteType: return !!getSymbolLinks(target as Symbol).writeType; case TypeSystemPropertyName.ParameterInitializerContainsUndefined: return getNodeLinks(target as ParameterDeclaration).parameterInitializerContainsUndefined !== undefined; } return Debug.assertNever(propertyName); } /** * Pop an entry from the type resolution stack and return its associated result value. The result value will * be true if no circularities were detected, or false if a circularity was found. */ function popTypeResolution(): boolean { resolutionTargets.pop(); resolutionPropertyNames.pop(); return resolutionResults.pop()!; } function getDeclarationContainer(node: Node): Node { return findAncestor(getRootDeclaration(node), node => { switch (node.kind) { case SyntaxKind.VariableDeclaration: case SyntaxKind.VariableDeclarationList: case SyntaxKind.ImportSpecifier: case SyntaxKind.NamedImports: case SyntaxKind.NamespaceImport: case SyntaxKind.ImportClause: return false; default: return true; } })!.parent; } function getTypeOfPrototypeProperty(prototype: Symbol): Type { // TypeScript 1.0 spec (April 2014): 8.4 // Every class automatically contains a static property member named 'prototype', // the type of which is an instantiation of the class type with type Any supplied as a type argument for each type parameter. // It is an error to explicitly declare a static property member with the name 'prototype'. const classType = getDeclaredTypeOfSymbol(getParentOfSymbol(prototype)!) as InterfaceType; return classType.typeParameters ? createTypeReference(classType as GenericType, map(classType.typeParameters, _ => anyType)) : classType; } // Return the type of the given property in the given type, or undefined if no such property exists function getTypeOfPropertyOfType(type: Type, name: __String): Type | undefined { const prop = getPropertyOfType(type, name); return prop ? getTypeOfSymbol(prop) : undefined; } /** * Return the type of the matching property or index signature in the given type, or undefined * if no matching property or index signature exists. Add optionality to index signature types. */ function getTypeOfPropertyOrIndexSignatureOfType(type: Type, name: __String): Type | undefined { let propType; return getTypeOfPropertyOfType(type, name) || (propType = getApplicableIndexInfoForName(type, name)?.type) && addOptionality(propType, /*isProperty*/ true, /*isOptional*/ true); } function isTypeAny(type: Type | undefined) { return type && (type.flags & TypeFlags.Any) !== 0; } function isErrorType(type: Type) { // The only 'any' types that have alias symbols are those manufactured by getTypeFromTypeAliasReference for // a reference to an unresolved symbol. We want those to behave like the errorType. return type === errorType || !!(type.flags & TypeFlags.Any && type.aliasSymbol); } // Return the type of a binding element parent. We check SymbolLinks first to see if a type has been // assigned by contextual typing. function getTypeForBindingElementParent(node: BindingElementGrandparent, checkMode: CheckMode) { if (checkMode !== CheckMode.Normal) { return getTypeForVariableLikeDeclaration(node, /*includeOptionality*/ false, checkMode); } const symbol = getSymbolOfDeclaration(node); return symbol && getSymbolLinks(symbol).type || getTypeForVariableLikeDeclaration(node, /*includeOptionality*/ false, checkMode); } function getRestType(source: Type, properties: PropertyName[], symbol: Symbol | undefined): Type { source = filterType(source, t => !(t.flags & TypeFlags.Nullable)); if (source.flags & TypeFlags.Never) { return emptyObjectType; } if (source.flags & TypeFlags.Union) { return mapType(source, t => getRestType(t, properties, symbol)); } let omitKeyType = getUnionType(map(properties, getLiteralTypeFromPropertyName)); const spreadableProperties: Symbol[] = []; const unspreadableToRestKeys: Type[] = []; for (const prop of getPropertiesOfType(source)) { const literalTypeFromProperty = getLiteralTypeFromProperty(prop, TypeFlags.StringOrNumberLiteralOrUnique); if ( !isTypeAssignableTo(literalTypeFromProperty, omitKeyType) && !(getDeclarationModifierFlagsFromSymbol(prop) & (ModifierFlags.Private | ModifierFlags.Protected)) && isSpreadableProperty(prop) ) { spreadableProperties.push(prop); } else { unspreadableToRestKeys.push(literalTypeFromProperty); } } if (isGenericObjectType(source) || isGenericIndexType(omitKeyType)) { if (unspreadableToRestKeys.length) { // If the type we're spreading from has properties that cannot // be spread into the rest type (e.g. getters, methods), ensure // they are explicitly omitted, as they would in the non-generic case. omitKeyType = getUnionType([omitKeyType, ...unspreadableToRestKeys]); } if (omitKeyType.flags & TypeFlags.Never) { return source; } const omitTypeAlias = getGlobalOmitSymbol(); if (!omitTypeAlias) { return errorType; } return getTypeAliasInstantiation(omitTypeAlias, [source, omitKeyType]); } const members = createSymbolTable(); for (const prop of spreadableProperties) { members.set(prop.escapedName, getSpreadSymbol(prop, /*readonly*/ false)); } const result = createAnonymousType(symbol, members, emptyArray, emptyArray, getIndexInfosOfType(source)); result.objectFlags |= ObjectFlags.ObjectRestType; return result; } function isGenericTypeWithUndefinedConstraint(type: Type) { return !!(type.flags & TypeFlags.Instantiable) && maybeTypeOfKind(getBaseConstraintOfType(type) || unknownType, TypeFlags.Undefined); } function getNonUndefinedType(type: Type) { const typeOrConstraint = someType(type, isGenericTypeWithUndefinedConstraint) ? mapType(type, t => t.flags & TypeFlags.Instantiable ? getBaseConstraintOrType(t) : t) : type; return getTypeWithFacts(typeOrConstraint, TypeFacts.NEUndefined); } // Determine the control flow type associated with a destructuring declaration or assignment. The following // forms of destructuring are possible: // let { x } = obj; // BindingElement // let [ x ] = obj; // BindingElement // { x } = obj; // ShorthandPropertyAssignment // { x: v } = obj; // PropertyAssignment // [ x ] = obj; // Expression // We construct a synthetic element access expression corresponding to 'obj.x' such that the control // flow analyzer doesn't have to handle all the different syntactic forms. function getFlowTypeOfDestructuring(node: BindingElement | PropertyAssignment | ShorthandPropertyAssignment | Expression, declaredType: Type) { const reference = getSyntheticElementAccess(node); return reference ? getFlowTypeOfReference(reference, declaredType) : declaredType; } function getSyntheticElementAccess(node: BindingElement | PropertyAssignment | ShorthandPropertyAssignment | Expression): ElementAccessExpression | undefined { const parentAccess = getParentElementAccess(node); if (parentAccess && canHaveFlowNode(parentAccess) && parentAccess.flowNode) { const propName = getDestructuringPropertyName(node); if (propName) { const literal = setTextRangeWorker(parseNodeFactory.createStringLiteral(propName), node); const lhsExpr = isLeftHandSideExpression(parentAccess) ? parentAccess : parseNodeFactory.createParenthesizedExpression(parentAccess); const result = setTextRangeWorker(parseNodeFactory.createElementAccessExpression(lhsExpr, literal), node); setParent(literal, result); setParent(result, node); if (lhsExpr !== parentAccess) { setParent(lhsExpr, result); } result.flowNode = parentAccess.flowNode; return result; } } } function getParentElementAccess(node: BindingElement | PropertyAssignment | ShorthandPropertyAssignment | Expression) { const ancestor = node.parent.parent; switch (ancestor.kind) { case SyntaxKind.BindingElement: case SyntaxKind.PropertyAssignment: return getSyntheticElementAccess(ancestor as BindingElement | PropertyAssignment); case SyntaxKind.ArrayLiteralExpression: return getSyntheticElementAccess(node.parent as Expression); case SyntaxKind.VariableDeclaration: return (ancestor as VariableDeclaration).initializer; case SyntaxKind.BinaryExpression: return (ancestor as BinaryExpression).right; } } function getDestructuringPropertyName(node: BindingElement | PropertyAssignment | ShorthandPropertyAssignment | Expression) { const parent = node.parent; if (node.kind === SyntaxKind.BindingElement && parent.kind === SyntaxKind.ObjectBindingPattern) { return getLiteralPropertyNameText((node as BindingElement).propertyName || (node as BindingElement).name as Identifier); } if (node.kind === SyntaxKind.PropertyAssignment || node.kind === SyntaxKind.ShorthandPropertyAssignment) { return getLiteralPropertyNameText((node as PropertyAssignment | ShorthandPropertyAssignment).name); } return "" + ((parent as BindingPattern | ArrayLiteralExpression).elements as NodeArray).indexOf(node); } function getLiteralPropertyNameText(name: PropertyName) { const type = getLiteralTypeFromPropertyName(name); return type.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral) ? "" + (type as StringLiteralType | NumberLiteralType).value : undefined; } /** Return the inferred type for a binding element */ function getTypeForBindingElement(declaration: BindingElement): Type | undefined { const checkMode = declaration.dotDotDotToken ? CheckMode.RestBindingElement : CheckMode.Normal; const parentType = getTypeForBindingElementParent(declaration.parent.parent, checkMode); return parentType && getBindingElementTypeFromParentType(declaration, parentType, /*noTupleBoundsCheck*/ false); } function getBindingElementTypeFromParentType(declaration: BindingElement, parentType: Type, noTupleBoundsCheck: boolean): Type { // If an any type was inferred for parent, infer that for the binding element if (isTypeAny(parentType)) { return parentType; } const pattern = declaration.parent; // Relax null check on ambient destructuring parameters, since the parameters have no implementation and are just documentation if (strictNullChecks && declaration.flags & NodeFlags.Ambient && isPartOfParameterDeclaration(declaration)) { parentType = getNonNullableType(parentType); } // Filter `undefined` from the type we check against if the parent has an initializer and that initializer is not possibly `undefined` else if (strictNullChecks && pattern.parent.initializer && !(hasTypeFacts(getTypeOfInitializer(pattern.parent.initializer), TypeFacts.EQUndefined))) { parentType = getTypeWithFacts(parentType, TypeFacts.NEUndefined); } const accessFlags = AccessFlags.ExpressionPosition | (noTupleBoundsCheck || hasDefaultValue(declaration) ? AccessFlags.AllowMissing : 0); let type: Type | undefined; if (pattern.kind === SyntaxKind.ObjectBindingPattern) { if (declaration.dotDotDotToken) { parentType = getReducedType(parentType); if (parentType.flags & TypeFlags.Unknown || !isValidSpreadType(parentType)) { error(declaration, Diagnostics.Rest_types_may_only_be_created_from_object_types); return errorType; } const literalMembers: PropertyName[] = []; for (const element of pattern.elements) { if (!element.dotDotDotToken) { literalMembers.push(element.propertyName || element.name as Identifier); } } type = getRestType(parentType, literalMembers, declaration.symbol); } else { // Use explicitly specified property name ({ p: xxx } form), or otherwise the implied name ({ p } form) const name = declaration.propertyName || declaration.name as Identifier; const indexType = getLiteralTypeFromPropertyName(name); const declaredType = getIndexedAccessType(parentType, indexType, accessFlags, name); type = getFlowTypeOfDestructuring(declaration, declaredType); } } else { // This elementType will be used if the specific property corresponding to this index is not // present (aka the tuple element property). This call also checks that the parentType is in // fact an iterable or array (depending on target language). const elementType = checkIteratedTypeOrElementType(IterationUse.Destructuring | (declaration.dotDotDotToken ? 0 : IterationUse.PossiblyOutOfBounds), parentType, undefinedType, pattern); const index = pattern.elements.indexOf(declaration); if (declaration.dotDotDotToken) { // If the parent is a tuple type, the rest element has a tuple type of the // remaining tuple element types. Otherwise, the rest element has an array type with same // element type as the parent type. const baseConstraint = mapType(parentType, t => t.flags & TypeFlags.InstantiableNonPrimitive ? getBaseConstraintOrType(t) : t); type = everyType(baseConstraint, isTupleType) ? mapType(baseConstraint, t => sliceTupleType(t as TupleTypeReference, index)) : createArrayType(elementType); } else if (isArrayLikeType(parentType)) { const indexType = getNumberLiteralType(index); const declaredType = getIndexedAccessTypeOrUndefined(parentType, indexType, accessFlags, declaration.name) || errorType; type = getFlowTypeOfDestructuring(declaration, declaredType); } else { type = elementType; } } if (!declaration.initializer) { return type; } if (getEffectiveTypeAnnotationNode(walkUpBindingElementsAndPatterns(declaration))) { // In strict null checking mode, if a default value of a non-undefined type is specified, remove // undefined from the final type. return strictNullChecks && !(hasTypeFacts(checkDeclarationInitializer(declaration, CheckMode.Normal), TypeFacts.IsUndefined)) ? getNonUndefinedType(type) : type; } return widenTypeInferredFromInitializer(declaration, getUnionType([getNonUndefinedType(type), checkDeclarationInitializer(declaration, CheckMode.Normal)], UnionReduction.Subtype)); } function getTypeForDeclarationFromJSDocComment(declaration: Node) { const jsdocType = getJSDocType(declaration); if (jsdocType) { return getTypeFromTypeNode(jsdocType); } return undefined; } function isNullOrUndefined(node: Expression) { const expr = skipParentheses(node, /*excludeJSDocTypeAssertions*/ true); return expr.kind === SyntaxKind.NullKeyword || expr.kind === SyntaxKind.Identifier && getResolvedSymbol(expr as Identifier) === undefinedSymbol; } function isEmptyArrayLiteral(node: Expression) { const expr = skipParentheses(node, /*excludeJSDocTypeAssertions*/ true); return expr.kind === SyntaxKind.ArrayLiteralExpression && (expr as ArrayLiteralExpression).elements.length === 0; } function addOptionality(type: Type, isProperty = false, isOptional = true): Type { return strictNullChecks && isOptional ? getOptionalType(type, isProperty) : type; } // Return the inferred type for a variable, parameter, or property declaration function getTypeForVariableLikeDeclaration( declaration: ParameterDeclaration | PropertyDeclaration | PropertySignature | VariableDeclaration | BindingElement | JSDocPropertyLikeTag, includeOptionality: boolean, checkMode: CheckMode, ): Type | undefined { // A variable declared in a for..in statement is of type string, or of type keyof T when the // right hand expression is of a type parameter type. if (isVariableDeclaration(declaration) && declaration.parent.parent.kind === SyntaxKind.ForInStatement) { const indexType = getIndexType(getNonNullableTypeIfNeeded(checkExpression(declaration.parent.parent.expression, /*checkMode*/ checkMode))); return indexType.flags & (TypeFlags.TypeParameter | TypeFlags.Index) ? getExtractStringType(indexType) : stringType; } if (isVariableDeclaration(declaration) && declaration.parent.parent.kind === SyntaxKind.ForOfStatement) { // checkRightHandSideOfForOf will return undefined if the for-of expression type was // missing properties/signatures required to get its iteratedType (like // [Symbol.iterator] or next). This may be because we accessed properties from anyType, // or it may have led to an error inside getElementTypeOfIterable. const forOfStatement = declaration.parent.parent; return checkRightHandSideOfForOf(forOfStatement) || anyType; } if (isBindingPattern(declaration.parent)) { return getTypeForBindingElement(declaration as BindingElement); } const isProperty = (isPropertyDeclaration(declaration) && !hasAccessorModifier(declaration)) || isPropertySignature(declaration) || isJSDocPropertyTag(declaration); const isOptional = includeOptionality && isOptionalDeclaration(declaration); // Use type from type annotation if one is present const declaredType = tryGetTypeFromEffectiveTypeNode(declaration); if (isCatchClauseVariableDeclarationOrBindingElement(declaration)) { if (declaredType) { // If the catch clause is explicitly annotated with any or unknown, accept it, otherwise error. return isTypeAny(declaredType) || declaredType === unknownType ? declaredType : errorType; } // If the catch clause is not explicitly annotated, treat it as though it were explicitly // annotated with unknown or any, depending on useUnknownInCatchVariables. return useUnknownInCatchVariables ? unknownType : anyType; } if (declaredType) { return addOptionality(declaredType, isProperty, isOptional); } if ( (noImplicitAny || isInJSFile(declaration)) && isVariableDeclaration(declaration) && !isBindingPattern(declaration.name) && !(getCombinedModifierFlagsCached(declaration) & ModifierFlags.Export) && !(declaration.flags & NodeFlags.Ambient) ) { // If --noImplicitAny is on or the declaration is in a Javascript file, // use control flow tracked 'any' type for non-ambient, non-exported var or let variables with no // initializer or a 'null' or 'undefined' initializer. if (!(getCombinedNodeFlagsCached(declaration) & NodeFlags.Constant) && (!declaration.initializer || isNullOrUndefined(declaration.initializer))) { return autoType; } // Use control flow tracked 'any[]' type for non-ambient, non-exported variables with an empty array // literal initializer. if (declaration.initializer && isEmptyArrayLiteral(declaration.initializer)) { return autoArrayType; } } if (isParameter(declaration)) { if (!declaration.symbol) { // parameters of function types defined in JSDoc in TS files don't have symbols return; } const func = declaration.parent as FunctionLikeDeclaration; // For a parameter of a set accessor, use the type of the get accessor if one is present if (func.kind === SyntaxKind.SetAccessor && hasBindableName(func)) { const getter = getDeclarationOfKind(getSymbolOfDeclaration(declaration.parent), SyntaxKind.GetAccessor); if (getter) { const getterSignature = getSignatureFromDeclaration(getter); const thisParameter = getAccessorThisParameter(func as AccessorDeclaration); if (thisParameter && declaration === thisParameter) { // Use the type from the *getter* Debug.assert(!thisParameter.type); return getTypeOfSymbol(getterSignature.thisParameter!); } return getReturnTypeOfSignature(getterSignature); } } const parameterTypeOfTypeTag = getParameterTypeOfTypeTag(func, declaration); if (parameterTypeOfTypeTag) return parameterTypeOfTypeTag; // Use contextual parameter type if one is available const type = declaration.symbol.escapedName === InternalSymbolName.This ? getContextualThisParameterType(func) : getContextuallyTypedParameterType(declaration); if (type) { return addOptionality(type, /*isProperty*/ false, isOptional); } } // Use the type of the initializer expression if one is present and the declaration is // not a parameter of a contextually typed function if (hasOnlyExpressionInitializer(declaration) && !!declaration.initializer) { if (isInJSFile(declaration) && !isParameter(declaration)) { const containerObjectType = getJSContainerObjectType(declaration, getSymbolOfDeclaration(declaration), getDeclaredExpandoInitializer(declaration)); if (containerObjectType) { return containerObjectType; } } const type = widenTypeInferredFromInitializer(declaration, checkDeclarationInitializer(declaration, checkMode)); return addOptionality(type, isProperty, isOptional); } if (isPropertyDeclaration(declaration) && (noImplicitAny || isInJSFile(declaration))) { // We have a property declaration with no type annotation or initializer, in noImplicitAny mode or a .js file. // Use control flow analysis of this.xxx assignments in the constructor or static block to determine the type of the property. if (!hasStaticModifier(declaration)) { const constructor = findConstructorDeclaration(declaration.parent); const type = constructor ? getFlowTypeInConstructor(declaration.symbol, constructor) : getEffectiveModifierFlags(declaration) & ModifierFlags.Ambient ? getTypeOfPropertyInBaseClass(declaration.symbol) : undefined; return type && addOptionality(type, /*isProperty*/ true, isOptional); } else { const staticBlocks = filter(declaration.parent.members, isClassStaticBlockDeclaration); const type = staticBlocks.length ? getFlowTypeInStaticBlocks(declaration.symbol, staticBlocks) : getEffectiveModifierFlags(declaration) & ModifierFlags.Ambient ? getTypeOfPropertyInBaseClass(declaration.symbol) : undefined; return type && addOptionality(type, /*isProperty*/ true, isOptional); } } if (isJsxAttribute(declaration)) { // if JSX attribute doesn't have initializer, by default the attribute will have boolean value of true. // I.e is sugar for return trueType; } // If the declaration specifies a binding pattern and is not a parameter of a contextually // typed function, use the type implied by the binding pattern if (isBindingPattern(declaration.name)) { return getTypeFromBindingPattern(declaration.name, /*includePatternInType*/ false, /*reportErrors*/ true); } // No type specified and nothing can be inferred return undefined; } function isConstructorDeclaredProperty(symbol: Symbol) { // A property is considered a constructor declared property when all declaration sites are this.xxx assignments, // when no declaration sites have JSDoc type annotations, and when at least one declaration site is in the body of // a class constructor. if (symbol.valueDeclaration && isBinaryExpression(symbol.valueDeclaration)) { const links = getSymbolLinks(symbol); if (links.isConstructorDeclaredProperty === undefined) { links.isConstructorDeclaredProperty = false; links.isConstructorDeclaredProperty = !!getDeclaringConstructor(symbol) && every(symbol.declarations, declaration => isBinaryExpression(declaration) && isPossiblyAliasedThisProperty(declaration) && (declaration.left.kind !== SyntaxKind.ElementAccessExpression || isStringOrNumericLiteralLike((declaration.left as ElementAccessExpression).argumentExpression)) && !getAnnotatedTypeForAssignmentDeclaration(/*declaredType*/ undefined, declaration, symbol, declaration)); } return links.isConstructorDeclaredProperty; } return false; } function isAutoTypedProperty(symbol: Symbol) { // A property is auto-typed when its declaration has no type annotation or initializer and we're in // noImplicitAny mode or a .js file. const declaration = symbol.valueDeclaration; return declaration && isPropertyDeclaration(declaration) && !getEffectiveTypeAnnotationNode(declaration) && !declaration.initializer && (noImplicitAny || isInJSFile(declaration)); } function getDeclaringConstructor(symbol: Symbol) { if (!symbol.declarations) { return; } for (const declaration of symbol.declarations) { const container = getThisContainer(declaration, /*includeArrowFunctions*/ false, /*includeClassComputedPropertyName*/ false); if (container && (container.kind === SyntaxKind.Constructor || isJSConstructor(container))) { return container as ConstructorDeclaration; } } } /** Create a synthetic property access flow node after the last statement of the file */ function getFlowTypeFromCommonJSExport(symbol: Symbol) { const file = getSourceFileOfNode(symbol.declarations![0]); const accessName = unescapeLeadingUnderscores(symbol.escapedName); const areAllModuleExports = symbol.declarations!.every(d => isInJSFile(d) && isAccessExpression(d) && isModuleExportsAccessExpression(d.expression)); const reference = areAllModuleExports ? factory.createPropertyAccessExpression(factory.createPropertyAccessExpression(factory.createIdentifier("module"), factory.createIdentifier("exports")), accessName) : factory.createPropertyAccessExpression(factory.createIdentifier("exports"), accessName); if (areAllModuleExports) { setParent((reference.expression as PropertyAccessExpression).expression, reference.expression); } setParent(reference.expression, reference); setParent(reference, file); reference.flowNode = file.endFlowNode; return getFlowTypeOfReference(reference, autoType, undefinedType); } function getFlowTypeInStaticBlocks(symbol: Symbol, staticBlocks: readonly ClassStaticBlockDeclaration[]) { const accessName = startsWith(symbol.escapedName as string, "__#") ? factory.createPrivateIdentifier((symbol.escapedName as string).split("@")[1]) : unescapeLeadingUnderscores(symbol.escapedName); for (const staticBlock of staticBlocks) { const reference = factory.createPropertyAccessExpression(factory.createThis(), accessName); setParent(reference.expression, reference); setParent(reference, staticBlock); reference.flowNode = staticBlock.returnFlowNode; const flowType = getFlowTypeOfProperty(reference, symbol); if (noImplicitAny && (flowType === autoType || flowType === autoArrayType)) { error(symbol.valueDeclaration, Diagnostics.Member_0_implicitly_has_an_1_type, symbolToString(symbol), typeToString(flowType)); } // We don't infer a type if assignments are only null or undefined. if (everyType(flowType, isNullableType)) { continue; } return convertAutoToAny(flowType); } } function getFlowTypeInConstructor(symbol: Symbol, constructor: ConstructorDeclaration) { const accessName = startsWith(symbol.escapedName as string, "__#") ? factory.createPrivateIdentifier((symbol.escapedName as string).split("@")[1]) : unescapeLeadingUnderscores(symbol.escapedName); const reference = factory.createPropertyAccessExpression(factory.createThis(), accessName); setParent(reference.expression, reference); setParent(reference, constructor); reference.flowNode = constructor.returnFlowNode; const flowType = getFlowTypeOfProperty(reference, symbol); if (noImplicitAny && (flowType === autoType || flowType === autoArrayType)) { error(symbol.valueDeclaration, Diagnostics.Member_0_implicitly_has_an_1_type, symbolToString(symbol), typeToString(flowType)); } // We don't infer a type if assignments are only null or undefined. return everyType(flowType, isNullableType) ? undefined : convertAutoToAny(flowType); } function getFlowTypeOfProperty(reference: Node, prop: Symbol | undefined) { const initialType = prop?.valueDeclaration && (!isAutoTypedProperty(prop) || getEffectiveModifierFlags(prop.valueDeclaration) & ModifierFlags.Ambient) && getTypeOfPropertyInBaseClass(prop) || undefinedType; return getFlowTypeOfReference(reference, autoType, initialType); } function getWidenedTypeForAssignmentDeclaration(symbol: Symbol, resolvedSymbol?: Symbol) { // function/class/{} initializers are themselves containers, so they won't merge in the same way as other initializers const container = getAssignedExpandoInitializer(symbol.valueDeclaration); if (container) { const tag = isInJSFile(container) ? getJSDocTypeTag(container) : undefined; if (tag && tag.typeExpression) { return getTypeFromTypeNode(tag.typeExpression); } const containerObjectType = symbol.valueDeclaration && getJSContainerObjectType(symbol.valueDeclaration, symbol, container); return containerObjectType || getWidenedLiteralType(checkExpressionCached(container)); } let type; let definedInConstructor = false; let definedInMethod = false; // We use control flow analysis to determine the type of the property if the property qualifies as a constructor // declared property and the resulting control flow type isn't just undefined or null. if (isConstructorDeclaredProperty(symbol)) { type = getFlowTypeInConstructor(symbol, getDeclaringConstructor(symbol)!); } if (!type) { let types: Type[] | undefined; if (symbol.declarations) { let jsdocType: Type | undefined; for (const declaration of symbol.declarations) { const expression = (isBinaryExpression(declaration) || isCallExpression(declaration)) ? declaration : isAccessExpression(declaration) ? isBinaryExpression(declaration.parent) ? declaration.parent : declaration : undefined; if (!expression) { continue; // Non-assignment declaration merged in (eg, an Identifier to mark the thing as a namespace) - skip over it and pull type info from elsewhere } const kind = isAccessExpression(expression) ? getAssignmentDeclarationPropertyAccessKind(expression) : getAssignmentDeclarationKind(expression); if (kind === AssignmentDeclarationKind.ThisProperty || isBinaryExpression(expression) && isPossiblyAliasedThisProperty(expression, kind)) { if (isDeclarationInConstructor(expression)) { definedInConstructor = true; } else { definedInMethod = true; } } if (!isCallExpression(expression)) { jsdocType = getAnnotatedTypeForAssignmentDeclaration(jsdocType, expression, symbol, declaration); } if (!jsdocType) { (types || (types = [])).push((isBinaryExpression(expression) || isCallExpression(expression)) ? getInitializerTypeFromAssignmentDeclaration(symbol, resolvedSymbol, expression, kind) : neverType); } } type = jsdocType; } if (!type) { if (!length(types)) { return errorType; // No types from any declarations :( } let constructorTypes = definedInConstructor && symbol.declarations ? getConstructorDefinedThisAssignmentTypes(types!, symbol.declarations) : undefined; // use only the constructor types unless they were only assigned null | undefined (including widening variants) if (definedInMethod) { const propType = getTypeOfPropertyInBaseClass(symbol); if (propType) { (constructorTypes || (constructorTypes = [])).push(propType); definedInConstructor = true; } } const sourceTypes = some(constructorTypes, t => !!(t.flags & ~TypeFlags.Nullable)) ? constructorTypes : types; // TODO: GH#18217 type = getUnionType(sourceTypes!); } } const widened = getWidenedType(addOptionality(type, /*isProperty*/ false, definedInMethod && !definedInConstructor)); if (symbol.valueDeclaration && isInJSFile(symbol.valueDeclaration) && filterType(widened, t => !!(t.flags & ~TypeFlags.Nullable)) === neverType) { reportImplicitAny(symbol.valueDeclaration, anyType); return anyType; } return widened; } function getJSContainerObjectType(decl: Node, symbol: Symbol, init: Expression | undefined): Type | undefined { if (!isInJSFile(decl) || !init || !isObjectLiteralExpression(init) || init.properties.length) { return undefined; } const exports = createSymbolTable(); while (isBinaryExpression(decl) || isPropertyAccessExpression(decl)) { const s = getSymbolOfNode(decl); if (s?.exports?.size) { mergeSymbolTable(exports, s.exports); } decl = isBinaryExpression(decl) ? decl.parent : decl.parent.parent; } const s = getSymbolOfNode(decl); if (s?.exports?.size) { mergeSymbolTable(exports, s.exports); } const type = createAnonymousType(symbol, exports, emptyArray, emptyArray, emptyArray); type.objectFlags |= ObjectFlags.JSLiteral; return type; } function getAnnotatedTypeForAssignmentDeclaration(declaredType: Type | undefined, expression: Expression, symbol: Symbol, declaration: Declaration) { const typeNode = getEffectiveTypeAnnotationNode(expression.parent); if (typeNode) { const type = getWidenedType(getTypeFromTypeNode(typeNode)); if (!declaredType) { return type; } else if (!isErrorType(declaredType) && !isErrorType(type) && !isTypeIdenticalTo(declaredType, type)) { errorNextVariableOrPropertyDeclarationMustHaveSameType(/*firstDeclaration*/ undefined, declaredType, declaration, type); } } if (symbol.parent?.valueDeclaration) { const possiblyAnnotatedSymbol = getFunctionExpressionParentSymbolOrSymbol(symbol.parent); if (possiblyAnnotatedSymbol.valueDeclaration) { const typeNode = getEffectiveTypeAnnotationNode(possiblyAnnotatedSymbol.valueDeclaration); if (typeNode) { const annotationSymbol = getPropertyOfType(getTypeFromTypeNode(typeNode), symbol.escapedName); if (annotationSymbol) { return getNonMissingTypeOfSymbol(annotationSymbol); } } } } return declaredType; } /** If we don't have an explicit JSDoc type, get the type from the initializer. */ function getInitializerTypeFromAssignmentDeclaration(symbol: Symbol, resolvedSymbol: Symbol | undefined, expression: BinaryExpression | CallExpression, kind: AssignmentDeclarationKind) { if (isCallExpression(expression)) { if (resolvedSymbol) { return getTypeOfSymbol(resolvedSymbol); // This shouldn't happen except under some hopefully forbidden merges of export assignments and object define assignments } const objectLitType = checkExpressionCached((expression as BindableObjectDefinePropertyCall).arguments[2]); const valueType = getTypeOfPropertyOfType(objectLitType, "value" as __String); if (valueType) { return valueType; } const getFunc = getTypeOfPropertyOfType(objectLitType, "get" as __String); if (getFunc) { const getSig = getSingleCallSignature(getFunc); if (getSig) { return getReturnTypeOfSignature(getSig); } } const setFunc = getTypeOfPropertyOfType(objectLitType, "set" as __String); if (setFunc) { const setSig = getSingleCallSignature(setFunc); if (setSig) { return getTypeOfFirstParameterOfSignature(setSig); } } return anyType; } if (containsSameNamedThisProperty(expression.left, expression.right)) { return anyType; } const isDirectExport = kind === AssignmentDeclarationKind.ExportsProperty && (isPropertyAccessExpression(expression.left) || isElementAccessExpression(expression.left)) && (isModuleExportsAccessExpression(expression.left.expression) || (isIdentifier(expression.left.expression) && isExportsIdentifier(expression.left.expression))); const type = resolvedSymbol ? getTypeOfSymbol(resolvedSymbol) : isDirectExport ? getRegularTypeOfLiteralType(checkExpressionCached(expression.right)) : getWidenedLiteralType(checkExpressionCached(expression.right)); if ( type.flags & TypeFlags.Object && kind === AssignmentDeclarationKind.ModuleExports && symbol.escapedName === InternalSymbolName.ExportEquals ) { const exportedType = resolveStructuredTypeMembers(type as ObjectType); const members = createSymbolTable(); copyEntries(exportedType.members, members); const initialSize = members.size; if (resolvedSymbol && !resolvedSymbol.exports) { resolvedSymbol.exports = createSymbolTable(); } (resolvedSymbol || symbol).exports!.forEach((s, name) => { const exportedMember = members.get(name)!; if (exportedMember && exportedMember !== s && !(s.flags & SymbolFlags.Alias)) { if (s.flags & SymbolFlags.Value && exportedMember.flags & SymbolFlags.Value) { // If the member has an additional value-like declaration, union the types from the two declarations, // but issue an error if they occurred in two different files. The purpose is to support a JS file with // a pattern like: // // module.exports = { a: true }; // module.exports.a = 3; // // but we may have a JS file with `module.exports = { a: true }` along with a TypeScript module augmentation // declaring an `export const a: number`. In that case, we issue a duplicate identifier error, because // it's unclear what that's supposed to mean, so it's probably a mistake. if (s.valueDeclaration && exportedMember.valueDeclaration && getSourceFileOfNode(s.valueDeclaration) !== getSourceFileOfNode(exportedMember.valueDeclaration)) { const unescapedName = unescapeLeadingUnderscores(s.escapedName); const exportedMemberName = tryCast(exportedMember.valueDeclaration, isNamedDeclaration)?.name || exportedMember.valueDeclaration; addRelatedInfo( error(s.valueDeclaration, Diagnostics.Duplicate_identifier_0, unescapedName), createDiagnosticForNode(exportedMemberName, Diagnostics._0_was_also_declared_here, unescapedName), ); addRelatedInfo( error(exportedMemberName, Diagnostics.Duplicate_identifier_0, unescapedName), createDiagnosticForNode(s.valueDeclaration, Diagnostics._0_was_also_declared_here, unescapedName), ); } const union = createSymbol(s.flags | exportedMember.flags, name); union.links.type = getUnionType([getTypeOfSymbol(s), getTypeOfSymbol(exportedMember)]); union.valueDeclaration = exportedMember.valueDeclaration; union.declarations = concatenate(exportedMember.declarations, s.declarations); members.set(name, union); } else { members.set(name, mergeSymbol(s, exportedMember)); } } else { members.set(name, s); } }); const result = createAnonymousType( initialSize !== members.size ? undefined : exportedType.symbol, // Only set the type's symbol if it looks to be the same as the original type members, exportedType.callSignatures, exportedType.constructSignatures, exportedType.indexInfos, ); if (initialSize === members.size) { if (type.aliasSymbol) { result.aliasSymbol = type.aliasSymbol; result.aliasTypeArguments = type.aliasTypeArguments; } if (getObjectFlags(type) & ObjectFlags.Reference) { result.aliasSymbol = (type as TypeReference).symbol; const args = getTypeArguments(type as TypeReference); result.aliasTypeArguments = length(args) ? args : undefined; } } result.objectFlags |= getPropagatingFlagsOfTypes([type]) | getObjectFlags(type) & (ObjectFlags.JSLiteral | ObjectFlags.ArrayLiteral | ObjectFlags.ObjectLiteral); if (result.symbol && result.symbol.flags & SymbolFlags.Class && type === getDeclaredTypeOfClassOrInterface(result.symbol)) { result.objectFlags |= ObjectFlags.IsClassInstanceClone; // Propagate the knowledge that this type is equivalent to the symbol's class instance type } return result; } if (isEmptyArrayLiteralType(type)) { reportImplicitAny(expression, anyArrayType); return anyArrayType; } return type; } function containsSameNamedThisProperty(thisProperty: Expression, expression: Expression) { return isPropertyAccessExpression(thisProperty) && thisProperty.expression.kind === SyntaxKind.ThisKeyword && forEachChildRecursively(expression, n => isMatchingReference(thisProperty, n)); } function isDeclarationInConstructor(expression: Expression) { const thisContainer = getThisContainer(expression, /*includeArrowFunctions*/ false, /*includeClassComputedPropertyName*/ false); // Properties defined in a constructor (or base constructor, or javascript constructor function) don't get undefined added. // Function expressions that are assigned to the prototype count as methods. return thisContainer.kind === SyntaxKind.Constructor || thisContainer.kind === SyntaxKind.FunctionDeclaration || (thisContainer.kind === SyntaxKind.FunctionExpression && !isPrototypePropertyAssignment(thisContainer.parent)); } function getConstructorDefinedThisAssignmentTypes(types: Type[], declarations: Declaration[]): Type[] | undefined { Debug.assert(types.length === declarations.length); return types.filter((_, i) => { const declaration = declarations[i]; const expression = isBinaryExpression(declaration) ? declaration : isBinaryExpression(declaration.parent) ? declaration.parent : undefined; return expression && isDeclarationInConstructor(expression); }); } // Return the type implied by a binding pattern element. This is the type of the initializer of the element if // one is present. Otherwise, if the element is itself a binding pattern, it is the type implied by the binding // pattern. Otherwise, it is the type any. function getTypeFromBindingElement(element: BindingElement, includePatternInType?: boolean, reportErrors?: boolean): Type { if (element.initializer) { // The type implied by a binding pattern is independent of context, so we check the initializer with no // contextual type or, if the element itself is a binding pattern, with the type implied by that binding // pattern. const contextualType = isBindingPattern(element.name) ? getTypeFromBindingPattern(element.name, /*includePatternInType*/ true, /*reportErrors*/ false) : unknownType; return addOptionality(widenTypeInferredFromInitializer(element, checkDeclarationInitializer(element, CheckMode.Normal, contextualType))); } if (isBindingPattern(element.name)) { return getTypeFromBindingPattern(element.name, includePatternInType, reportErrors); } if (reportErrors && !declarationBelongsToPrivateAmbientMember(element)) { reportImplicitAny(element, anyType); } // When we're including the pattern in the type (an indication we're obtaining a contextual type), we // use a non-inferrable any type. Inference will never directly infer this type, but it is possible // to infer a type that contains it, e.g. for a binding pattern like [foo] or { foo }. In such cases, // widening of the binding pattern type substitutes a regular any for the non-inferrable any. return includePatternInType ? nonInferrableAnyType : anyType; } // Return the type implied by an object binding pattern function getTypeFromObjectBindingPattern(pattern: ObjectBindingPattern, includePatternInType: boolean, reportErrors: boolean): Type { const members = createSymbolTable(); let stringIndexInfo: IndexInfo | undefined; let objectFlags = ObjectFlags.ObjectLiteral | ObjectFlags.ContainsObjectOrArrayLiteral; forEach(pattern.elements, e => { const name = e.propertyName || e.name as Identifier; if (e.dotDotDotToken) { stringIndexInfo = createIndexInfo(stringType, anyType, /*isReadonly*/ false); return; } const exprType = getLiteralTypeFromPropertyName(name); if (!isTypeUsableAsPropertyName(exprType)) { // do not include computed properties in the implied type objectFlags |= ObjectFlags.ObjectLiteralPatternWithComputedProperties; return; } const text = getPropertyNameFromType(exprType); const flags = SymbolFlags.Property | (e.initializer ? SymbolFlags.Optional : 0); const symbol = createSymbol(flags, text); symbol.links.type = getTypeFromBindingElement(e, includePatternInType, reportErrors); symbol.links.bindingElement = e; members.set(symbol.escapedName, symbol); }); const result = createAnonymousType(/*symbol*/ undefined, members, emptyArray, emptyArray, stringIndexInfo ? [stringIndexInfo] : emptyArray); result.objectFlags |= objectFlags; if (includePatternInType) { result.pattern = pattern; result.objectFlags |= ObjectFlags.ContainsObjectOrArrayLiteral; } return result; } // Return the type implied by an array binding pattern function getTypeFromArrayBindingPattern(pattern: BindingPattern, includePatternInType: boolean, reportErrors: boolean): Type { const elements = pattern.elements; const lastElement = lastOrUndefined(elements); const restElement = lastElement && lastElement.kind === SyntaxKind.BindingElement && lastElement.dotDotDotToken ? lastElement : undefined; if (elements.length === 0 || elements.length === 1 && restElement) { return languageVersion >= ScriptTarget.ES2015 ? createIterableType(anyType) : anyArrayType; } const elementTypes = map(elements, e => isOmittedExpression(e) ? anyType : getTypeFromBindingElement(e, includePatternInType, reportErrors)); const minLength = findLastIndex(elements, e => !(e === restElement || isOmittedExpression(e) || hasDefaultValue(e)), elements.length - 1) + 1; const elementFlags = map(elements, (e, i) => e === restElement ? ElementFlags.Rest : i >= minLength ? ElementFlags.Optional : ElementFlags.Required); let result = createTupleType(elementTypes, elementFlags) as TypeReference; if (includePatternInType) { result = cloneTypeReference(result); result.pattern = pattern; result.objectFlags |= ObjectFlags.ContainsObjectOrArrayLiteral; } return result; } // Return the type implied by a binding pattern. This is the type implied purely by the binding pattern itself // and without regard to its context (i.e. without regard any type annotation or initializer associated with the // declaration in which the binding pattern is contained). For example, the implied type of [x, y] is [any, any] // and the implied type of { x, y: z = 1 } is { x: any; y: number; }. The type implied by a binding pattern is // used as the contextual type of an initializer associated with the binding pattern. Also, for a destructuring // parameter with no type annotation or initializer, the type implied by the binding pattern becomes the type of // the parameter. function getTypeFromBindingPattern(pattern: BindingPattern, includePatternInType = false, reportErrors = false): Type { if (includePatternInType) contextualBindingPatterns.push(pattern); const result = pattern.kind === SyntaxKind.ObjectBindingPattern ? getTypeFromObjectBindingPattern(pattern, includePatternInType, reportErrors) : getTypeFromArrayBindingPattern(pattern, includePatternInType, reportErrors); if (includePatternInType) contextualBindingPatterns.pop(); return result; } // Return the type associated with a variable, parameter, or property declaration. In the simple case this is the type // specified in a type annotation or inferred from an initializer. However, in the case of a destructuring declaration it // is a bit more involved. For example: // // var [x, s = ""] = [1, "one"]; // // Here, the array literal [1, "one"] is contextually typed by the type [any, string], which is the implied type of the // binding pattern [x, s = ""]. Because the contextual type is a tuple type, the resulting type of [1, "one"] is the // tuple type [number, string]. Thus, the type inferred for 'x' is number and the type inferred for 's' is string. function getWidenedTypeForVariableLikeDeclaration(declaration: ParameterDeclaration | PropertyDeclaration | PropertySignature | VariableDeclaration | BindingElement | JSDocPropertyLikeTag, reportErrors?: boolean): Type { return widenTypeForVariableLikeDeclaration(getTypeForVariableLikeDeclaration(declaration, /*includeOptionality*/ true, CheckMode.Normal), declaration, reportErrors); } function getTypeFromImportAttributes(node: ImportAttributes): Type { const links = getNodeLinks(node); if (!links.resolvedType) { const symbol = createSymbol(SymbolFlags.ObjectLiteral, InternalSymbolName.ImportAttributes); const members = createSymbolTable(); forEach(node.elements, attr => { const member = createSymbol(SymbolFlags.Property, getNameFromImportAttribute(attr)); member.parent = symbol; member.links.type = checkImportAttribute(attr); member.links.target = member; members.set(member.escapedName, member); }); const type = createAnonymousType(symbol, members, emptyArray, emptyArray, emptyArray); type.objectFlags |= ObjectFlags.ObjectLiteral | ObjectFlags.NonInferrableType; links.resolvedType = type; } return links.resolvedType; } function isGlobalSymbolConstructor(node: Node) { const symbol = getSymbolOfNode(node); const globalSymbol = getGlobalESSymbolConstructorTypeSymbol(/*reportErrors*/ false); return globalSymbol && symbol && symbol === globalSymbol; } function widenTypeForVariableLikeDeclaration(type: Type | undefined, declaration: any, reportErrors?: boolean) { if (type) { // TODO: If back compat with pre-3.0/4.0 libs isn't required, remove the following SymbolConstructor special case transforming `symbol` into `unique symbol` if (type.flags & TypeFlags.ESSymbol && isGlobalSymbolConstructor(declaration.parent)) { type = getESSymbolLikeTypeForNode(declaration); } if (reportErrors) { reportErrorsFromWidening(declaration, type); } // always widen a 'unique symbol' type if the type was created for a different declaration. if (type.flags & TypeFlags.UniqueESSymbol && (isBindingElement(declaration) || !declaration.type) && type.symbol !== getSymbolOfDeclaration(declaration)) { type = esSymbolType; } return getWidenedType(type); } // Rest parameters default to type any[], other parameters default to type any type = isParameter(declaration) && declaration.dotDotDotToken ? anyArrayType : anyType; // Report implicit any errors unless this is a private property within an ambient declaration if (reportErrors) { if (!declarationBelongsToPrivateAmbientMember(declaration)) { reportImplicitAny(declaration, type); } } return type; } function declarationBelongsToPrivateAmbientMember(declaration: VariableLikeDeclaration) { const root = getRootDeclaration(declaration); const memberDeclaration = root.kind === SyntaxKind.Parameter ? root.parent : root; return isPrivateWithinAmbient(memberDeclaration); } function tryGetTypeFromEffectiveTypeNode(node: Node) { const typeNode = getEffectiveTypeAnnotationNode(node); if (typeNode) { return getTypeFromTypeNode(typeNode); } } function isParameterOfContextSensitiveSignature(symbol: Symbol) { let decl = symbol.valueDeclaration; if (!decl) { return false; } if (isBindingElement(decl)) { decl = walkUpBindingElementsAndPatterns(decl); } if (isParameter(decl)) { return isContextSensitiveFunctionOrObjectLiteralMethod(decl.parent); } return false; } function getTypeOfVariableOrParameterOrProperty(symbol: Symbol): Type { const links = getSymbolLinks(symbol); if (!links.type) { const type = getTypeOfVariableOrParameterOrPropertyWorker(symbol); // For a contextually typed parameter it is possible that a type has already // been assigned (in assignTypeToParameterAndFixTypeParameters), and we want // to preserve this type. In fact, we need to _prefer_ that type, but it won't // be assigned until contextual typing is complete, so we need to defer in // cases where contextual typing may take place. if (!links.type && !isParameterOfContextSensitiveSignature(symbol)) { links.type = type; } return type; } return links.type; } function getTypeOfVariableOrParameterOrPropertyWorker(symbol: Symbol): Type { // Handle prototype property if (symbol.flags & SymbolFlags.Prototype) { return getTypeOfPrototypeProperty(symbol); } // CommonsJS require and module both have type any. if (symbol === requireSymbol) { return anyType; } if (symbol.flags & SymbolFlags.ModuleExports && symbol.valueDeclaration) { const fileSymbol = getSymbolOfDeclaration(getSourceFileOfNode(symbol.valueDeclaration)); const result = createSymbol(fileSymbol.flags, "exports" as __String); result.declarations = fileSymbol.declarations ? fileSymbol.declarations.slice() : []; result.parent = symbol; result.links.target = fileSymbol; if (fileSymbol.valueDeclaration) result.valueDeclaration = fileSymbol.valueDeclaration; if (fileSymbol.members) result.members = new Map(fileSymbol.members); if (fileSymbol.exports) result.exports = new Map(fileSymbol.exports); const members = createSymbolTable(); members.set("exports" as __String, result); return createAnonymousType(symbol, members, emptyArray, emptyArray, emptyArray); } Debug.assertIsDefined(symbol.valueDeclaration); const declaration = symbol.valueDeclaration; // Handle export default expressions if (isSourceFile(declaration) && isJsonSourceFile(declaration)) { if (!declaration.statements.length) { return emptyObjectType; } return getWidenedType(getWidenedLiteralType(checkExpression(declaration.statements[0].expression))); } if (isAccessor(declaration)) { // Binding of certain patterns in JS code will occasionally mark symbols as both properties // and accessors. Here we dispatch to accessor resolution if needed. return getTypeOfAccessors(symbol); } // Handle variable, parameter or property if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) { // Symbol is property of some kind that is merged with something - should use `getTypeOfFuncClassEnumModule` and not `getTypeOfVariableOrParameterOrProperty` if (symbol.flags & SymbolFlags.ValueModule && !(symbol.flags & SymbolFlags.Assignment)) { return getTypeOfFuncClassEnumModule(symbol); } return reportCircularityError(symbol); } let type: Type; if (declaration.kind === SyntaxKind.ExportAssignment) { type = widenTypeForVariableLikeDeclaration(tryGetTypeFromEffectiveTypeNode(declaration) || checkExpressionCached((declaration as ExportAssignment).expression), declaration); } else if ( isBinaryExpression(declaration) || (isInJSFile(declaration) && (isCallExpression(declaration) || (isPropertyAccessExpression(declaration) || isBindableStaticElementAccessExpression(declaration)) && isBinaryExpression(declaration.parent))) ) { type = getWidenedTypeForAssignmentDeclaration(symbol); } else if ( isPropertyAccessExpression(declaration) || isElementAccessExpression(declaration) || isIdentifier(declaration) || isStringLiteralLike(declaration) || isNumericLiteral(declaration) || isClassDeclaration(declaration) || isFunctionDeclaration(declaration) || (isMethodDeclaration(declaration) && !isObjectLiteralMethod(declaration)) || isMethodSignature(declaration) || isSourceFile(declaration) ) { // Symbol is property of some kind that is merged with something - should use `getTypeOfFuncClassEnumModule` and not `getTypeOfVariableOrParameterOrProperty` if (symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.Enum | SymbolFlags.ValueModule)) { return getTypeOfFuncClassEnumModule(symbol); } type = isBinaryExpression(declaration.parent) ? getWidenedTypeForAssignmentDeclaration(symbol) : tryGetTypeFromEffectiveTypeNode(declaration) || anyType; } else if (isPropertyAssignment(declaration)) { type = tryGetTypeFromEffectiveTypeNode(declaration) || checkPropertyAssignment(declaration); } else if (isJsxAttribute(declaration)) { type = tryGetTypeFromEffectiveTypeNode(declaration) || checkJsxAttribute(declaration); } else if (isShorthandPropertyAssignment(declaration)) { type = tryGetTypeFromEffectiveTypeNode(declaration) || checkExpressionForMutableLocation(declaration.name, CheckMode.Normal); } else if (isObjectLiteralMethod(declaration)) { type = tryGetTypeFromEffectiveTypeNode(declaration) || checkObjectLiteralMethod(declaration, CheckMode.Normal); } else if ( isParameter(declaration) || isPropertyDeclaration(declaration) || isPropertySignature(declaration) || isVariableDeclaration(declaration) || isBindingElement(declaration) || isJSDocPropertyLikeTag(declaration) ) { type = getWidenedTypeForVariableLikeDeclaration(declaration, /*reportErrors*/ true); } // getTypeOfSymbol dispatches some JS merges incorrectly because their symbol flags are not mutually exclusive. // Re-dispatch based on valueDeclaration.kind instead. else if (isEnumDeclaration(declaration)) { type = getTypeOfFuncClassEnumModule(symbol); } else if (isEnumMember(declaration)) { type = getTypeOfEnumMember(symbol); } else { return Debug.fail("Unhandled declaration kind! " + Debug.formatSyntaxKind(declaration.kind) + " for " + Debug.formatSymbol(symbol)); } if (!popTypeResolution()) { // Symbol is property of some kind that is merged with something - should use `getTypeOfFuncClassEnumModule` and not `getTypeOfVariableOrParameterOrProperty` if (symbol.flags & SymbolFlags.ValueModule && !(symbol.flags & SymbolFlags.Assignment)) { return getTypeOfFuncClassEnumModule(symbol); } return reportCircularityError(symbol); } return type; } function getAnnotatedAccessorTypeNode(accessor: AccessorDeclaration | PropertyDeclaration | undefined): TypeNode | undefined { if (accessor) { switch (accessor.kind) { case SyntaxKind.GetAccessor: const getterTypeAnnotation = getEffectiveReturnTypeNode(accessor); return getterTypeAnnotation; case SyntaxKind.SetAccessor: const setterTypeAnnotation = getEffectiveSetAccessorTypeAnnotationNode(accessor); return setterTypeAnnotation; case SyntaxKind.PropertyDeclaration: Debug.assert(hasAccessorModifier(accessor)); const accessorTypeAnnotation = getEffectiveTypeAnnotationNode(accessor); return accessorTypeAnnotation; } } return undefined; } function getAnnotatedAccessorType(accessor: AccessorDeclaration | PropertyDeclaration | undefined): Type | undefined { const node = getAnnotatedAccessorTypeNode(accessor); return node && getTypeFromTypeNode(node); } function getAnnotatedAccessorThisParameter(accessor: AccessorDeclaration): Symbol | undefined { const parameter = getAccessorThisParameter(accessor); return parameter && parameter.symbol; } function getThisTypeOfDeclaration(declaration: SignatureDeclaration): Type | undefined { return getThisTypeOfSignature(getSignatureFromDeclaration(declaration)); } function getTypeOfAccessors(symbol: Symbol): Type { const links = getSymbolLinks(symbol); if (!links.type) { if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) { return errorType; } const getter = getDeclarationOfKind(symbol, SyntaxKind.GetAccessor); const setter = getDeclarationOfKind(symbol, SyntaxKind.SetAccessor); const accessor = tryCast(getDeclarationOfKind(symbol, SyntaxKind.PropertyDeclaration), isAutoAccessorPropertyDeclaration); // We try to resolve a getter type annotation, a setter type annotation, or a getter function // body return type inference, in that order. let type = getter && isInJSFile(getter) && getTypeForDeclarationFromJSDocComment(getter) || getAnnotatedAccessorType(getter) || getAnnotatedAccessorType(setter) || getAnnotatedAccessorType(accessor) || getter && getter.body && getReturnTypeFromBody(getter) || accessor && accessor.initializer && getWidenedTypeForVariableLikeDeclaration(accessor, /*reportErrors*/ true); if (!type) { if (setter && !isPrivateWithinAmbient(setter)) { errorOrSuggestion(noImplicitAny, setter, Diagnostics.Property_0_implicitly_has_type_any_because_its_set_accessor_lacks_a_parameter_type_annotation, symbolToString(symbol)); } else if (getter && !isPrivateWithinAmbient(getter)) { errorOrSuggestion(noImplicitAny, getter, Diagnostics.Property_0_implicitly_has_type_any_because_its_get_accessor_lacks_a_return_type_annotation, symbolToString(symbol)); } else if (accessor && !isPrivateWithinAmbient(accessor)) { errorOrSuggestion(noImplicitAny, accessor, Diagnostics.Member_0_implicitly_has_an_1_type, symbolToString(symbol), "any"); } type = anyType; } if (!popTypeResolution()) { if (getAnnotatedAccessorTypeNode(getter)) { error(getter, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_type_annotation, symbolToString(symbol)); } else if (getAnnotatedAccessorTypeNode(setter)) { error(setter, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_type_annotation, symbolToString(symbol)); } else if (getAnnotatedAccessorTypeNode(accessor)) { error(setter, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_type_annotation, symbolToString(symbol)); } else if (getter && noImplicitAny) { error(getter, Diagnostics._0_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions, symbolToString(symbol)); } type = anyType; } links.type ??= type; } return links.type; } function getWriteTypeOfAccessors(symbol: Symbol): Type { const links = getSymbolLinks(symbol); if (!links.writeType) { if (!pushTypeResolution(symbol, TypeSystemPropertyName.WriteType)) { return errorType; } const setter = getDeclarationOfKind(symbol, SyntaxKind.SetAccessor) ?? tryCast(getDeclarationOfKind(symbol, SyntaxKind.PropertyDeclaration), isAutoAccessorPropertyDeclaration); let writeType = getAnnotatedAccessorType(setter); if (!popTypeResolution()) { if (getAnnotatedAccessorTypeNode(setter)) { error(setter, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_type_annotation, symbolToString(symbol)); } writeType = anyType; } // Absent an explicit setter type annotation we use the read type of the accessor. links.writeType ??= writeType || getTypeOfAccessors(symbol); } return links.writeType; } function getBaseTypeVariableOfClass(symbol: Symbol) { const baseConstructorType = getBaseConstructorTypeOfClass(getDeclaredTypeOfClassOrInterface(symbol)); return baseConstructorType.flags & TypeFlags.TypeVariable ? baseConstructorType : baseConstructorType.flags & TypeFlags.Intersection ? find((baseConstructorType as IntersectionType).types, t => !!(t.flags & TypeFlags.TypeVariable)) : undefined; } function getTypeOfFuncClassEnumModule(symbol: Symbol): Type { let links = getSymbolLinks(symbol); const originalLinks = links; if (!links.type) { const expando = symbol.valueDeclaration && getSymbolOfExpando(symbol.valueDeclaration, /*allowDeclaration*/ false); if (expando) { const merged = mergeJSSymbols(symbol, expando); if (merged) { // note:we overwrite links because we just cloned the symbol symbol = merged; links = merged.links; } } originalLinks.type = links.type = getTypeOfFuncClassEnumModuleWorker(symbol); } return links.type; } function getTypeOfFuncClassEnumModuleWorker(symbol: Symbol): Type { const declaration = symbol.valueDeclaration; if (symbol.flags & SymbolFlags.Module && isShorthandAmbientModuleSymbol(symbol)) { return anyType; } else if ( declaration && (declaration.kind === SyntaxKind.BinaryExpression || isAccessExpression(declaration) && declaration.parent.kind === SyntaxKind.BinaryExpression) ) { return getWidenedTypeForAssignmentDeclaration(symbol); } else if (symbol.flags & SymbolFlags.ValueModule && declaration && isSourceFile(declaration) && declaration.commonJsModuleIndicator) { const resolvedModule = resolveExternalModuleSymbol(symbol); if (resolvedModule !== symbol) { if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) { return errorType; } const exportEquals = getMergedSymbol(symbol.exports!.get(InternalSymbolName.ExportEquals)!); const type = getWidenedTypeForAssignmentDeclaration(exportEquals, exportEquals === resolvedModule ? undefined : resolvedModule); if (!popTypeResolution()) { return reportCircularityError(symbol); } return type; } } const type = createObjectType(ObjectFlags.Anonymous, symbol); if (symbol.flags & SymbolFlags.Class) { const baseTypeVariable = getBaseTypeVariableOfClass(symbol); return baseTypeVariable ? getIntersectionType([type, baseTypeVariable]) : type; } else { return strictNullChecks && symbol.flags & SymbolFlags.Optional ? getOptionalType(type, /*isProperty*/ true) : type; } } function getTypeOfEnumMember(symbol: Symbol): Type { const links = getSymbolLinks(symbol); return links.type || (links.type = getDeclaredTypeOfEnumMember(symbol)); } function getTypeOfAlias(symbol: Symbol): Type { const links = getSymbolLinks(symbol); if (!links.type) { if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) { return errorType; } const targetSymbol = resolveAlias(symbol); const exportSymbol = symbol.declarations && getTargetOfAliasDeclaration(getDeclarationOfAliasSymbol(symbol)!, /*dontRecursivelyResolve*/ true); const declaredType = firstDefined(exportSymbol?.declarations, d => isExportAssignment(d) ? tryGetTypeFromEffectiveTypeNode(d) : undefined); // It only makes sense to get the type of a value symbol. If the result of resolving // the alias is not a value, then it has no type. To get the type associated with a // type symbol, call getDeclaredTypeOfSymbol. // This check is important because without it, a call to getTypeOfSymbol could end // up recursively calling getTypeOfAlias, causing a stack overflow. links.type ??= exportSymbol?.declarations && isDuplicatedCommonJSExport(exportSymbol.declarations) && symbol.declarations!.length ? getFlowTypeFromCommonJSExport(exportSymbol) : isDuplicatedCommonJSExport(symbol.declarations) ? autoType : declaredType ? declaredType : getSymbolFlags(targetSymbol) & SymbolFlags.Value ? getTypeOfSymbol(targetSymbol) : errorType; if (!popTypeResolution()) { reportCircularityError(exportSymbol ?? symbol); return links.type ??= errorType; } } return links.type; } function getTypeOfInstantiatedSymbol(symbol: Symbol): Type { const links = getSymbolLinks(symbol); return links.type || (links.type = instantiateType(getTypeOfSymbol(links.target!), links.mapper)); } function getWriteTypeOfInstantiatedSymbol(symbol: Symbol): Type { const links = getSymbolLinks(symbol); return links.writeType || (links.writeType = instantiateType(getWriteTypeOfSymbol(links.target!), links.mapper)); } function reportCircularityError(symbol: Symbol) { const declaration = symbol.valueDeclaration; // Check if variable has type annotation that circularly references the variable itself if (declaration) { if (getEffectiveTypeAnnotationNode(declaration)) { error(symbol.valueDeclaration, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_type_annotation, symbolToString(symbol)); return errorType; } // Check if variable has initializer that circularly references the variable itself if (noImplicitAny && (declaration.kind !== SyntaxKind.Parameter || (declaration as HasInitializer).initializer)) { error(symbol.valueDeclaration, Diagnostics._0_implicitly_has_type_any_because_it_does_not_have_a_type_annotation_and_is_referenced_directly_or_indirectly_in_its_own_initializer, symbolToString(symbol)); } } else if (symbol.flags & SymbolFlags.Alias) { const node = getDeclarationOfAliasSymbol(symbol); if (node) { error(node, Diagnostics.Circular_definition_of_import_alias_0, symbolToString(symbol)); } } // Circularities could also result from parameters in function expressions that end up // having themselves as contextual types following type argument inference. In those cases // we have already reported an implicit any error so we don't report anything here. return anyType; } function getTypeOfSymbolWithDeferredType(symbol: Symbol) { const links = getSymbolLinks(symbol); if (!links.type) { Debug.assertIsDefined(links.deferralParent); Debug.assertIsDefined(links.deferralConstituents); links.type = links.deferralParent.flags & TypeFlags.Union ? getUnionType(links.deferralConstituents) : getIntersectionType(links.deferralConstituents); } return links.type; } function getWriteTypeOfSymbolWithDeferredType(symbol: Symbol): Type | undefined { const links = getSymbolLinks(symbol); if (!links.writeType && links.deferralWriteConstituents) { Debug.assertIsDefined(links.deferralParent); Debug.assertIsDefined(links.deferralConstituents); links.writeType = links.deferralParent.flags & TypeFlags.Union ? getUnionType(links.deferralWriteConstituents) : getIntersectionType(links.deferralWriteConstituents); } return links.writeType; } /** * Distinct write types come only from set accessors, but synthetic union and intersection * properties deriving from set accessors will either pre-compute or defer the union or * intersection of the writeTypes of their constituents. */ function getWriteTypeOfSymbol(symbol: Symbol): Type { const checkFlags = getCheckFlags(symbol); if (symbol.flags & SymbolFlags.Property) { return checkFlags & CheckFlags.SyntheticProperty ? checkFlags & CheckFlags.DeferredType ? getWriteTypeOfSymbolWithDeferredType(symbol) || getTypeOfSymbolWithDeferredType(symbol) : // NOTE: cast to TransientSymbol should be safe because only TransientSymbols can have CheckFlags.SyntheticProperty (symbol as TransientSymbol).links.writeType || (symbol as TransientSymbol).links.type! : removeMissingType(getTypeOfSymbol(symbol), !!(symbol.flags & SymbolFlags.Optional)); } if (symbol.flags & SymbolFlags.Accessor) { return checkFlags & CheckFlags.Instantiated ? getWriteTypeOfInstantiatedSymbol(symbol) : getWriteTypeOfAccessors(symbol); } return getTypeOfSymbol(symbol); } function getTypeOfSymbol(symbol: Symbol): Type { const checkFlags = getCheckFlags(symbol); if (checkFlags & CheckFlags.DeferredType) { return getTypeOfSymbolWithDeferredType(symbol); } if (checkFlags & CheckFlags.Instantiated) { return getTypeOfInstantiatedSymbol(symbol); } if (checkFlags & CheckFlags.Mapped) { return getTypeOfMappedSymbol(symbol as MappedSymbol); } if (checkFlags & CheckFlags.ReverseMapped) { return getTypeOfReverseMappedSymbol(symbol as ReverseMappedSymbol); } if (symbol.flags & (SymbolFlags.Variable | SymbolFlags.Property)) { return getTypeOfVariableOrParameterOrProperty(symbol); } if (symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.Enum | SymbolFlags.ValueModule)) { return getTypeOfFuncClassEnumModule(symbol); } if (symbol.flags & SymbolFlags.EnumMember) { return getTypeOfEnumMember(symbol); } if (symbol.flags & SymbolFlags.Accessor) { return getTypeOfAccessors(symbol); } if (symbol.flags & SymbolFlags.Alias) { return getTypeOfAlias(symbol); } return errorType; } function getNonMissingTypeOfSymbol(symbol: Symbol) { return removeMissingType(getTypeOfSymbol(symbol), !!(symbol.flags & SymbolFlags.Optional)); } function isReferenceToSomeType(type: Type, targets: readonly Type[]) { if (type === undefined || (getObjectFlags(type) & ObjectFlags.Reference) === 0) { return false; } for (const target of targets) { if ((type as TypeReference).target === target) { return true; } } return false; } function isReferenceToType(type: Type, target: Type) { return type !== undefined && target !== undefined && (getObjectFlags(type) & ObjectFlags.Reference) !== 0 && (type as TypeReference).target === target; } function getTargetType(type: Type): Type { return getObjectFlags(type) & ObjectFlags.Reference ? (type as TypeReference).target : type; } // TODO: GH#18217 If `checkBase` is undefined, we should not call this because this will always return false. function hasBaseType(type: Type, checkBase: Type | undefined) { return check(type); function check(type: Type): boolean { if (getObjectFlags(type) & (ObjectFlags.ClassOrInterface | ObjectFlags.Reference)) { const target = getTargetType(type) as InterfaceType; return target === checkBase || some(getBaseTypes(target), check); } else if (type.flags & TypeFlags.Intersection) { return some((type as IntersectionType).types, check); } return false; } } // Appends the type parameters given by a list of declarations to a set of type parameters and returns the resulting set. // The function allocates a new array if the input type parameter set is undefined, but otherwise it modifies the set // in-place and returns the same array. function appendTypeParameters(typeParameters: TypeParameter[] | undefined, declarations: readonly TypeParameterDeclaration[]): TypeParameter[] | undefined { for (const declaration of declarations) { typeParameters = appendIfUnique(typeParameters, getDeclaredTypeOfTypeParameter(getSymbolOfDeclaration(declaration))); } return typeParameters; } // Return the outer type parameters of a node or undefined if the node has no outer type parameters. function getOuterTypeParameters(node: Node, includeThisTypes?: boolean): TypeParameter[] | undefined { while (true) { node = node.parent; // TODO: GH#18217 Use SourceFile kind check instead if (node && isBinaryExpression(node)) { // prototype assignments get the outer type parameters of their constructor function const assignmentKind = getAssignmentDeclarationKind(node); if (assignmentKind === AssignmentDeclarationKind.Prototype || assignmentKind === AssignmentDeclarationKind.PrototypeProperty) { const symbol = getSymbolOfDeclaration(node.left as BindableStaticNameExpression | PropertyAssignment); if (symbol && symbol.parent && !findAncestor(symbol.parent.valueDeclaration, d => node === d)) { node = symbol.parent.valueDeclaration!; } } } if (!node) { return undefined; } const kind = node.kind; switch (kind) { case SyntaxKind.ClassDeclaration: case SyntaxKind.ClassExpression: case SyntaxKind.InterfaceDeclaration: case SyntaxKind.CallSignature: case SyntaxKind.ConstructSignature: case SyntaxKind.MethodSignature: case SyntaxKind.FunctionType: case SyntaxKind.ConstructorType: case SyntaxKind.JSDocFunctionType: case SyntaxKind.FunctionDeclaration: case SyntaxKind.MethodDeclaration: case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: case SyntaxKind.TypeAliasDeclaration: case SyntaxKind.JSDocTemplateTag: case SyntaxKind.JSDocTypedefTag: case SyntaxKind.JSDocEnumTag: case SyntaxKind.JSDocCallbackTag: case SyntaxKind.MappedType: case SyntaxKind.ConditionalType: { const outerTypeParameters = getOuterTypeParameters(node, includeThisTypes); if ((kind === SyntaxKind.FunctionExpression || kind === SyntaxKind.ArrowFunction || isObjectLiteralMethod(node)) && isContextSensitive(node as Expression | MethodDeclaration)) { const signature = firstOrUndefined(getSignaturesOfType(getTypeOfSymbol(getSymbolOfDeclaration(node as FunctionLikeDeclaration)), SignatureKind.Call)); if (signature && signature.typeParameters) { return [...(outerTypeParameters || emptyArray), ...signature.typeParameters]; } } if (kind === SyntaxKind.MappedType) { return append(outerTypeParameters, getDeclaredTypeOfTypeParameter(getSymbolOfDeclaration((node as MappedTypeNode).typeParameter))); } else if (kind === SyntaxKind.ConditionalType) { return concatenate(outerTypeParameters, getInferTypeParameters(node as ConditionalTypeNode)); } const outerAndOwnTypeParameters = appendTypeParameters(outerTypeParameters, getEffectiveTypeParameterDeclarations(node as DeclarationWithTypeParameters)); const thisType = includeThisTypes && (kind === SyntaxKind.ClassDeclaration || kind === SyntaxKind.ClassExpression || kind === SyntaxKind.InterfaceDeclaration || isJSConstructor(node)) && getDeclaredTypeOfClassOrInterface(getSymbolOfDeclaration(node as ClassLikeDeclaration | InterfaceDeclaration)).thisType; return thisType ? append(outerAndOwnTypeParameters, thisType) : outerAndOwnTypeParameters; } case SyntaxKind.JSDocParameterTag: const paramSymbol = getParameterSymbolFromJSDoc(node as JSDocParameterTag); if (paramSymbol) { node = paramSymbol.valueDeclaration!; } break; case SyntaxKind.JSDoc: { const outerTypeParameters = getOuterTypeParameters(node, includeThisTypes); return (node as JSDoc).tags ? appendTypeParameters(outerTypeParameters, flatMap((node as JSDoc).tags, t => isJSDocTemplateTag(t) ? t.typeParameters : undefined)) : outerTypeParameters; } } } } // The outer type parameters are those defined by enclosing generic classes, methods, or functions. function getOuterTypeParametersOfClassOrInterface(symbol: Symbol): TypeParameter[] | undefined { const declaration = (symbol.flags & SymbolFlags.Class || symbol.flags & SymbolFlags.Function) ? symbol.valueDeclaration : symbol.declarations?.find(decl => { if (decl.kind === SyntaxKind.InterfaceDeclaration) { return true; } if (decl.kind !== SyntaxKind.VariableDeclaration) { return false; } const initializer = (decl as VariableDeclaration).initializer; return !!initializer && (initializer.kind === SyntaxKind.FunctionExpression || initializer.kind === SyntaxKind.ArrowFunction); })!; Debug.assert(!!declaration, "Class was missing valueDeclaration -OR- non-class had no interface declarations"); return getOuterTypeParameters(declaration); } // The local type parameters are the combined set of type parameters from all declarations of the class, // interface, or type alias. function getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol: Symbol): TypeParameter[] | undefined { if (!symbol.declarations) { return; } let result: TypeParameter[] | undefined; for (const node of symbol.declarations) { if ( node.kind === SyntaxKind.InterfaceDeclaration || node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.ClassExpression || isJSConstructor(node) || isTypeAlias(node) ) { const declaration = node as InterfaceDeclaration | TypeAliasDeclaration | JSDocTypedefTag | JSDocCallbackTag; result = appendTypeParameters(result, getEffectiveTypeParameterDeclarations(declaration)); } } return result; } // The full set of type parameters for a generic class or interface type consists of its outer type parameters plus // its locally declared type parameters. function getTypeParametersOfClassOrInterface(symbol: Symbol): TypeParameter[] | undefined { return concatenate(getOuterTypeParametersOfClassOrInterface(symbol), getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol)); } // A type is a mixin constructor if it has a single construct signature taking no type parameters and a single // rest parameter of type any[]. function isMixinConstructorType(type: Type) { const signatures = getSignaturesOfType(type, SignatureKind.Construct); if (signatures.length === 1) { const s = signatures[0]; if (!s.typeParameters && s.parameters.length === 1 && signatureHasRestParameter(s)) { const paramType = getTypeOfParameter(s.parameters[0]); return isTypeAny(paramType) || getElementTypeOfArrayType(paramType) === anyType; } } return false; } function isConstructorType(type: Type): boolean { if (getSignaturesOfType(type, SignatureKind.Construct).length > 0) { return true; } if (type.flags & TypeFlags.TypeVariable) { const constraint = getBaseConstraintOfType(type); return !!constraint && isMixinConstructorType(constraint); } return false; } function getBaseTypeNodeOfClass(type: InterfaceType): ExpressionWithTypeArguments | undefined { const decl = getClassLikeDeclarationOfSymbol(type.symbol); return decl && getEffectiveBaseTypeNode(decl); } function getConstructorsForTypeArguments(type: Type, typeArgumentNodes: readonly TypeNode[] | undefined, location: Node): readonly Signature[] { const typeArgCount = length(typeArgumentNodes); const isJavascript = isInJSFile(location); return filter(getSignaturesOfType(type, SignatureKind.Construct), sig => (isJavascript || typeArgCount >= getMinTypeArgumentCount(sig.typeParameters)) && typeArgCount <= length(sig.typeParameters)); } function getInstantiatedConstructorsForTypeArguments(type: Type, typeArgumentNodes: readonly TypeNode[] | undefined, location: Node): readonly Signature[] { const signatures = getConstructorsForTypeArguments(type, typeArgumentNodes, location); const typeArguments = map(typeArgumentNodes, getTypeFromTypeNode); return sameMap(signatures, sig => some(sig.typeParameters) ? getSignatureInstantiation(sig, typeArguments, isInJSFile(location)) : sig); } /** * The base constructor of a class can resolve to * * undefinedType if the class has no extends clause, * * errorType if an error occurred during resolution of the extends expression, * * nullType if the extends expression is the null value, * * anyType if the extends expression has type any, or * * an object type with at least one construct signature. */ function getBaseConstructorTypeOfClass(type: InterfaceType): Type { if (!type.resolvedBaseConstructorType) { const decl = getClassLikeDeclarationOfSymbol(type.symbol); const extended = decl && getEffectiveBaseTypeNode(decl); const baseTypeNode = getBaseTypeNodeOfClass(type); if (!baseTypeNode) { return type.resolvedBaseConstructorType = undefinedType; } if (!pushTypeResolution(type, TypeSystemPropertyName.ResolvedBaseConstructorType)) { return errorType; } const baseConstructorType = checkExpression(baseTypeNode.expression); if (extended && baseTypeNode !== extended) { Debug.assert(!extended.typeArguments); // Because this is in a JS file, and baseTypeNode is in an @extends tag checkExpression(extended.expression); } if (baseConstructorType.flags & (TypeFlags.Object | TypeFlags.Intersection)) { // Resolving the members of a class requires us to resolve the base class of that class. // We force resolution here such that we catch circularities now. resolveStructuredTypeMembers(baseConstructorType as ObjectType); } if (!popTypeResolution()) { error(type.symbol.valueDeclaration, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_base_expression, symbolToString(type.symbol)); return type.resolvedBaseConstructorType ??= errorType; } if (!(baseConstructorType.flags & TypeFlags.Any) && baseConstructorType !== nullWideningType && !isConstructorType(baseConstructorType)) { const err = error(baseTypeNode.expression, Diagnostics.Type_0_is_not_a_constructor_function_type, typeToString(baseConstructorType)); if (baseConstructorType.flags & TypeFlags.TypeParameter) { const constraint = getConstraintFromTypeParameter(baseConstructorType); let ctorReturn: Type = unknownType; if (constraint) { const ctorSig = getSignaturesOfType(constraint, SignatureKind.Construct); if (ctorSig[0]) { ctorReturn = getReturnTypeOfSignature(ctorSig[0]); } } if (baseConstructorType.symbol.declarations) { addRelatedInfo(err, createDiagnosticForNode(baseConstructorType.symbol.declarations[0], Diagnostics.Did_you_mean_for_0_to_be_constrained_to_type_new_args_Colon_any_1, symbolToString(baseConstructorType.symbol), typeToString(ctorReturn))); } } return type.resolvedBaseConstructorType ??= errorType; } type.resolvedBaseConstructorType ??= baseConstructorType; } return type.resolvedBaseConstructorType; } function getImplementsTypes(type: InterfaceType): BaseType[] { let resolvedImplementsTypes: BaseType[] = emptyArray; if (type.symbol.declarations) { for (const declaration of type.symbol.declarations) { const implementsTypeNodes = getEffectiveImplementsTypeNodes(declaration as ClassLikeDeclaration); if (!implementsTypeNodes) continue; for (const node of implementsTypeNodes) { const implementsType = getTypeFromTypeNode(node); if (!isErrorType(implementsType)) { if (resolvedImplementsTypes === emptyArray) { resolvedImplementsTypes = [implementsType as ObjectType]; } else { resolvedImplementsTypes.push(implementsType); } } } } } return resolvedImplementsTypes; } function reportCircularBaseType(node: Node, type: Type) { error(node, Diagnostics.Type_0_recursively_references_itself_as_a_base_type, typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType)); } function getBaseTypes(type: InterfaceType): BaseType[] { if (!type.baseTypesResolved) { if (pushTypeResolution(type, TypeSystemPropertyName.ResolvedBaseTypes)) { if (type.objectFlags & ObjectFlags.Tuple) { type.resolvedBaseTypes = [getTupleBaseType(type as TupleType)]; } else if (type.symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { if (type.symbol.flags & SymbolFlags.Class) { resolveBaseTypesOfClass(type); } if (type.symbol.flags & SymbolFlags.Interface) { resolveBaseTypesOfInterface(type); } } else { Debug.fail("type must be class or interface"); } if (!popTypeResolution() && type.symbol.declarations) { for (const declaration of type.symbol.declarations) { if (declaration.kind === SyntaxKind.ClassDeclaration || declaration.kind === SyntaxKind.InterfaceDeclaration) { reportCircularBaseType(declaration, type); } } } } type.baseTypesResolved = true; } return type.resolvedBaseTypes; } function getTupleBaseType(type: TupleType) { const elementTypes = sameMap(type.typeParameters, (t, i) => type.elementFlags[i] & ElementFlags.Variadic ? getIndexedAccessType(t, numberType) : t); return createArrayType(getUnionType(elementTypes || emptyArray), type.readonly); } function resolveBaseTypesOfClass(type: InterfaceType) { type.resolvedBaseTypes = resolvingEmptyArray; const baseConstructorType = getApparentType(getBaseConstructorTypeOfClass(type)); if (!(baseConstructorType.flags & (TypeFlags.Object | TypeFlags.Intersection | TypeFlags.Any))) { return type.resolvedBaseTypes = emptyArray; } const baseTypeNode = getBaseTypeNodeOfClass(type)!; let baseType: Type; const originalBaseType = baseConstructorType.symbol ? getDeclaredTypeOfSymbol(baseConstructorType.symbol) : undefined; if ( baseConstructorType.symbol && baseConstructorType.symbol.flags & SymbolFlags.Class && areAllOuterTypeParametersApplied(originalBaseType!) ) { // When base constructor type is a class with no captured type arguments we know that the constructors all have the same type parameters as the // class and all return the instance type of the class. There is no need for further checks and we can apply the // type arguments in the same manner as a type reference to get the same error reporting experience. baseType = getTypeFromClassOrInterfaceReference(baseTypeNode, baseConstructorType.symbol); } else if (baseConstructorType.flags & TypeFlags.Any) { baseType = baseConstructorType; } else { // The class derives from a "class-like" constructor function, check that we have at least one construct signature // with a matching number of type parameters and use the return type of the first instantiated signature. Elsewhere // we check that all instantiated signatures return the same type. const constructors = getInstantiatedConstructorsForTypeArguments(baseConstructorType, baseTypeNode.typeArguments, baseTypeNode); if (!constructors.length) { error(baseTypeNode.expression, Diagnostics.No_base_constructor_has_the_specified_number_of_type_arguments); return type.resolvedBaseTypes = emptyArray; } baseType = getReturnTypeOfSignature(constructors[0]); } if (isErrorType(baseType)) { return type.resolvedBaseTypes = emptyArray; } const reducedBaseType = getReducedType(baseType); if (!isValidBaseType(reducedBaseType)) { const elaboration = elaborateNeverIntersection(/*errorInfo*/ undefined, baseType); const diagnostic = chainDiagnosticMessages(elaboration, Diagnostics.Base_constructor_return_type_0_is_not_an_object_type_or_intersection_of_object_types_with_statically_known_members, typeToString(reducedBaseType)); diagnostics.add(createDiagnosticForNodeFromMessageChain(getSourceFileOfNode(baseTypeNode.expression), baseTypeNode.expression, diagnostic)); return type.resolvedBaseTypes = emptyArray; } if (type === reducedBaseType || hasBaseType(reducedBaseType, type)) { error(type.symbol.valueDeclaration, Diagnostics.Type_0_recursively_references_itself_as_a_base_type, typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType)); return type.resolvedBaseTypes = emptyArray; } if (type.resolvedBaseTypes === resolvingEmptyArray) { // Circular reference, likely through instantiation of default parameters // (otherwise there'd be an error from hasBaseType) - this is fine, but `.members` should be reset // as `getIndexedAccessType` via `instantiateType` via `getTypeFromClassOrInterfaceReference` forces a // partial instantiation of the members without the base types fully resolved type.members = undefined; } return type.resolvedBaseTypes = [reducedBaseType]; } function areAllOuterTypeParametersApplied(type: Type): boolean { // TODO: GH#18217 Shouldn't this take an InterfaceType? // An unapplied type parameter has its symbol still the same as the matching argument symbol. // Since parameters are applied outer-to-inner, only the last outer parameter needs to be checked. const outerTypeParameters = (type as InterfaceType).outerTypeParameters; if (outerTypeParameters) { const last = outerTypeParameters.length - 1; const typeArguments = getTypeArguments(type as TypeReference); return outerTypeParameters[last].symbol !== typeArguments[last].symbol; } return true; } // A valid base type is `any`, an object type or intersection of object types. function isValidBaseType(type: Type): type is BaseType { if (type.flags & TypeFlags.TypeParameter) { const constraint = getBaseConstraintOfType(type); if (constraint) { return isValidBaseType(constraint); } } // TODO: Given that we allow type parmeters here now, is this `!isGenericMappedType(type)` check really needed? // There's no reason a `T` should be allowed while a `Readonly` should not. return !!(type.flags & (TypeFlags.Object | TypeFlags.NonPrimitive | TypeFlags.Any) && !isGenericMappedType(type) || type.flags & TypeFlags.Intersection && every((type as IntersectionType).types, isValidBaseType)); } function resolveBaseTypesOfInterface(type: InterfaceType): void { type.resolvedBaseTypes = type.resolvedBaseTypes || emptyArray; if (type.symbol.declarations) { for (const declaration of type.symbol.declarations) { if (declaration.kind === SyntaxKind.InterfaceDeclaration && getInterfaceBaseTypeNodes(declaration as InterfaceDeclaration)) { for (const node of getInterfaceBaseTypeNodes(declaration as InterfaceDeclaration)!) { const baseType = getReducedType(getTypeFromTypeNode(node)); if (!isErrorType(baseType)) { if (isValidBaseType(baseType)) { if (type !== baseType && !hasBaseType(baseType, type)) { if (type.resolvedBaseTypes === emptyArray) { type.resolvedBaseTypes = [baseType as ObjectType]; } else { type.resolvedBaseTypes.push(baseType); } } else { reportCircularBaseType(declaration, type); } } else { error(node, Diagnostics.An_interface_can_only_extend_an_object_type_or_intersection_of_object_types_with_statically_known_members); } } } } } } } /** * Returns true if the interface given by the symbol is free of "this" references. * * Specifically, the result is true if the interface itself contains no references * to "this" in its body, if all base types are interfaces, * and if none of the base interfaces have a "this" type. */ function isThislessInterface(symbol: Symbol): boolean { if (!symbol.declarations) { return true; } for (const declaration of symbol.declarations) { if (declaration.kind === SyntaxKind.InterfaceDeclaration) { if (declaration.flags & NodeFlags.ContainsThis) { return false; } const baseTypeNodes = getInterfaceBaseTypeNodes(declaration as InterfaceDeclaration); if (baseTypeNodes) { for (const node of baseTypeNodes) { if (isEntityNameExpression(node.expression)) { const baseSymbol = resolveEntityName(node.expression, SymbolFlags.Type, /*ignoreErrors*/ true); if (!baseSymbol || !(baseSymbol.flags & SymbolFlags.Interface) || getDeclaredTypeOfClassOrInterface(baseSymbol).thisType) { return false; } } } } } } return true; } function getDeclaredTypeOfClassOrInterface(symbol: Symbol): InterfaceType { let links = getSymbolLinks(symbol); const originalLinks = links; if (!links.declaredType) { const kind = symbol.flags & SymbolFlags.Class ? ObjectFlags.Class : ObjectFlags.Interface; const merged = mergeJSSymbols(symbol, symbol.valueDeclaration && getAssignedClassSymbol(symbol.valueDeclaration)); if (merged) { // note:we overwrite links because we just cloned the symbol symbol = merged; links = merged.links; } const type = originalLinks.declaredType = links.declaredType = createObjectType(kind, symbol) as InterfaceType; const outerTypeParameters = getOuterTypeParametersOfClassOrInterface(symbol); const localTypeParameters = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol); // A class or interface is generic if it has type parameters or a "this" type. We always give classes a "this" type // because it is not feasible to analyze all members to determine if the "this" type escapes the class (in particular, // property types inferred from initializers and method return types inferred from return statements are very hard // to exhaustively analyze). We give interfaces a "this" type if we can't definitely determine that they are free of // "this" references. if (outerTypeParameters || localTypeParameters || kind === ObjectFlags.Class || !isThislessInterface(symbol)) { type.objectFlags |= ObjectFlags.Reference; type.typeParameters = concatenate(outerTypeParameters, localTypeParameters); type.outerTypeParameters = outerTypeParameters; type.localTypeParameters = localTypeParameters; (type as GenericType).instantiations = new Map(); (type as GenericType).instantiations.set(getTypeListId(type.typeParameters), type as GenericType); (type as GenericType).target = type as GenericType; (type as GenericType).resolvedTypeArguments = type.typeParameters; type.thisType = createTypeParameter(symbol); type.thisType.isThisType = true; type.thisType.constraint = type; } } return links.declaredType as InterfaceType; } function getDeclaredTypeOfTypeAlias(symbol: Symbol): Type { const links = getSymbolLinks(symbol); if (!links.declaredType) { // Note that we use the links object as the target here because the symbol object is used as the unique // identity for resolution of the 'type' property in SymbolLinks. if (!pushTypeResolution(symbol, TypeSystemPropertyName.DeclaredType)) { return errorType; } const declaration = Debug.checkDefined(symbol.declarations?.find(isTypeAlias), "Type alias symbol with no valid declaration found"); const typeNode = isJSDocTypeAlias(declaration) ? declaration.typeExpression : declaration.type; // If typeNode is missing, we will error in checkJSDocTypedefTag. let type = typeNode ? getTypeFromTypeNode(typeNode) : errorType; if (popTypeResolution()) { const typeParameters = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol); if (typeParameters) { // Initialize the instantiation cache for generic type aliases. The declared type corresponds to // an instantiation of the type alias with the type parameters supplied as type arguments. links.typeParameters = typeParameters; links.instantiations = new Map(); links.instantiations.set(getTypeListId(typeParameters), type); } if (type === intrinsicMarkerType && symbol.escapedName === "BuiltinIteratorReturn") { type = getBuiltinIteratorReturnType(); } } else { type = errorType; if (declaration.kind === SyntaxKind.JSDocEnumTag) { error(declaration.typeExpression.type, Diagnostics.Type_alias_0_circularly_references_itself, symbolToString(symbol)); } else { error(isNamedDeclaration(declaration) ? declaration.name || declaration : declaration, Diagnostics.Type_alias_0_circularly_references_itself, symbolToString(symbol)); } } links.declaredType ??= type; } return links.declaredType; } function getBaseTypeOfEnumLikeType(type: Type) { return type.flags & TypeFlags.EnumLike && type.symbol.flags & SymbolFlags.EnumMember ? getDeclaredTypeOfSymbol(getParentOfSymbol(type.symbol)!) : type; } function getDeclaredTypeOfEnum(symbol: Symbol): Type { const links = getSymbolLinks(symbol); if (!links.declaredType) { const memberTypeList: Type[] = []; if (symbol.declarations) { for (const declaration of symbol.declarations) { if (declaration.kind === SyntaxKind.EnumDeclaration) { for (const member of (declaration as EnumDeclaration).members) { if (hasBindableName(member)) { const memberSymbol = getSymbolOfDeclaration(member); const value = getEnumMemberValue(member).value; const memberType = getFreshTypeOfLiteralType( value !== undefined ? getEnumLiteralType(value, getSymbolId(symbol), memberSymbol) : createComputedEnumType(memberSymbol), ); getSymbolLinks(memberSymbol).declaredType = memberType; memberTypeList.push(getRegularTypeOfLiteralType(memberType)); } } } } } const enumType = memberTypeList.length ? getUnionType(memberTypeList, UnionReduction.Literal, symbol, /*aliasTypeArguments*/ undefined) : createComputedEnumType(symbol); if (enumType.flags & TypeFlags.Union) { enumType.flags |= TypeFlags.EnumLiteral; enumType.symbol = symbol; } links.declaredType = enumType; } return links.declaredType; } function createComputedEnumType(symbol: Symbol) { const regularType = createTypeWithSymbol(TypeFlags.Enum, symbol) as EnumType; const freshType = createTypeWithSymbol(TypeFlags.Enum, symbol) as EnumType; regularType.regularType = regularType; regularType.freshType = freshType; freshType.regularType = regularType; freshType.freshType = freshType; return regularType; } function getDeclaredTypeOfEnumMember(symbol: Symbol): Type { const links = getSymbolLinks(symbol); if (!links.declaredType) { const enumType = getDeclaredTypeOfEnum(getParentOfSymbol(symbol)!); if (!links.declaredType) { links.declaredType = enumType; } } return links.declaredType; } function getDeclaredTypeOfTypeParameter(symbol: Symbol): TypeParameter { const links = getSymbolLinks(symbol); return links.declaredType || (links.declaredType = createTypeParameter(symbol)); } function getDeclaredTypeOfAlias(symbol: Symbol): Type { const links = getSymbolLinks(symbol); return links.declaredType || (links.declaredType = getDeclaredTypeOfSymbol(resolveAlias(symbol))); } function getDeclaredTypeOfSymbol(symbol: Symbol): Type { return tryGetDeclaredTypeOfSymbol(symbol) || errorType; } function tryGetDeclaredTypeOfSymbol(symbol: Symbol): Type | undefined { if (symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { return getDeclaredTypeOfClassOrInterface(symbol); } if (symbol.flags & SymbolFlags.TypeAlias) { return getDeclaredTypeOfTypeAlias(symbol); } if (symbol.flags & SymbolFlags.TypeParameter) { return getDeclaredTypeOfTypeParameter(symbol); } if (symbol.flags & SymbolFlags.Enum) { return getDeclaredTypeOfEnum(symbol); } if (symbol.flags & SymbolFlags.EnumMember) { return getDeclaredTypeOfEnumMember(symbol); } if (symbol.flags & SymbolFlags.Alias) { return getDeclaredTypeOfAlias(symbol); } return undefined; } /** * A type is free of this references if it's the any, string, number, boolean, symbol, or void keyword, a string * literal type, an array with an element type that is free of this references, or a type reference that is * free of this references. */ function isThislessType(node: TypeNode): boolean { switch (node.kind) { case SyntaxKind.AnyKeyword: case SyntaxKind.UnknownKeyword: case SyntaxKind.StringKeyword: case SyntaxKind.NumberKeyword: case SyntaxKind.BigIntKeyword: case SyntaxKind.BooleanKeyword: case SyntaxKind.SymbolKeyword: case SyntaxKind.ObjectKeyword: case SyntaxKind.VoidKeyword: case SyntaxKind.UndefinedKeyword: case SyntaxKind.NeverKeyword: case SyntaxKind.LiteralType: return true; case SyntaxKind.ArrayType: return isThislessType((node as ArrayTypeNode).elementType); case SyntaxKind.TypeReference: return !(node as TypeReferenceNode).typeArguments || (node as TypeReferenceNode).typeArguments!.every(isThislessType); } return false; } /** A type parameter is thisless if its constraint is thisless, or if it has no constraint. */ function isThislessTypeParameter(node: TypeParameterDeclaration) { const constraint = getEffectiveConstraintOfTypeParameter(node); return !constraint || isThislessType(constraint); } /** * A variable-like declaration is free of this references if it has a type annotation * that is thisless, or if it has no type annotation and no initializer (and is thus of type any). */ function isThislessVariableLikeDeclaration(node: VariableLikeDeclaration): boolean { const typeNode = getEffectiveTypeAnnotationNode(node); return typeNode ? isThislessType(typeNode) : !hasInitializer(node); } /** * A function-like declaration is considered free of `this` references if it has a return type * annotation that is free of this references and if each parameter is thisless and if * each type parameter (if present) is thisless. */ function isThislessFunctionLikeDeclaration(node: FunctionLikeDeclaration): boolean { const returnType = getEffectiveReturnTypeNode(node); const typeParameters = getEffectiveTypeParameterDeclarations(node); return (node.kind === SyntaxKind.Constructor || (!!returnType && isThislessType(returnType))) && node.parameters.every(isThislessVariableLikeDeclaration) && typeParameters.every(isThislessTypeParameter); } /** * Returns true if the class or interface member given by the symbol is free of "this" references. The * function may return false for symbols that are actually free of "this" references because it is not * feasible to perform a complete analysis in all cases. In particular, property members with types * inferred from their initializers and function members with inferred return types are conservatively * assumed not to be free of "this" references. */ function isThisless(symbol: Symbol): boolean { if (symbol.declarations && symbol.declarations.length === 1) { const declaration = symbol.declarations[0]; if (declaration) { switch (declaration.kind) { case SyntaxKind.PropertyDeclaration: case SyntaxKind.PropertySignature: return isThislessVariableLikeDeclaration(declaration as VariableLikeDeclaration); case SyntaxKind.MethodDeclaration: case SyntaxKind.MethodSignature: case SyntaxKind.Constructor: case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: return isThislessFunctionLikeDeclaration(declaration as FunctionLikeDeclaration | AccessorDeclaration); } } } return false; } // The mappingThisOnly flag indicates that the only type parameter being mapped is "this". When the flag is true, // we check symbols to see if we can quickly conclude they are free of "this" references, thus needing no instantiation. function createInstantiatedSymbolTable(symbols: Symbol[], mapper: TypeMapper, mappingThisOnly: boolean): SymbolTable { const result = createSymbolTable(); for (const symbol of symbols) { result.set(symbol.escapedName, mappingThisOnly && isThisless(symbol) ? symbol : instantiateSymbol(symbol, mapper)); } return result; } function addInheritedMembers(symbols: SymbolTable, baseSymbols: Symbol[]) { for (const base of baseSymbols) { if (isStaticPrivateIdentifierProperty(base)) { continue; } const derived = symbols.get(base.escapedName); if ( !derived // non-constructor/static-block assignment declarations are ignored here; they're not treated as overrides || derived.valueDeclaration && isBinaryExpression(derived.valueDeclaration) && !isConstructorDeclaredProperty(derived) && !getContainingClassStaticBlock(derived.valueDeclaration) ) { symbols.set(base.escapedName, base); symbols.set(base.escapedName, base); } } } function isStaticPrivateIdentifierProperty(s: Symbol): boolean { return !!s.valueDeclaration && isPrivateIdentifierClassElementDeclaration(s.valueDeclaration) && isStatic(s.valueDeclaration); } function resolveDeclaredMembers(type: InterfaceType): InterfaceTypeWithDeclaredMembers { if (!(type as InterfaceTypeWithDeclaredMembers).declaredProperties) { const symbol = type.symbol; const members = getMembersOfSymbol(symbol); (type as InterfaceTypeWithDeclaredMembers).declaredProperties = getNamedMembers(members); // Start with signatures at empty array in case of recursive types (type as InterfaceTypeWithDeclaredMembers).declaredCallSignatures = emptyArray; (type as InterfaceTypeWithDeclaredMembers).declaredConstructSignatures = emptyArray; (type as InterfaceTypeWithDeclaredMembers).declaredIndexInfos = emptyArray; (type as InterfaceTypeWithDeclaredMembers).declaredCallSignatures = getSignaturesOfSymbol(members.get(InternalSymbolName.Call)); (type as InterfaceTypeWithDeclaredMembers).declaredConstructSignatures = getSignaturesOfSymbol(members.get(InternalSymbolName.New)); (type as InterfaceTypeWithDeclaredMembers).declaredIndexInfos = getIndexInfosOfSymbol(symbol); } return type as InterfaceTypeWithDeclaredMembers; } /** * Indicates whether a declaration name is definitely late-bindable. * A declaration name is only late-bindable if: * - It is a `ComputedPropertyName`. * - Its expression is an `Identifier` or either a `PropertyAccessExpression` an * `ElementAccessExpression` consisting only of these same three types of nodes. * - The type of its expression is a string or numeric literal type, or is a `unique symbol` type. */ function isLateBindableName(node: DeclarationName): node is LateBoundName { if (!isComputedPropertyName(node) && !isElementAccessExpression(node)) { return false; } const expr = isComputedPropertyName(node) ? node.expression : node.argumentExpression; return isEntityNameExpression(expr) && isTypeUsableAsPropertyName(isComputedPropertyName(node) ? checkComputedPropertyName(node) : checkExpressionCached(expr)); } function isLateBoundName(name: __String): boolean { return (name as string).charCodeAt(0) === CharacterCodes._ && (name as string).charCodeAt(1) === CharacterCodes._ && (name as string).charCodeAt(2) === CharacterCodes.at; } /** * Indicates whether a declaration has a late-bindable dynamic name. */ function hasLateBindableName(node: Declaration): node is LateBoundDeclaration | LateBoundBinaryExpressionDeclaration { const name = getNameOfDeclaration(node); return !!name && isLateBindableName(name); } /** * Indicates whether a declaration has an early-bound name or a dynamic name that can be late-bound. */ function hasBindableName(node: Declaration) { return !hasDynamicName(node) || hasLateBindableName(node); } /** * Indicates whether a declaration name is a dynamic name that cannot be late-bound. */ function isNonBindableDynamicName(node: DeclarationName) { return isDynamicName(node) && !isLateBindableName(node); } /** * Adds a declaration to a late-bound dynamic member. This performs the same function for * late-bound members that `addDeclarationToSymbol` in binder.ts performs for early-bound * members. */ function addDeclarationToLateBoundSymbol(symbol: Symbol, member: LateBoundDeclaration | BinaryExpression, symbolFlags: SymbolFlags) { Debug.assert(!!(getCheckFlags(symbol) & CheckFlags.Late), "Expected a late-bound symbol."); symbol.flags |= symbolFlags; getSymbolLinks(member.symbol).lateSymbol = symbol; if (!symbol.declarations) { symbol.declarations = [member]; } else if (!member.symbol.isReplaceableByMethod) { symbol.declarations.push(member); } if (symbolFlags & SymbolFlags.Value) { if (!symbol.valueDeclaration || symbol.valueDeclaration.kind !== member.kind) { symbol.valueDeclaration = member; } } } /** * Performs late-binding of a dynamic member. This performs the same function for * late-bound members that `declareSymbol` in binder.ts performs for early-bound * members. * * If a symbol is a dynamic name from a computed property, we perform an additional "late" * binding phase to attempt to resolve the name for the symbol from the type of the computed * property's expression. If the type of the expression is a string-literal, numeric-literal, * or unique symbol type, we can use that type as the name of the symbol. * * For example, given: * * const x = Symbol(); * * interface I { * [x]: number; * } * * The binder gives the property `[x]: number` a special symbol with the name "__computed". * In the late-binding phase we can type-check the expression `x` and see that it has a * unique symbol type which we can then use as the name of the member. This allows users * to define custom symbols that can be used in the members of an object type. * * @param parent The containing symbol for the member. * @param earlySymbols The early-bound symbols of the parent. * @param lateSymbols The late-bound symbols of the parent. * @param decl The member to bind. */ function lateBindMember(parent: Symbol, earlySymbols: SymbolTable | undefined, lateSymbols: Map<__String, TransientSymbol>, decl: LateBoundDeclaration | LateBoundBinaryExpressionDeclaration) { Debug.assert(!!decl.symbol, "The member is expected to have a symbol."); const links = getNodeLinks(decl); if (!links.resolvedSymbol) { // In the event we attempt to resolve the late-bound name of this member recursively, // fall back to the early-bound name of this member. links.resolvedSymbol = decl.symbol; const declName = isBinaryExpression(decl) ? decl.left : decl.name; const type = isElementAccessExpression(declName) ? checkExpressionCached(declName.argumentExpression) : checkComputedPropertyName(declName); if (isTypeUsableAsPropertyName(type)) { const memberName = getPropertyNameFromType(type); const symbolFlags = decl.symbol.flags; // Get or add a late-bound symbol for the member. This allows us to merge late-bound accessor declarations. let lateSymbol = lateSymbols.get(memberName); if (!lateSymbol) lateSymbols.set(memberName, lateSymbol = createSymbol(SymbolFlags.None, memberName, CheckFlags.Late)); // Report an error if there's a symbol declaration with the same name and conflicting flags. const earlySymbol = earlySymbols && earlySymbols.get(memberName); // Duplicate property declarations of classes are checked in checkClassForDuplicateDeclarations. if (!(parent.flags & SymbolFlags.Class) && lateSymbol.flags & getExcludedSymbolFlags(symbolFlags)) { // If we have an existing early-bound member, combine its declarations so that we can // report an error at each declaration. const declarations = earlySymbol ? concatenate(earlySymbol.declarations, lateSymbol.declarations) : lateSymbol.declarations; const name = !(type.flags & TypeFlags.UniqueESSymbol) && unescapeLeadingUnderscores(memberName) || declarationNameToString(declName); forEach(declarations, declaration => error(getNameOfDeclaration(declaration) || declaration, Diagnostics.Property_0_was_also_declared_here, name)); error(declName || decl, Diagnostics.Duplicate_property_0, name); lateSymbol = createSymbol(SymbolFlags.None, memberName, CheckFlags.Late); } lateSymbol.links.nameType = type; addDeclarationToLateBoundSymbol(lateSymbol, decl, symbolFlags); if (lateSymbol.parent) { Debug.assert(lateSymbol.parent === parent, "Existing symbol parent should match new one"); } else { lateSymbol.parent = parent; } return links.resolvedSymbol = lateSymbol; } } return links.resolvedSymbol; } function getResolvedMembersOrExportsOfSymbol(symbol: Symbol, resolutionKind: MembersOrExportsResolutionKind): Map<__String, Symbol> { const links = getSymbolLinks(symbol); if (!links[resolutionKind]) { const isStatic = resolutionKind === MembersOrExportsResolutionKind.resolvedExports; const earlySymbols = !isStatic ? symbol.members : symbol.flags & SymbolFlags.Module ? getExportsOfModuleWorker(symbol).exports : symbol.exports; // In the event we recursively resolve the members/exports of the symbol, we // set the initial value of resolvedMembers/resolvedExports to the early-bound // members/exports of the symbol. links[resolutionKind] = earlySymbols || emptySymbols; // fill in any as-yet-unresolved late-bound members. const lateSymbols = createSymbolTable() as Map<__String, TransientSymbol>; for (const decl of symbol.declarations || emptyArray) { const members = getMembersOfDeclaration(decl); if (members) { for (const member of members) { if (isStatic === hasStaticModifier(member)) { if (hasLateBindableName(member)) { lateBindMember(symbol, earlySymbols, lateSymbols, member); } } } } } const assignments = getFunctionExpressionParentSymbolOrSymbol(symbol).assignmentDeclarationMembers; if (assignments) { const decls = arrayFrom(assignments.values()); for (const member of decls) { const assignmentKind = getAssignmentDeclarationKind(member as BinaryExpression | CallExpression); const isInstanceMember = assignmentKind === AssignmentDeclarationKind.PrototypeProperty || isBinaryExpression(member) && isPossiblyAliasedThisProperty(member, assignmentKind) || assignmentKind === AssignmentDeclarationKind.ObjectDefinePrototypeProperty || assignmentKind === AssignmentDeclarationKind.Prototype; // A straight `Prototype` assignment probably can never have a computed name if (isStatic === !isInstanceMember) { if (hasLateBindableName(member)) { lateBindMember(symbol, earlySymbols, lateSymbols, member); } } } } let resolved = combineSymbolTables(earlySymbols, lateSymbols); if (symbol.flags & SymbolFlags.Transient && links.cjsExportMerged && symbol.declarations) { for (const decl of symbol.declarations) { const original = getSymbolLinks(decl.symbol)[resolutionKind]; if (!resolved) { resolved = original; continue; } if (!original) continue; original.forEach((s, name) => { const existing = resolved!.get(name); if (!existing) resolved!.set(name, s); else if (existing === s) return; else resolved!.set(name, mergeSymbol(existing, s)); }); } } links[resolutionKind] = resolved || emptySymbols; } return links[resolutionKind]; } /** * Gets a SymbolTable containing both the early- and late-bound members of a symbol. * * For a description of late-binding, see `lateBindMember`. */ function getMembersOfSymbol(symbol: Symbol) { return symbol.flags & SymbolFlags.LateBindingContainer ? getResolvedMembersOrExportsOfSymbol(symbol, MembersOrExportsResolutionKind.resolvedMembers) : symbol.members || emptySymbols; } /** * If a symbol is the dynamic name of the member of an object type, get the late-bound * symbol of the member. * * For a description of late-binding, see `lateBindMember`. */ function getLateBoundSymbol(symbol: Symbol): Symbol { if (symbol.flags & SymbolFlags.ClassMember && symbol.escapedName === InternalSymbolName.Computed) { const links = getSymbolLinks(symbol); if (!links.lateSymbol && some(symbol.declarations, hasLateBindableName)) { // force late binding of members/exports. This will set the late-bound symbol const parent = getMergedSymbol(symbol.parent)!; if (some(symbol.declarations, hasStaticModifier)) { getExportsOfSymbol(parent); } else { getMembersOfSymbol(parent); } } return links.lateSymbol || (links.lateSymbol = symbol); } return symbol; } function getTypeWithThisArgument(type: Type, thisArgument?: Type, needApparentType?: boolean): Type { if (getObjectFlags(type) & ObjectFlags.Reference) { const target = (type as TypeReference).target; const typeArguments = getTypeArguments(type as TypeReference); return length(target.typeParameters) === length(typeArguments) ? createTypeReference(target, concatenate(typeArguments, [thisArgument || target.thisType!])) : type; } else if (type.flags & TypeFlags.Intersection) { const types = sameMap((type as IntersectionType).types, t => getTypeWithThisArgument(t, thisArgument, needApparentType)); return types !== (type as IntersectionType).types ? getIntersectionType(types) : type; } return needApparentType ? getApparentType(type) : type; } function resolveObjectTypeMembers(type: ObjectType, source: InterfaceTypeWithDeclaredMembers, typeParameters: readonly TypeParameter[], typeArguments: readonly Type[]) { let mapper: TypeMapper | undefined; let members: SymbolTable; let callSignatures: readonly Signature[]; let constructSignatures: readonly Signature[]; let indexInfos: readonly IndexInfo[]; if (rangeEquals(typeParameters, typeArguments, 0, typeParameters.length)) { members = source.symbol ? getMembersOfSymbol(source.symbol) : createSymbolTable(source.declaredProperties); callSignatures = source.declaredCallSignatures; constructSignatures = source.declaredConstructSignatures; indexInfos = source.declaredIndexInfos; } else { mapper = createTypeMapper(typeParameters, typeArguments); members = createInstantiatedSymbolTable(source.declaredProperties, mapper, /*mappingThisOnly*/ typeParameters.length === 1); callSignatures = instantiateSignatures(source.declaredCallSignatures, mapper); constructSignatures = instantiateSignatures(source.declaredConstructSignatures, mapper); indexInfos = instantiateIndexInfos(source.declaredIndexInfos, mapper); } const baseTypes = getBaseTypes(source); if (baseTypes.length) { if (source.symbol && members === getMembersOfSymbol(source.symbol)) { const symbolTable = createSymbolTable(source.declaredProperties); // copy index signature symbol as well (for quickinfo) const sourceIndex = getIndexSymbol(source.symbol); if (sourceIndex) { symbolTable.set(InternalSymbolName.Index, sourceIndex); } members = symbolTable; } setStructuredTypeMembers(type, members, callSignatures, constructSignatures, indexInfos); const thisArgument = lastOrUndefined(typeArguments); for (const baseType of baseTypes) { const instantiatedBaseType = thisArgument ? getTypeWithThisArgument(instantiateType(baseType, mapper), thisArgument) : baseType; addInheritedMembers(members, getPropertiesOfType(instantiatedBaseType)); callSignatures = concatenate(callSignatures, getSignaturesOfType(instantiatedBaseType, SignatureKind.Call)); constructSignatures = concatenate(constructSignatures, getSignaturesOfType(instantiatedBaseType, SignatureKind.Construct)); const inheritedIndexInfos = instantiatedBaseType !== anyType ? getIndexInfosOfType(instantiatedBaseType) : [createIndexInfo(stringType, anyType, /*isReadonly*/ false)]; indexInfos = concatenate(indexInfos, filter(inheritedIndexInfos, info => !findIndexInfo(indexInfos, info.keyType))); } } setStructuredTypeMembers(type, members, callSignatures, constructSignatures, indexInfos); } function resolveClassOrInterfaceMembers(type: InterfaceType): void { resolveObjectTypeMembers(type, resolveDeclaredMembers(type), emptyArray, emptyArray); } function resolveTypeReferenceMembers(type: TypeReference): void { const source = resolveDeclaredMembers(type.target); const typeParameters = concatenate(source.typeParameters!, [source.thisType!]); const typeArguments = getTypeArguments(type); const paddedTypeArguments = typeArguments.length === typeParameters.length ? typeArguments : concatenate(typeArguments, [type]); resolveObjectTypeMembers(type, source, typeParameters, paddedTypeArguments); } function createSignature( declaration: SignatureDeclaration | JSDocSignature | undefined, typeParameters: readonly TypeParameter[] | undefined, thisParameter: Symbol | undefined, parameters: readonly Symbol[], resolvedReturnType: Type | undefined, resolvedTypePredicate: TypePredicate | undefined, minArgumentCount: number, flags: SignatureFlags, ): Signature { const sig = new Signature(checker, flags); sig.declaration = declaration; sig.typeParameters = typeParameters; sig.parameters = parameters; sig.thisParameter = thisParameter; sig.resolvedReturnType = resolvedReturnType; sig.resolvedTypePredicate = resolvedTypePredicate; sig.minArgumentCount = minArgumentCount; sig.resolvedMinArgumentCount = undefined; sig.target = undefined; sig.mapper = undefined; sig.compositeSignatures = undefined; sig.compositeKind = undefined; return sig; } function cloneSignature(sig: Signature): Signature { const result = createSignature(sig.declaration, sig.typeParameters, sig.thisParameter, sig.parameters, /*resolvedReturnType*/ undefined, /*resolvedTypePredicate*/ undefined, sig.minArgumentCount, sig.flags & SignatureFlags.PropagatingFlags); result.target = sig.target; result.mapper = sig.mapper; result.compositeSignatures = sig.compositeSignatures; result.compositeKind = sig.compositeKind; return result; } function createUnionSignature(signature: Signature, unionSignatures: Signature[]) { const result = cloneSignature(signature); result.compositeSignatures = unionSignatures; result.compositeKind = TypeFlags.Union; result.target = undefined; result.mapper = undefined; return result; } function getOptionalCallSignature(signature: Signature, callChainFlags: SignatureFlags): Signature { if ((signature.flags & SignatureFlags.CallChainFlags) === callChainFlags) { return signature; } if (!signature.optionalCallSignatureCache) { signature.optionalCallSignatureCache = {}; } const key = callChainFlags === SignatureFlags.IsInnerCallChain ? "inner" : "outer"; return signature.optionalCallSignatureCache[key] || (signature.optionalCallSignatureCache[key] = createOptionalCallSignature(signature, callChainFlags)); } function createOptionalCallSignature(signature: Signature, callChainFlags: SignatureFlags) { Debug.assert(callChainFlags === SignatureFlags.IsInnerCallChain || callChainFlags === SignatureFlags.IsOuterCallChain, "An optional call signature can either be for an inner call chain or an outer call chain, but not both."); const result = cloneSignature(signature); result.flags |= callChainFlags; return result; } function getExpandedParameters(sig: Signature, skipUnionExpanding?: boolean): readonly (readonly Symbol[])[] { if (signatureHasRestParameter(sig)) { const restIndex = sig.parameters.length - 1; const restSymbol = sig.parameters[restIndex]; const restType = getTypeOfSymbol(restSymbol); if (isTupleType(restType)) { return [expandSignatureParametersWithTupleMembers(restType, restIndex, restSymbol)]; } else if (!skipUnionExpanding && restType.flags & TypeFlags.Union && every((restType as UnionType).types, isTupleType)) { return map((restType as UnionType).types, t => expandSignatureParametersWithTupleMembers(t as TupleTypeReference, restIndex, restSymbol)); } } return [sig.parameters]; function expandSignatureParametersWithTupleMembers(restType: TupleTypeReference, restIndex: number, restSymbol: Symbol) { const elementTypes = getTypeArguments(restType); const associatedNames = getUniqAssociatedNamesFromTupleType(restType, restSymbol); const restParams = map(elementTypes, (t, i) => { // Lookup the label from the individual tuple passed in before falling back to the signature `rest` parameter name const name = associatedNames && associatedNames[i] ? associatedNames[i] : getParameterNameAtPosition(sig, restIndex + i, restType); const flags = restType.target.elementFlags[i]; const checkFlags = flags & ElementFlags.Variable ? CheckFlags.RestParameter : flags & ElementFlags.Optional ? CheckFlags.OptionalParameter : 0; const symbol = createSymbol(SymbolFlags.FunctionScopedVariable, name, checkFlags); symbol.links.type = flags & ElementFlags.Rest ? createArrayType(t) : t; return symbol; }); return concatenate(sig.parameters.slice(0, restIndex), restParams); } function getUniqAssociatedNamesFromTupleType(type: TupleTypeReference, restSymbol: Symbol) { const names = map(type.target.labeledElementDeclarations, (labeledElement, i) => getTupleElementLabel(labeledElement, i, type.target.elementFlags[i], restSymbol)); if (names) { const duplicates: number[] = []; const uniqueNames = new Set<__String>(); for (let i = 0; i < names.length; i++) { const name = names[i]; if (!tryAddToSet(uniqueNames, name)) { duplicates.push(i); } } const counters = new Map<__String, number>(); for (const i of duplicates) { let counter = counters.get(names[i]) ?? 1; let name: __String; while (!tryAddToSet(uniqueNames, name = `${names[i]}_${counter}` as __String)) { counter++; } names[i] = name; counters.set(names[i], counter + 1); } } return names; } } function getDefaultConstructSignatures(classType: InterfaceType): Signature[] { const baseConstructorType = getBaseConstructorTypeOfClass(classType); const baseSignatures = getSignaturesOfType(baseConstructorType, SignatureKind.Construct); const declaration = getClassLikeDeclarationOfSymbol(classType.symbol); const isAbstract = !!declaration && hasSyntacticModifier(declaration, ModifierFlags.Abstract); if (baseSignatures.length === 0) { return [createSignature(/*declaration*/ undefined, classType.localTypeParameters, /*thisParameter*/ undefined, emptyArray, classType, /*resolvedTypePredicate*/ undefined, 0, isAbstract ? SignatureFlags.Abstract : SignatureFlags.None)]; } const baseTypeNode = getBaseTypeNodeOfClass(classType)!; const isJavaScript = isInJSFile(baseTypeNode); const typeArguments = typeArgumentsFromTypeReferenceNode(baseTypeNode); const typeArgCount = length(typeArguments); const result: Signature[] = []; for (const baseSig of baseSignatures) { const minTypeArgumentCount = getMinTypeArgumentCount(baseSig.typeParameters); const typeParamCount = length(baseSig.typeParameters); if (isJavaScript || typeArgCount >= minTypeArgumentCount && typeArgCount <= typeParamCount) { const sig = typeParamCount ? createSignatureInstantiation(baseSig, fillMissingTypeArguments(typeArguments, baseSig.typeParameters, minTypeArgumentCount, isJavaScript)) : cloneSignature(baseSig); sig.typeParameters = classType.localTypeParameters; sig.resolvedReturnType = classType; sig.flags = isAbstract ? sig.flags | SignatureFlags.Abstract : sig.flags & ~SignatureFlags.Abstract; result.push(sig); } } return result; } function findMatchingSignature(signatureList: readonly Signature[], signature: Signature, partialMatch: boolean, ignoreThisTypes: boolean, ignoreReturnTypes: boolean): Signature | undefined { for (const s of signatureList) { if (compareSignaturesIdentical(s, signature, partialMatch, ignoreThisTypes, ignoreReturnTypes, partialMatch ? compareTypesSubtypeOf : compareTypesIdentical)) { return s; } } } function findMatchingSignatures(signatureLists: readonly (readonly Signature[])[], signature: Signature, listIndex: number): Signature[] | undefined { if (signature.typeParameters) { // We require an exact match for generic signatures, so we only return signatures from the first // signature list and only if they have exact matches in the other signature lists. if (listIndex > 0) { return undefined; } for (let i = 1; i < signatureLists.length; i++) { if (!findMatchingSignature(signatureLists[i], signature, /*partialMatch*/ false, /*ignoreThisTypes*/ false, /*ignoreReturnTypes*/ false)) { return undefined; } } return [signature]; } let result: Signature[] | undefined; for (let i = 0; i < signatureLists.length; i++) { // Allow matching non-generic signatures to have excess parameters (as a fallback if exact parameter match is not found) and different return types. // Prefer matching this types if possible. const match = i === listIndex ? signature : findMatchingSignature(signatureLists[i], signature, /*partialMatch*/ false, /*ignoreThisTypes*/ false, /*ignoreReturnTypes*/ true) || findMatchingSignature(signatureLists[i], signature, /*partialMatch*/ true, /*ignoreThisTypes*/ false, /*ignoreReturnTypes*/ true); if (!match) { return undefined; } result = appendIfUnique(result, match); } return result; } // The signatures of a union type are those signatures that are present in each of the constituent types. // Generic signatures must match exactly, but non-generic signatures are allowed to have extra optional // parameters and may differ in return types. When signatures differ in return types, the resulting return // type is the union of the constituent return types. function getUnionSignatures(signatureLists: readonly (readonly Signature[])[]): Signature[] { let result: Signature[] | undefined; let indexWithLengthOverOne: number | undefined; for (let i = 0; i < signatureLists.length; i++) { if (signatureLists[i].length === 0) return emptyArray; if (signatureLists[i].length > 1) { indexWithLengthOverOne = indexWithLengthOverOne === undefined ? i : -1; // -1 is a signal there are multiple overload sets } for (const signature of signatureLists[i]) { // Only process signatures with parameter lists that aren't already in the result list if (!result || !findMatchingSignature(result, signature, /*partialMatch*/ false, /*ignoreThisTypes*/ false, /*ignoreReturnTypes*/ true)) { const unionSignatures = findMatchingSignatures(signatureLists, signature, i); if (unionSignatures) { let s = signature; // Union the result types when more than one signature matches if (unionSignatures.length > 1) { let thisParameter = signature.thisParameter; const firstThisParameterOfUnionSignatures = forEach(unionSignatures, sig => sig.thisParameter); if (firstThisParameterOfUnionSignatures) { const thisType = getIntersectionType(mapDefined(unionSignatures, sig => sig.thisParameter && getTypeOfSymbol(sig.thisParameter))); thisParameter = createSymbolWithType(firstThisParameterOfUnionSignatures, thisType); } s = createUnionSignature(signature, unionSignatures); s.thisParameter = thisParameter; } (result || (result = [])).push(s); } } } } if (!length(result) && indexWithLengthOverOne !== -1) { // No sufficiently similar signature existed to subsume all the other signatures in the union - time to see if we can make a single // signature that handles all over them. We only do this when there are overloads in only one constituent. // (Overloads are conditional in nature and having overloads in multiple constituents would necessitate making a power set of // signatures from the type, whose ordering would be non-obvious) const masterList = signatureLists[indexWithLengthOverOne !== undefined ? indexWithLengthOverOne : 0]; let results: Signature[] | undefined = masterList.slice(); for (const signatures of signatureLists) { if (signatures !== masterList) { const signature = signatures[0]; Debug.assert(!!signature, "getUnionSignatures bails early on empty signature lists and should not have empty lists on second pass"); results = !!signature.typeParameters && some(results, s => !!s.typeParameters && !compareTypeParametersIdentical(signature.typeParameters, s.typeParameters)) ? undefined : map(results, sig => combineSignaturesOfUnionMembers(sig, signature)); if (!results) { break; } } } result = results; } return result || emptyArray; } function compareTypeParametersIdentical(sourceParams: readonly TypeParameter[] | undefined, targetParams: readonly TypeParameter[] | undefined): boolean { if (length(sourceParams) !== length(targetParams)) { return false; } if (!sourceParams || !targetParams) { return true; } const mapper = createTypeMapper(targetParams, sourceParams); for (let i = 0; i < sourceParams.length; i++) { const source = sourceParams[i]; const target = targetParams[i]; if (source === target) continue; // We instantiate the target type parameter constraints into the source types so we can recognize `` as the same as `` if (!isTypeIdenticalTo(getConstraintFromTypeParameter(source) || unknownType, instantiateType(getConstraintFromTypeParameter(target) || unknownType, mapper))) return false; // We don't compare defaults - we just use the type parameter defaults from the first signature that seems to match. // It might make sense to combine these defaults in the future, but doing so intelligently requires knowing // if the parameter is used covariantly or contravariantly (so we intersect if it's used like a parameter or union if used like a return type) // and, since it's just an inference _default_, just picking one arbitrarily works OK. } return true; } function combineUnionThisParam(left: Symbol | undefined, right: Symbol | undefined, mapper: TypeMapper | undefined): Symbol | undefined { if (!left || !right) { return left || right; } // A signature `this` type might be a read or a write position... It's very possible that it should be invariant // and we should refuse to merge signatures if there are `this` types and they do not match. However, so as to be // permissive when calling, for now, we'll intersect the `this` types just like we do for param types in union signatures. const thisType = getIntersectionType([getTypeOfSymbol(left), instantiateType(getTypeOfSymbol(right), mapper)]); return createSymbolWithType(left, thisType); } function combineUnionParameters(left: Signature, right: Signature, mapper: TypeMapper | undefined) { const leftCount = getParameterCount(left); const rightCount = getParameterCount(right); const longest = leftCount >= rightCount ? left : right; const shorter = longest === left ? right : left; const longestCount = longest === left ? leftCount : rightCount; const eitherHasEffectiveRest = hasEffectiveRestParameter(left) || hasEffectiveRestParameter(right); const needsExtraRestElement = eitherHasEffectiveRest && !hasEffectiveRestParameter(longest); const params = new Array(longestCount + (needsExtraRestElement ? 1 : 0)); for (let i = 0; i < longestCount; i++) { let longestParamType = tryGetTypeAtPosition(longest, i)!; if (longest === right) { longestParamType = instantiateType(longestParamType, mapper); } let shorterParamType = tryGetTypeAtPosition(shorter, i) || unknownType; if (shorter === right) { shorterParamType = instantiateType(shorterParamType, mapper); } const unionParamType = getIntersectionType([longestParamType, shorterParamType]); const isRestParam = eitherHasEffectiveRest && !needsExtraRestElement && i === (longestCount - 1); const isOptional = i >= getMinArgumentCount(longest) && i >= getMinArgumentCount(shorter); const leftName = i >= leftCount ? undefined : getParameterNameAtPosition(left, i); const rightName = i >= rightCount ? undefined : getParameterNameAtPosition(right, i); const paramName = leftName === rightName ? leftName : !leftName ? rightName : !rightName ? leftName : undefined; const paramSymbol = createSymbol( SymbolFlags.FunctionScopedVariable | (isOptional && !isRestParam ? SymbolFlags.Optional : 0), paramName || `arg${i}` as __String, isRestParam ? CheckFlags.RestParameter : isOptional ? CheckFlags.OptionalParameter : 0, ); paramSymbol.links.type = isRestParam ? createArrayType(unionParamType) : unionParamType; params[i] = paramSymbol; } if (needsExtraRestElement) { const restParamSymbol = createSymbol(SymbolFlags.FunctionScopedVariable, "args" as __String, CheckFlags.RestParameter); restParamSymbol.links.type = createArrayType(getTypeAtPosition(shorter, longestCount)); if (shorter === right) { restParamSymbol.links.type = instantiateType(restParamSymbol.links.type, mapper); } params[longestCount] = restParamSymbol; } return params; } function combineSignaturesOfUnionMembers(left: Signature, right: Signature): Signature { const typeParams = left.typeParameters || right.typeParameters; let paramMapper: TypeMapper | undefined; if (left.typeParameters && right.typeParameters) { paramMapper = createTypeMapper(right.typeParameters, left.typeParameters); // We just use the type parameter defaults from the first signature } const declaration = left.declaration; const params = combineUnionParameters(left, right, paramMapper); const thisParam = combineUnionThisParam(left.thisParameter, right.thisParameter, paramMapper); const minArgCount = Math.max(left.minArgumentCount, right.minArgumentCount); const result = createSignature( declaration, typeParams, thisParam, params, /*resolvedReturnType*/ undefined, /*resolvedTypePredicate*/ undefined, minArgCount, (left.flags | right.flags) & SignatureFlags.PropagatingFlags, ); result.compositeKind = TypeFlags.Union; result.compositeSignatures = concatenate(left.compositeKind !== TypeFlags.Intersection && left.compositeSignatures || [left], [right]); if (paramMapper) { result.mapper = left.compositeKind !== TypeFlags.Intersection && left.mapper && left.compositeSignatures ? combineTypeMappers(left.mapper, paramMapper) : paramMapper; } else if (left.compositeKind !== TypeFlags.Intersection && left.mapper && left.compositeSignatures) { result.mapper = left.mapper; } return result; } function getUnionIndexInfos(types: readonly Type[]): IndexInfo[] { const sourceInfos = getIndexInfosOfType(types[0]); if (sourceInfos) { const result = []; for (const info of sourceInfos) { const indexType = info.keyType; if (every(types, t => !!getIndexInfoOfType(t, indexType))) { result.push(createIndexInfo(indexType, getUnionType(map(types, t => getIndexTypeOfType(t, indexType)!)), some(types, t => getIndexInfoOfType(t, indexType)!.isReadonly))); } } return result; } return emptyArray; } function resolveUnionTypeMembers(type: UnionType) { // The members and properties collections are empty for union types. To get all properties of a union // type use getPropertiesOfType (only the language service uses this). const callSignatures = getUnionSignatures(map(type.types, t => t === globalFunctionType ? [unknownSignature] : getSignaturesOfType(t, SignatureKind.Call))); const constructSignatures = getUnionSignatures(map(type.types, t => getSignaturesOfType(t, SignatureKind.Construct))); const indexInfos = getUnionIndexInfos(type.types); setStructuredTypeMembers(type, emptySymbols, callSignatures, constructSignatures, indexInfos); } function intersectTypes(type1: Type, type2: Type): Type; function intersectTypes(type1: Type | undefined, type2: Type | undefined): Type | undefined; function intersectTypes(type1: Type | undefined, type2: Type | undefined): Type | undefined { return !type1 ? type2 : !type2 ? type1 : getIntersectionType([type1, type2]); } function findMixins(types: readonly Type[]): readonly boolean[] { const constructorTypeCount = countWhere(types, t => getSignaturesOfType(t, SignatureKind.Construct).length > 0); const mixinFlags = map(types, isMixinConstructorType); if (constructorTypeCount > 0 && constructorTypeCount === countWhere(mixinFlags, b => b)) { const firstMixinIndex = mixinFlags.indexOf(/*searchElement*/ true); mixinFlags[firstMixinIndex] = false; } return mixinFlags; } function includeMixinType(type: Type, types: readonly Type[], mixinFlags: readonly boolean[], index: number): Type { const mixedTypes: Type[] = []; for (let i = 0; i < types.length; i++) { if (i === index) { mixedTypes.push(type); } else if (mixinFlags[i]) { mixedTypes.push(getReturnTypeOfSignature(getSignaturesOfType(types[i], SignatureKind.Construct)[0])); } } return getIntersectionType(mixedTypes); } function resolveIntersectionTypeMembers(type: IntersectionType) { // The members and properties collections are empty for intersection types. To get all properties of an // intersection type use getPropertiesOfType (only the language service uses this). let callSignatures: Signature[] | undefined; let constructSignatures: Signature[] | undefined; let indexInfos: IndexInfo[] | undefined; const types = type.types; const mixinFlags = findMixins(types); const mixinCount = countWhere(mixinFlags, b => b); for (let i = 0; i < types.length; i++) { const t = type.types[i]; // When an intersection type contains mixin constructor types, the construct signatures from // those types are discarded and their return types are mixed into the return types of all // other construct signatures in the intersection type. For example, the intersection type // '{ new(...args: any[]) => A } & { new(s: string) => B }' has a single construct signature // 'new(s: string) => A & B'. if (!mixinFlags[i]) { let signatures = getSignaturesOfType(t, SignatureKind.Construct); if (signatures.length && mixinCount > 0) { signatures = map(signatures, s => { const clone = cloneSignature(s); clone.resolvedReturnType = includeMixinType(getReturnTypeOfSignature(s), types, mixinFlags, i); return clone; }); } constructSignatures = appendSignatures(constructSignatures, signatures); } callSignatures = appendSignatures(callSignatures, getSignaturesOfType(t, SignatureKind.Call)); indexInfos = reduceLeft(getIndexInfosOfType(t), (infos, newInfo) => appendIndexInfo(infos, newInfo, /*union*/ false), indexInfos); } setStructuredTypeMembers(type, emptySymbols, callSignatures || emptyArray, constructSignatures || emptyArray, indexInfos || emptyArray); } function appendSignatures(signatures: Signature[] | undefined, newSignatures: readonly Signature[]) { for (const sig of newSignatures) { if (!signatures || every(signatures, s => !compareSignaturesIdentical(s, sig, /*partialMatch*/ false, /*ignoreThisTypes*/ false, /*ignoreReturnTypes*/ false, compareTypesIdentical))) { signatures = append(signatures, sig); } } return signatures; } function appendIndexInfo(indexInfos: IndexInfo[] | undefined, newInfo: IndexInfo, union: boolean) { if (indexInfos) { for (let i = 0; i < indexInfos.length; i++) { const info = indexInfos[i]; if (info.keyType === newInfo.keyType) { indexInfos[i] = createIndexInfo(info.keyType, union ? getUnionType([info.type, newInfo.type]) : getIntersectionType([info.type, newInfo.type]), union ? info.isReadonly || newInfo.isReadonly : info.isReadonly && newInfo.isReadonly); return indexInfos; } } } return append(indexInfos, newInfo); } /** * Converts an AnonymousType to a ResolvedType. */ function resolveAnonymousTypeMembers(type: AnonymousType) { if (type.target) { setStructuredTypeMembers(type, emptySymbols, emptyArray, emptyArray, emptyArray); const members = createInstantiatedSymbolTable(getPropertiesOfObjectType(type.target), type.mapper!, /*mappingThisOnly*/ false); const callSignatures = instantiateSignatures(getSignaturesOfType(type.target, SignatureKind.Call), type.mapper!); const constructSignatures = instantiateSignatures(getSignaturesOfType(type.target, SignatureKind.Construct), type.mapper!); const indexInfos = instantiateIndexInfos(getIndexInfosOfType(type.target), type.mapper!); setStructuredTypeMembers(type, members, callSignatures, constructSignatures, indexInfos); return; } const symbol = getMergedSymbol(type.symbol); if (symbol.flags & SymbolFlags.TypeLiteral) { setStructuredTypeMembers(type, emptySymbols, emptyArray, emptyArray, emptyArray); const members = getMembersOfSymbol(symbol); const callSignatures = getSignaturesOfSymbol(members.get(InternalSymbolName.Call)); const constructSignatures = getSignaturesOfSymbol(members.get(InternalSymbolName.New)); const indexInfos = getIndexInfosOfSymbol(symbol); setStructuredTypeMembers(type, members, callSignatures, constructSignatures, indexInfos); return; } // Combinations of function, class, enum and module let members = getExportsOfSymbol(symbol); let indexInfos: IndexInfo[] | undefined; if (symbol === globalThisSymbol) { const varsOnly = new Map<__String, Symbol>(); members.forEach(p => { if (!(p.flags & SymbolFlags.BlockScoped) && !(p.flags & SymbolFlags.ValueModule && p.declarations?.length && every(p.declarations, isAmbientModule))) { varsOnly.set(p.escapedName, p); } }); members = varsOnly; } let baseConstructorIndexInfo: IndexInfo | undefined; setStructuredTypeMembers(type, members, emptyArray, emptyArray, emptyArray); if (symbol.flags & SymbolFlags.Class) { const classType = getDeclaredTypeOfClassOrInterface(symbol); const baseConstructorType = getBaseConstructorTypeOfClass(classType); if (baseConstructorType.flags & (TypeFlags.Object | TypeFlags.Intersection | TypeFlags.TypeVariable)) { members = createSymbolTable(getNamedOrIndexSignatureMembers(members)); addInheritedMembers(members, getPropertiesOfType(baseConstructorType)); } else if (baseConstructorType === anyType) { baseConstructorIndexInfo = createIndexInfo(stringType, anyType, /*isReadonly*/ false); } } const indexSymbol = getIndexSymbolFromSymbolTable(members); if (indexSymbol) { indexInfos = getIndexInfosOfIndexSymbol(indexSymbol); } else { if (baseConstructorIndexInfo) { indexInfos = append(indexInfos, baseConstructorIndexInfo); } if ( symbol.flags & SymbolFlags.Enum && (getDeclaredTypeOfSymbol(symbol).flags & TypeFlags.Enum || some(type.properties, prop => !!(getTypeOfSymbol(prop).flags & TypeFlags.NumberLike))) ) { indexInfos = append(indexInfos, enumNumberIndexInfo); } } setStructuredTypeMembers(type, members, emptyArray, emptyArray, indexInfos || emptyArray); // We resolve the members before computing the signatures because a signature may use // typeof with a qualified name expression that circularly references the type we are // in the process of resolving (see issue #6072). The temporarily empty signature list // will never be observed because a qualified name can't reference signatures. if (symbol.flags & (SymbolFlags.Function | SymbolFlags.Method)) { type.callSignatures = getSignaturesOfSymbol(symbol); } // And likewise for construct signatures for classes if (symbol.flags & SymbolFlags.Class) { const classType = getDeclaredTypeOfClassOrInterface(symbol); let constructSignatures = symbol.members ? getSignaturesOfSymbol(symbol.members.get(InternalSymbolName.Constructor)) : emptyArray; if (symbol.flags & SymbolFlags.Function) { constructSignatures = addRange( constructSignatures.slice(), mapDefined( type.callSignatures, sig => isJSConstructor(sig.declaration) ? createSignature(sig.declaration, sig.typeParameters, sig.thisParameter, sig.parameters, classType, /*resolvedTypePredicate*/ undefined, sig.minArgumentCount, sig.flags & SignatureFlags.PropagatingFlags) : undefined, ), ); } if (!constructSignatures.length) { constructSignatures = getDefaultConstructSignatures(classType); } type.constructSignatures = constructSignatures; } } type ReplaceableIndexedAccessType = IndexedAccessType & { objectType: TypeParameter; indexType: TypeParameter; }; function replaceIndexedAccess(instantiable: Type, type: ReplaceableIndexedAccessType, replacement: Type) { // map type.indexType to 0 // map type.objectType to `[TReplacement]` // thus making the indexed access `[TReplacement][0]` or `TReplacement` return instantiateType(instantiable, createTypeMapper([type.indexType, type.objectType], [getNumberLiteralType(0), createTupleType([replacement])])); } // If the original mapped type had an intersection constraint we extract its components, // and we make an attempt to do so even if the intersection has been reduced to a union. // This entire process allows us to possibly retrieve the filtering type literals. // e.g. { [K in keyof U & ("a" | "b") ] } -> "a" | "b" function getLimitedConstraint(type: ReverseMappedType) { const constraint = getConstraintTypeFromMappedType(type.mappedType); if (!(constraint.flags & TypeFlags.Union || constraint.flags & TypeFlags.Intersection)) { return; } const origin = (constraint.flags & TypeFlags.Union) ? (constraint as UnionType).origin : (constraint as IntersectionType); if (!origin || !(origin.flags & TypeFlags.Intersection)) { return; } const limitedConstraint = getIntersectionType((origin as IntersectionType).types.filter(t => t !== type.constraintType)); return limitedConstraint !== neverType ? limitedConstraint : undefined; } function resolveReverseMappedTypeMembers(type: ReverseMappedType) { const indexInfo = getIndexInfoOfType(type.source, stringType); const modifiers = getMappedTypeModifiers(type.mappedType); const readonlyMask = modifiers & MappedTypeModifiers.IncludeReadonly ? false : true; const optionalMask = modifiers & MappedTypeModifiers.IncludeOptional ? 0 : SymbolFlags.Optional; const indexInfos = indexInfo ? [createIndexInfo(stringType, inferReverseMappedType(indexInfo.type, type.mappedType, type.constraintType) || unknownType, readonlyMask && indexInfo.isReadonly)] : emptyArray; const members = createSymbolTable(); const limitedConstraint = getLimitedConstraint(type); for (const prop of getPropertiesOfType(type.source)) { // In case of a reverse mapped type with an intersection constraint, if we were able to // extract the filtering type literals we skip those properties that are not assignable to them, // because the extra properties wouldn't get through the application of the mapped type anyway if (limitedConstraint) { const propertyNameType = getLiteralTypeFromProperty(prop, TypeFlags.StringOrNumberLiteralOrUnique); if (!isTypeAssignableTo(propertyNameType, limitedConstraint)) { continue; } } const checkFlags = CheckFlags.ReverseMapped | (readonlyMask && isReadonlySymbol(prop) ? CheckFlags.Readonly : 0); const inferredProp = createSymbol(SymbolFlags.Property | prop.flags & optionalMask, prop.escapedName, checkFlags) as ReverseMappedSymbol; inferredProp.declarations = prop.declarations; inferredProp.links.nameType = getSymbolLinks(prop).nameType; inferredProp.links.propertyType = getTypeOfSymbol(prop); if ( type.constraintType.type.flags & TypeFlags.IndexedAccess && (type.constraintType.type as IndexedAccessType).objectType.flags & TypeFlags.TypeParameter && (type.constraintType.type as IndexedAccessType).indexType.flags & TypeFlags.TypeParameter ) { // A reverse mapping of `{[K in keyof T[K_1]]: T[K_1]}` is the same as that of `{[K in keyof T]: T}`, since all we care about is // inferring to the "type parameter" (or indexed access) shared by the constraint and template. So, to reduce the number of // type identities produced, we simplify such indexed access occurences const newTypeParam = (type.constraintType.type as IndexedAccessType).objectType; const newMappedType = replaceIndexedAccess(type.mappedType, type.constraintType.type as ReplaceableIndexedAccessType, newTypeParam); inferredProp.links.mappedType = newMappedType as MappedType; inferredProp.links.constraintType = getIndexType(newTypeParam) as IndexType; } else { inferredProp.links.mappedType = type.mappedType; inferredProp.links.constraintType = type.constraintType; } members.set(prop.escapedName, inferredProp); } setStructuredTypeMembers(type, members, emptyArray, emptyArray, indexInfos); } // Return the lower bound of the key type in a mapped type. Intuitively, the lower // bound includes those keys that are known to always be present, for example because // because of constraints on type parameters (e.g. 'keyof T' for a constrained T). function getLowerBoundOfKeyType(type: Type): Type { if (type.flags & TypeFlags.Index) { const t = getApparentType((type as IndexType).type); return isGenericTupleType(t) ? getKnownKeysOfTupleType(t) : getIndexType(t); } if (type.flags & TypeFlags.Conditional) { if ((type as ConditionalType).root.isDistributive) { const checkType = (type as ConditionalType).checkType; const constraint = getLowerBoundOfKeyType(checkType); if (constraint !== checkType) { return getConditionalTypeInstantiation(type as ConditionalType, prependTypeMapping((type as ConditionalType).root.checkType, constraint, (type as ConditionalType).mapper), /*forConstraint*/ false); } } return type; } if (type.flags & TypeFlags.Union) { return mapType(type as UnionType, getLowerBoundOfKeyType, /*noReductions*/ true); } if (type.flags & TypeFlags.Intersection) { // Similarly to getTypeFromIntersectionTypeNode, we preserve the special string & {}, number & {}, // and bigint & {} intersections that are used to prevent subtype reduction in union types. const types = (type as IntersectionType).types; if (types.length === 2 && !!(types[0].flags & (TypeFlags.String | TypeFlags.Number | TypeFlags.BigInt)) && types[1] === emptyTypeLiteralType) { return type; } return getIntersectionType(sameMap((type as UnionType).types, getLowerBoundOfKeyType)); } return type; } function getIsLateCheckFlag(s: Symbol): CheckFlags { return getCheckFlags(s) & CheckFlags.Late; } function forEachMappedTypePropertyKeyTypeAndIndexSignatureKeyType(type: Type, include: TypeFlags, stringsOnly: boolean, cb: (keyType: Type) => void) { for (const prop of getPropertiesOfType(type)) { cb(getLiteralTypeFromProperty(prop, include)); } if (type.flags & TypeFlags.Any) { cb(stringType); } else { for (const info of getIndexInfosOfType(type)) { if (!stringsOnly || info.keyType.flags & (TypeFlags.String | TypeFlags.TemplateLiteral)) { cb(info.keyType); } } } } /** Resolve the members of a mapped type { [P in K]: T } */ function resolveMappedTypeMembers(type: MappedType) { const members: SymbolTable = createSymbolTable(); let indexInfos: IndexInfo[] | undefined; // Resolve upfront such that recursive references see an empty object type. setStructuredTypeMembers(type, emptySymbols, emptyArray, emptyArray, emptyArray); // In { [P in K]: T }, we refer to P as the type parameter type, K as the constraint type, // and T as the template type. const typeParameter = getTypeParameterFromMappedType(type); const constraintType = getConstraintTypeFromMappedType(type); const mappedType = (type.target as MappedType) || type; const nameType = getNameTypeFromMappedType(mappedType); const shouldLinkPropDeclarations = getMappedTypeNameTypeKind(mappedType) !== MappedTypeNameTypeKind.Remapping; const templateType = getTemplateTypeFromMappedType(mappedType); const modifiersType = getApparentType(getModifiersTypeFromMappedType(type)); // The 'T' in 'keyof T' const templateModifiers = getMappedTypeModifiers(type); const include = TypeFlags.StringOrNumberLiteralOrUnique; if (isMappedTypeWithKeyofConstraintDeclaration(type)) { // We have a { [P in keyof T]: X } forEachMappedTypePropertyKeyTypeAndIndexSignatureKeyType(modifiersType, include, /*stringsOnly*/ false, addMemberForKeyType); } else { forEachType(getLowerBoundOfKeyType(constraintType), addMemberForKeyType); } setStructuredTypeMembers(type, members, emptyArray, emptyArray, indexInfos || emptyArray); function addMemberForKeyType(keyType: Type) { const propNameType = nameType ? instantiateType(nameType, appendTypeMapping(type.mapper, typeParameter, keyType)) : keyType; forEachType(propNameType, t => addMemberForKeyTypeWorker(keyType, t)); } function addMemberForKeyTypeWorker(keyType: Type, propNameType: Type) { // If the current iteration type constituent is a string literal type, create a property. // Otherwise, for type string create a string index signature. if (isTypeUsableAsPropertyName(propNameType)) { const propName = getPropertyNameFromType(propNameType); // String enum members from separate enums with identical values // are distinct types with the same property name. Make the resulting // property symbol's name type be the union of those enum member types. const existingProp = members.get(propName) as MappedSymbol | undefined; if (existingProp) { existingProp.links.nameType = getUnionType([existingProp.links.nameType!, propNameType]); existingProp.links.keyType = getUnionType([existingProp.links.keyType, keyType]); } else { const modifiersProp = isTypeUsableAsPropertyName(keyType) ? getPropertyOfType(modifiersType, getPropertyNameFromType(keyType)) : undefined; const isOptional = !!(templateModifiers & MappedTypeModifiers.IncludeOptional || !(templateModifiers & MappedTypeModifiers.ExcludeOptional) && modifiersProp && modifiersProp.flags & SymbolFlags.Optional); const isReadonly = !!(templateModifiers & MappedTypeModifiers.IncludeReadonly || !(templateModifiers & MappedTypeModifiers.ExcludeReadonly) && modifiersProp && isReadonlySymbol(modifiersProp)); const stripOptional = strictNullChecks && !isOptional && modifiersProp && modifiersProp.flags & SymbolFlags.Optional; const lateFlag: CheckFlags = modifiersProp ? getIsLateCheckFlag(modifiersProp) : 0; const prop = createSymbol(SymbolFlags.Property | (isOptional ? SymbolFlags.Optional : 0), propName, lateFlag | CheckFlags.Mapped | (isReadonly ? CheckFlags.Readonly : 0) | (stripOptional ? CheckFlags.StripOptional : 0)) as MappedSymbol; prop.links.mappedType = type; prop.links.nameType = propNameType; prop.links.keyType = keyType; if (modifiersProp) { prop.links.syntheticOrigin = modifiersProp; prop.declarations = shouldLinkPropDeclarations ? modifiersProp.declarations : undefined; } members.set(propName, prop); } } else if (isValidIndexKeyType(propNameType) || propNameType.flags & (TypeFlags.Any | TypeFlags.Enum)) { const indexKeyType = propNameType.flags & (TypeFlags.Any | TypeFlags.String) ? stringType : propNameType.flags & (TypeFlags.Number | TypeFlags.Enum) ? numberType : propNameType; const propType = instantiateType(templateType, appendTypeMapping(type.mapper, typeParameter, keyType)); const modifiersIndexInfo = getApplicableIndexInfo(modifiersType, propNameType); const isReadonly = !!(templateModifiers & MappedTypeModifiers.IncludeReadonly || !(templateModifiers & MappedTypeModifiers.ExcludeReadonly) && modifiersIndexInfo?.isReadonly); const indexInfo = createIndexInfo(indexKeyType, propType, isReadonly); indexInfos = appendIndexInfo(indexInfos, indexInfo, /*union*/ true); } } } function getTypeOfMappedSymbol(symbol: MappedSymbol) { if (!symbol.links.type) { const mappedType = symbol.links.mappedType; if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) { mappedType.containsError = true; return errorType; } const templateType = getTemplateTypeFromMappedType(mappedType.target as MappedType || mappedType); const mapper = appendTypeMapping(mappedType.mapper, getTypeParameterFromMappedType(mappedType), symbol.links.keyType); const propType = instantiateType(templateType, mapper); // When creating an optional property in strictNullChecks mode, if 'undefined' isn't assignable to the // type, we include 'undefined' in the type. Similarly, when creating a non-optional property in strictNullChecks // mode, if the underlying property is optional we remove 'undefined' from the type. let type = strictNullChecks && symbol.flags & SymbolFlags.Optional && !maybeTypeOfKind(propType, TypeFlags.Undefined | TypeFlags.Void) ? getOptionalType(propType, /*isProperty*/ true) : symbol.links.checkFlags & CheckFlags.StripOptional ? removeMissingOrUndefinedType(propType) : propType; if (!popTypeResolution()) { error(currentNode, Diagnostics.Type_of_property_0_circularly_references_itself_in_mapped_type_1, symbolToString(symbol), typeToString(mappedType)); type = errorType; } symbol.links.type ??= type; } return symbol.links.type; } function getTypeParameterFromMappedType(type: MappedType) { return type.typeParameter || (type.typeParameter = getDeclaredTypeOfTypeParameter(getSymbolOfDeclaration(type.declaration.typeParameter))); } function getConstraintTypeFromMappedType(type: MappedType) { return type.constraintType || (type.constraintType = getConstraintOfTypeParameter(getTypeParameterFromMappedType(type)) || errorType); } function getNameTypeFromMappedType(type: MappedType) { return type.declaration.nameType ? type.nameType || (type.nameType = instantiateType(getTypeFromTypeNode(type.declaration.nameType), type.mapper)) : undefined; } function getTemplateTypeFromMappedType(type: MappedType) { return type.templateType || (type.templateType = type.declaration.type ? instantiateType(addOptionality(getTypeFromTypeNode(type.declaration.type), /*isProperty*/ true, !!(getMappedTypeModifiers(type) & MappedTypeModifiers.IncludeOptional)), type.mapper) : errorType); } function getConstraintDeclarationForMappedType(type: MappedType) { return getEffectiveConstraintOfTypeParameter(type.declaration.typeParameter); } function isMappedTypeWithKeyofConstraintDeclaration(type: MappedType) { const constraintDeclaration = getConstraintDeclarationForMappedType(type)!; // TODO: GH#18217 return constraintDeclaration.kind === SyntaxKind.TypeOperator && (constraintDeclaration as TypeOperatorNode).operator === SyntaxKind.KeyOfKeyword; } function getModifiersTypeFromMappedType(type: MappedType) { if (!type.modifiersType) { if (isMappedTypeWithKeyofConstraintDeclaration(type)) { // If the constraint declaration is a 'keyof T' node, the modifiers type is T. We check // AST nodes here because, when T is a non-generic type, the logic below eagerly resolves // 'keyof T' to a literal union type and we can't recover T from that type. type.modifiersType = instantiateType(getTypeFromTypeNode((getConstraintDeclarationForMappedType(type) as TypeOperatorNode).type), type.mapper); } else { // Otherwise, get the declared constraint type, and if the constraint type is a type parameter, // get the constraint of that type parameter. If the resulting type is an indexed type 'keyof T', // the modifiers type is T. Otherwise, the modifiers type is unknown. const declaredType = getTypeFromMappedTypeNode(type.declaration) as MappedType; const constraint = getConstraintTypeFromMappedType(declaredType); const extendedConstraint = constraint && constraint.flags & TypeFlags.TypeParameter ? getConstraintOfTypeParameter(constraint as TypeParameter) : constraint; type.modifiersType = extendedConstraint && extendedConstraint.flags & TypeFlags.Index ? instantiateType((extendedConstraint as IndexType).type, type.mapper) : unknownType; } } return type.modifiersType; } function getMappedTypeModifiers(type: MappedType): MappedTypeModifiers { const declaration = type.declaration; return (declaration.readonlyToken ? declaration.readonlyToken.kind === SyntaxKind.MinusToken ? MappedTypeModifiers.ExcludeReadonly : MappedTypeModifiers.IncludeReadonly : 0) | (declaration.questionToken ? declaration.questionToken.kind === SyntaxKind.MinusToken ? MappedTypeModifiers.ExcludeOptional : MappedTypeModifiers.IncludeOptional : 0); } // Return -1, 0, or 1, where -1 means optionality is stripped (i.e. -?), 0 means optionality is unchanged, and 1 means // optionality is added (i.e. +?). function getMappedTypeOptionality(type: MappedType): number { const modifiers = getMappedTypeModifiers(type); return modifiers & MappedTypeModifiers.ExcludeOptional ? -1 : modifiers & MappedTypeModifiers.IncludeOptional ? 1 : 0; } // Return -1, 0, or 1, for stripped, unchanged, or added optionality respectively. When a homomorphic mapped type doesn't // modify optionality, recursively consult the optionality of the type being mapped over to see if it strips or adds optionality. // For intersections, return -1 or 1 when all constituents strip or add optionality, otherwise return 0. function getCombinedMappedTypeOptionality(type: Type): number { if (getObjectFlags(type) & ObjectFlags.Mapped) { return getMappedTypeOptionality(type as MappedType) || getCombinedMappedTypeOptionality(getModifiersTypeFromMappedType(type as MappedType)); } if (type.flags & TypeFlags.Intersection) { const optionality = getCombinedMappedTypeOptionality((type as IntersectionType).types[0]); return every((type as IntersectionType).types, (t, i) => i === 0 || getCombinedMappedTypeOptionality(t) === optionality) ? optionality : 0; } return 0; } function isPartialMappedType(type: Type) { return !!(getObjectFlags(type) & ObjectFlags.Mapped && getMappedTypeModifiers(type as MappedType) & MappedTypeModifiers.IncludeOptional); } function isGenericMappedType(type: Type): type is MappedType { if (getObjectFlags(type) & ObjectFlags.Mapped) { const constraint = getConstraintTypeFromMappedType(type as MappedType); if (isGenericIndexType(constraint)) { return true; } // A mapped type is generic if the 'as' clause references generic types other than the iteration type. // To determine this, we substitute the constraint type (that we now know isn't generic) for the iteration // type and check whether the resulting type is generic. const nameType = getNameTypeFromMappedType(type as MappedType); if (nameType && isGenericIndexType(instantiateType(nameType, makeUnaryTypeMapper(getTypeParameterFromMappedType(type as MappedType), constraint)))) { return true; } } return false; } function getMappedTypeNameTypeKind(type: MappedType): MappedTypeNameTypeKind { const nameType = getNameTypeFromMappedType(type); if (!nameType) { return MappedTypeNameTypeKind.None; } return isTypeAssignableTo(nameType, getTypeParameterFromMappedType(type)) ? MappedTypeNameTypeKind.Filtering : MappedTypeNameTypeKind.Remapping; } function resolveStructuredTypeMembers(type: StructuredType): ResolvedType { if (!(type as ResolvedType).members) { if (type.flags & TypeFlags.Object) { if ((type as ObjectType).objectFlags & ObjectFlags.Reference) { resolveTypeReferenceMembers(type as TypeReference); } else if ((type as ObjectType).objectFlags & ObjectFlags.ClassOrInterface) { resolveClassOrInterfaceMembers(type as InterfaceType); } else if ((type as ReverseMappedType).objectFlags & ObjectFlags.ReverseMapped) { resolveReverseMappedTypeMembers(type as ReverseMappedType); } else if ((type as ObjectType).objectFlags & ObjectFlags.Anonymous) { resolveAnonymousTypeMembers(type as AnonymousType); } else if ((type as MappedType).objectFlags & ObjectFlags.Mapped) { resolveMappedTypeMembers(type as MappedType); } else { Debug.fail("Unhandled object type " + Debug.formatObjectFlags(type.objectFlags)); } } else if (type.flags & TypeFlags.Union) { resolveUnionTypeMembers(type as UnionType); } else if (type.flags & TypeFlags.Intersection) { resolveIntersectionTypeMembers(type as IntersectionType); } else { Debug.fail("Unhandled type " + Debug.formatTypeFlags(type.flags)); } } return type as ResolvedType; } /** Return properties of an object type or an empty array for other types */ function getPropertiesOfObjectType(type: Type): Symbol[] { if (type.flags & TypeFlags.Object) { return resolveStructuredTypeMembers(type as ObjectType).properties; } return emptyArray; } /** If the given type is an object type and that type has a property by the given name, * return the symbol for that property. Otherwise return undefined. */ function getPropertyOfObjectType(type: Type, name: __String): Symbol | undefined { if (type.flags & TypeFlags.Object) { const resolved = resolveStructuredTypeMembers(type as ObjectType); const symbol = resolved.members.get(name); if (symbol && symbolIsValue(symbol)) { return symbol; } } } function getPropertiesOfUnionOrIntersectionType(type: UnionOrIntersectionType): Symbol[] { if (!type.resolvedProperties) { const members = createSymbolTable(); for (const current of type.types) { for (const prop of getPropertiesOfType(current)) { if (!members.has(prop.escapedName)) { const combinedProp = getPropertyOfUnionOrIntersectionType(type, prop.escapedName, /*skipObjectFunctionPropertyAugment*/ !!(type.flags & TypeFlags.Intersection)); if (combinedProp) { members.set(prop.escapedName, combinedProp); } } } // The properties of a union type are those that are present in all constituent types, so // we only need to check the properties of the first type without index signature if (type.flags & TypeFlags.Union && getIndexInfosOfType(current).length === 0) { break; } } type.resolvedProperties = getNamedMembers(members); } return type.resolvedProperties; } function getPropertiesOfType(type: Type): Symbol[] { type = getReducedApparentType(type); return type.flags & TypeFlags.UnionOrIntersection ? getPropertiesOfUnionOrIntersectionType(type as UnionType) : getPropertiesOfObjectType(type); } function forEachPropertyOfType(type: Type, action: (symbol: Symbol, escapedName: __String) => void): void { type = getReducedApparentType(type); if (type.flags & TypeFlags.StructuredType) { resolveStructuredTypeMembers(type as StructuredType).members.forEach((symbol, escapedName) => { if (isNamedMember(symbol, escapedName)) { action(symbol, escapedName); } }); } } function isTypeInvalidDueToUnionDiscriminant(contextualType: Type, obj: ObjectLiteralExpression | JsxAttributes): boolean { const list = obj.properties as NodeArray; return list.some(property => { const nameType = property.name && (isJsxNamespacedName(property.name) ? getStringLiteralType(getTextOfJsxAttributeName(property.name)) : getLiteralTypeFromPropertyName(property.name)); const name = nameType && isTypeUsableAsPropertyName(nameType) ? getPropertyNameFromType(nameType) : undefined; const expected = name === undefined ? undefined : getTypeOfPropertyOfType(contextualType, name); return !!expected && isLiteralType(expected) && !isTypeAssignableTo(getTypeOfNode(property), expected); }); } function getAllPossiblePropertiesOfTypes(types: readonly Type[]): Symbol[] { const unionType = getUnionType(types); if (!(unionType.flags & TypeFlags.Union)) { return getAugmentedPropertiesOfType(unionType); } const props = createSymbolTable(); for (const memberType of types) { for (const { escapedName } of getAugmentedPropertiesOfType(memberType)) { if (!props.has(escapedName)) { const prop = createUnionOrIntersectionProperty(unionType as UnionType, escapedName); // May be undefined if the property is private if (prop) props.set(escapedName, prop); } } } return arrayFrom(props.values()); } function getConstraintOfType(type: InstantiableType | UnionOrIntersectionType): Type | undefined { return type.flags & TypeFlags.TypeParameter ? getConstraintOfTypeParameter(type as TypeParameter) : type.flags & TypeFlags.IndexedAccess ? getConstraintOfIndexedAccess(type as IndexedAccessType) : type.flags & TypeFlags.Conditional ? getConstraintOfConditionalType(type as ConditionalType) : getBaseConstraintOfType(type); } function getConstraintOfTypeParameter(typeParameter: TypeParameter): Type | undefined { return hasNonCircularBaseConstraint(typeParameter) ? getConstraintFromTypeParameter(typeParameter) : undefined; } function isConstMappedType(type: MappedType, depth: number): boolean { const typeVariable = getHomomorphicTypeVariable(type); return !!typeVariable && isConstTypeVariable(typeVariable, depth); } function isConstTypeVariable(type: Type | undefined, depth = 0): boolean { return depth < 5 && !!(type && ( type.flags & TypeFlags.TypeParameter && some((type as TypeParameter).symbol?.declarations, d => hasSyntacticModifier(d, ModifierFlags.Const)) || type.flags & TypeFlags.UnionOrIntersection && some((type as UnionOrIntersectionType).types, t => isConstTypeVariable(t, depth)) || type.flags & TypeFlags.IndexedAccess && isConstTypeVariable((type as IndexedAccessType).objectType, depth + 1) || type.flags & TypeFlags.Conditional && isConstTypeVariable(getConstraintOfConditionalType(type as ConditionalType), depth + 1) || type.flags & TypeFlags.Substitution && isConstTypeVariable((type as SubstitutionType).baseType, depth) || getObjectFlags(type) & ObjectFlags.Mapped && isConstMappedType(type as MappedType, depth) || isGenericTupleType(type) && findIndex(getElementTypes(type), (t, i) => !!(type.target.elementFlags[i] & ElementFlags.Variadic) && isConstTypeVariable(t, depth)) >= 0 )); } function getConstraintOfIndexedAccess(type: IndexedAccessType) { return hasNonCircularBaseConstraint(type) ? getConstraintFromIndexedAccess(type) : undefined; } function getSimplifiedTypeOrConstraint(type: Type) { const simplified = getSimplifiedType(type, /*writing*/ false); return simplified !== type ? simplified : getConstraintOfType(type); } function getConstraintFromIndexedAccess(type: IndexedAccessType) { if (isMappedTypeGenericIndexedAccess(type)) { // For indexed access types of the form { [P in K]: E }[X], where K is non-generic and X is generic, // we substitute an instantiation of E where P is replaced with X. return substituteIndexedMappedType(type.objectType as MappedType, type.indexType); } const indexConstraint = getSimplifiedTypeOrConstraint(type.indexType); if (indexConstraint && indexConstraint !== type.indexType) { const indexedAccess = getIndexedAccessTypeOrUndefined(type.objectType, indexConstraint, type.accessFlags); if (indexedAccess) { return indexedAccess; } } const objectConstraint = getSimplifiedTypeOrConstraint(type.objectType); if (objectConstraint && objectConstraint !== type.objectType) { return getIndexedAccessTypeOrUndefined(objectConstraint, type.indexType, type.accessFlags); } return undefined; } function getDefaultConstraintOfConditionalType(type: ConditionalType) { if (!type.resolvedDefaultConstraint) { // An `any` branch of a conditional type would normally be viral - specifically, without special handling here, // a conditional type with a single branch of type `any` would be assignable to anything, since it's constraint would simplify to // just `any`. This result is _usually_ unwanted - so instead here we elide an `any` branch from the constraint type, // in effect treating `any` like `never` rather than `unknown` in this location. const trueConstraint = getInferredTrueTypeFromConditionalType(type); const falseConstraint = getFalseTypeFromConditionalType(type); type.resolvedDefaultConstraint = isTypeAny(trueConstraint) ? falseConstraint : isTypeAny(falseConstraint) ? trueConstraint : getUnionType([trueConstraint, falseConstraint]); } return type.resolvedDefaultConstraint; } function getConstraintOfDistributiveConditionalType(type: ConditionalType): Type | undefined { if (type.resolvedConstraintOfDistributive !== undefined) { return type.resolvedConstraintOfDistributive || undefined; } // Check if we have a conditional type of the form 'T extends U ? X : Y', where T is a constrained // type parameter. If so, create an instantiation of the conditional type where T is replaced // with its constraint. We do this because if the constraint is a union type it will be distributed // over the conditional type and possibly reduced. For example, 'T extends undefined ? never : T' // removes 'undefined' from T. // We skip returning a distributive constraint for a restrictive instantiation of a conditional type // as the constraint for all type params (check type included) have been replace with `unknown`, which // is going to produce even more false positive/negative results than the distribute constraint already does. // Please note: the distributive constraint is a kludge for emulating what a negated type could to do filter // a union - once negated types exist and are applied to the conditional false branch, this "constraint" // likely doesn't need to exist. if (type.root.isDistributive && type.restrictiveInstantiation !== type) { const simplified = getSimplifiedType(type.checkType, /*writing*/ false); const constraint = simplified === type.checkType ? getConstraintOfType(simplified) : simplified; if (constraint && constraint !== type.checkType) { const instantiated = getConditionalTypeInstantiation(type, prependTypeMapping(type.root.checkType, constraint, type.mapper), /*forConstraint*/ true); if (!(instantiated.flags & TypeFlags.Never)) { type.resolvedConstraintOfDistributive = instantiated; return instantiated; } } } type.resolvedConstraintOfDistributive = false; return undefined; } function getConstraintFromConditionalType(type: ConditionalType) { return getConstraintOfDistributiveConditionalType(type) || getDefaultConstraintOfConditionalType(type); } function getConstraintOfConditionalType(type: ConditionalType) { return hasNonCircularBaseConstraint(type) ? getConstraintFromConditionalType(type) : undefined; } function getEffectiveConstraintOfIntersection(types: readonly Type[], targetIsUnion: boolean) { let constraints: Type[] | undefined; let hasDisjointDomainType = false; for (const t of types) { if (t.flags & TypeFlags.Instantiable) { // We keep following constraints as long as we have an instantiable type that is known // not to be circular or infinite (hence we stop on index access types). let constraint = getConstraintOfType(t); while (constraint && constraint.flags & (TypeFlags.TypeParameter | TypeFlags.Index | TypeFlags.Conditional)) { constraint = getConstraintOfType(constraint); } if (constraint) { constraints = append(constraints, constraint); if (targetIsUnion) { constraints = append(constraints, t); } } } else if (t.flags & TypeFlags.DisjointDomains || isEmptyAnonymousObjectType(t)) { hasDisjointDomainType = true; } } // If the target is a union type or if we are intersecting with types belonging to one of the // disjoint domains, we may end up producing a constraint that hasn't been examined before. if (constraints && (targetIsUnion || hasDisjointDomainType)) { if (hasDisjointDomainType) { // We add any types belong to one of the disjoint domains because they might cause the final // intersection operation to reduce the union constraints. for (const t of types) { if (t.flags & TypeFlags.DisjointDomains || isEmptyAnonymousObjectType(t)) { constraints = append(constraints, t); } } } // The source types were normalized; ensure the result is normalized too. return getNormalizedType(getIntersectionType(constraints, IntersectionFlags.NoConstraintReduction), /*writing*/ false); } return undefined; } function getBaseConstraintOfType(type: Type): Type | undefined { if (type.flags & (TypeFlags.InstantiableNonPrimitive | TypeFlags.UnionOrIntersection | TypeFlags.TemplateLiteral | TypeFlags.StringMapping) || isGenericTupleType(type)) { const constraint = getResolvedBaseConstraint(type as InstantiableType | UnionOrIntersectionType); return constraint !== noConstraintType && constraint !== circularConstraintType ? constraint : undefined; } return type.flags & TypeFlags.Index ? stringNumberSymbolType : undefined; } /** * This is similar to `getBaseConstraintOfType` except it returns the input type if there's no base constraint, instead of `undefined` * It also doesn't map indexes to `string`, as where this is used this would be unneeded (and likely undesirable) */ function getBaseConstraintOrType(type: Type) { return getBaseConstraintOfType(type) || type; } function hasNonCircularBaseConstraint(type: InstantiableType): boolean { return getResolvedBaseConstraint(type) !== circularConstraintType; } /** * Return the resolved base constraint of a type variable. The noConstraintType singleton is returned if the * type variable has no constraint, and the circularConstraintType singleton is returned if the constraint * circularly references the type variable. */ function getResolvedBaseConstraint(type: InstantiableType | UnionOrIntersectionType): Type { if (type.resolvedBaseConstraint) { return type.resolvedBaseConstraint; } const stack: object[] = []; return type.resolvedBaseConstraint = getImmediateBaseConstraint(type); function getImmediateBaseConstraint(t: Type): Type { if (!t.immediateBaseConstraint) { if (!pushTypeResolution(t, TypeSystemPropertyName.ImmediateBaseConstraint)) { return circularConstraintType; } let result; // We always explore at least 10 levels of nested constraints. Thereafter, we continue to explore // up to 50 levels of nested constraints provided there are no "deeply nested" types on the stack // (i.e. no types for which five instantiations have been recorded on the stack). If we reach 50 // levels of nesting, we are presumably exploring a repeating pattern with a long cycle that hasn't // yet triggered the deeply nested limiter. We have no test cases that actually get to 50 levels of // nesting, so it is effectively just a safety stop. const identity = getRecursionIdentity(t); if (stack.length < 10 || stack.length < 50 && !contains(stack, identity)) { stack.push(identity); result = computeBaseConstraint(getSimplifiedType(t, /*writing*/ false)); stack.pop(); } if (!popTypeResolution()) { if (t.flags & TypeFlags.TypeParameter) { const errorNode = getConstraintDeclaration(t as TypeParameter); if (errorNode) { const diagnostic = error(errorNode, Diagnostics.Type_parameter_0_has_a_circular_constraint, typeToString(t)); if (currentNode && !isNodeDescendantOf(errorNode, currentNode) && !isNodeDescendantOf(currentNode, errorNode)) { addRelatedInfo(diagnostic, createDiagnosticForNode(currentNode, Diagnostics.Circularity_originates_in_type_at_this_location)); } } } result = circularConstraintType; } t.immediateBaseConstraint ??= result || noConstraintType; } return t.immediateBaseConstraint; } function getBaseConstraint(t: Type): Type | undefined { const c = getImmediateBaseConstraint(t); return c !== noConstraintType && c !== circularConstraintType ? c : undefined; } function computeBaseConstraint(t: Type): Type | undefined { if (t.flags & TypeFlags.TypeParameter) { const constraint = getConstraintFromTypeParameter(t as TypeParameter); return (t as TypeParameter).isThisType || !constraint ? constraint : getBaseConstraint(constraint); } if (t.flags & TypeFlags.UnionOrIntersection) { const types = (t as UnionOrIntersectionType).types; const baseTypes: Type[] = []; let different = false; for (const type of types) { const baseType = getBaseConstraint(type); if (baseType) { if (baseType !== type) { different = true; } baseTypes.push(baseType); } else { different = true; } } if (!different) { return t; } return t.flags & TypeFlags.Union && baseTypes.length === types.length ? getUnionType(baseTypes) : t.flags & TypeFlags.Intersection && baseTypes.length ? getIntersectionType(baseTypes) : undefined; } if (t.flags & TypeFlags.Index) { return stringNumberSymbolType; } if (t.flags & TypeFlags.TemplateLiteral) { const types = (t as TemplateLiteralType).types; const constraints = mapDefined(types, getBaseConstraint); return constraints.length === types.length ? getTemplateLiteralType((t as TemplateLiteralType).texts, constraints) : stringType; } if (t.flags & TypeFlags.StringMapping) { const constraint = getBaseConstraint((t as StringMappingType).type); return constraint && constraint !== (t as StringMappingType).type ? getStringMappingType((t as StringMappingType).symbol, constraint) : stringType; } if (t.flags & TypeFlags.IndexedAccess) { if (isMappedTypeGenericIndexedAccess(t)) { // For indexed access types of the form { [P in K]: E }[X], where K is non-generic and X is generic, // we substitute an instantiation of E where P is replaced with X. return getBaseConstraint(substituteIndexedMappedType((t as IndexedAccessType).objectType as MappedType, (t as IndexedAccessType).indexType)); } const baseObjectType = getBaseConstraint((t as IndexedAccessType).objectType); const baseIndexType = getBaseConstraint((t as IndexedAccessType).indexType); const baseIndexedAccess = baseObjectType && baseIndexType && getIndexedAccessTypeOrUndefined(baseObjectType, baseIndexType, (t as IndexedAccessType).accessFlags); return baseIndexedAccess && getBaseConstraint(baseIndexedAccess); } if (t.flags & TypeFlags.Conditional) { const constraint = getConstraintFromConditionalType(t as ConditionalType); return constraint && getBaseConstraint(constraint); } if (t.flags & TypeFlags.Substitution) { return getBaseConstraint(getSubstitutionIntersection(t as SubstitutionType)); } if (isGenericTupleType(t)) { // We substitute constraints for variadic elements only when the constraints are array types or // non-variadic tuple types as we want to avoid further (possibly unbounded) recursion. const newElements = map(getElementTypes(t), (v, i) => { const constraint = v.flags & TypeFlags.TypeParameter && t.target.elementFlags[i] & ElementFlags.Variadic && getBaseConstraint(v) || v; return constraint !== v && everyType(constraint, c => isArrayOrTupleType(c) && !isGenericTupleType(c)) ? constraint : v; }); return createTupleType(newElements, t.target.elementFlags, t.target.readonly, t.target.labeledElementDeclarations); } return t; } } function getApparentTypeOfIntersectionType(type: IntersectionType, thisArgument: Type) { if (type === thisArgument) { return type.resolvedApparentType || (type.resolvedApparentType = getTypeWithThisArgument(type, thisArgument, /*needApparentType*/ true)); } const key = `I${getTypeId(type)},${getTypeId(thisArgument)}`; return getCachedType(key) ?? setCachedType(key, getTypeWithThisArgument(type, thisArgument, /*needApparentType*/ true)); } function getResolvedTypeParameterDefault(typeParameter: TypeParameter): Type | undefined { if (!typeParameter.default) { if (typeParameter.target) { const targetDefault = getResolvedTypeParameterDefault(typeParameter.target); typeParameter.default = targetDefault ? instantiateType(targetDefault, typeParameter.mapper) : noConstraintType; } else { // To block recursion, set the initial value to the resolvingDefaultType. typeParameter.default = resolvingDefaultType; const defaultDeclaration = typeParameter.symbol && forEach(typeParameter.symbol.declarations, decl => isTypeParameterDeclaration(decl) && decl.default); const defaultType = defaultDeclaration ? getTypeFromTypeNode(defaultDeclaration) : noConstraintType; if (typeParameter.default === resolvingDefaultType) { // If we have not been called recursively, set the correct default type. typeParameter.default = defaultType; } } } else if (typeParameter.default === resolvingDefaultType) { // If we are called recursively for this type parameter, mark the default as circular. typeParameter.default = circularConstraintType; } return typeParameter.default; } /** * Gets the default type for a type parameter. * * If the type parameter is the result of an instantiation, this gets the instantiated * default type of its target. If the type parameter has no default type or the default is * circular, `undefined` is returned. */ function getDefaultFromTypeParameter(typeParameter: TypeParameter): Type | undefined { const defaultType = getResolvedTypeParameterDefault(typeParameter); return defaultType !== noConstraintType && defaultType !== circularConstraintType ? defaultType : undefined; } function hasNonCircularTypeParameterDefault(typeParameter: TypeParameter) { return getResolvedTypeParameterDefault(typeParameter) !== circularConstraintType; } /** * Indicates whether the declaration of a typeParameter has a default type. */ function hasTypeParameterDefault(typeParameter: TypeParameter): boolean { return !!(typeParameter.symbol && forEach(typeParameter.symbol.declarations, decl => isTypeParameterDeclaration(decl) && decl.default)); } function getApparentTypeOfMappedType(type: MappedType) { return type.resolvedApparentType || (type.resolvedApparentType = getResolvedApparentTypeOfMappedType(type)); } function getResolvedApparentTypeOfMappedType(type: MappedType): Type { const target = (type.target ?? type) as MappedType; const typeVariable = getHomomorphicTypeVariable(target); if (typeVariable && !target.declaration.nameType) { // We have a homomorphic mapped type or an instantiation of a homomorphic mapped type, i.e. a type // of the form { [P in keyof T]: X }. Obtain the modifiers type (the T of the keyof T), and if it is // another generic mapped type, recursively obtain its apparent type. Otherwise, obtain its base // constraint. Then, if every constituent of the base constraint is an array or tuple type, apply // this mapped type to the base constraint. It is safe to recurse when the modifiers type is a // mapped type because we protect again circular constraints in getTypeFromMappedTypeNode. const modifiersType = getModifiersTypeFromMappedType(type); const baseConstraint = isGenericMappedType(modifiersType) ? getApparentTypeOfMappedType(modifiersType) : getBaseConstraintOfType(modifiersType); if (baseConstraint && everyType(baseConstraint, t => isArrayOrTupleType(t) || isArrayOrTupleOrIntersection(t))) { return instantiateType(target, prependTypeMapping(typeVariable, baseConstraint, type.mapper)); } } return type; } function isArrayOrTupleOrIntersection(type: Type) { return !!(type.flags & TypeFlags.Intersection) && every((type as IntersectionType).types, isArrayOrTupleType); } function isMappedTypeGenericIndexedAccess(type: Type) { let objectType; return !!(type.flags & TypeFlags.IndexedAccess && getObjectFlags(objectType = (type as IndexedAccessType).objectType) & ObjectFlags.Mapped && !isGenericMappedType(objectType) && isGenericIndexType((type as IndexedAccessType).indexType) && !(getMappedTypeModifiers(objectType as MappedType) & MappedTypeModifiers.ExcludeOptional) && !(objectType as MappedType).declaration.nameType); } /** * For a type parameter, return the base constraint of the type parameter. For the string, number, * boolean, and symbol primitive types, return the corresponding object types. Otherwise return the * type itself. */ function getApparentType(type: Type): Type { const t = type.flags & TypeFlags.Instantiable ? getBaseConstraintOfType(type) || unknownType : type; const objectFlags = getObjectFlags(t); return objectFlags & ObjectFlags.Mapped ? getApparentTypeOfMappedType(t as MappedType) : objectFlags & ObjectFlags.Reference && t !== type ? getTypeWithThisArgument(t, type) : t.flags & TypeFlags.Intersection ? getApparentTypeOfIntersectionType(t as IntersectionType, type) : t.flags & TypeFlags.StringLike ? globalStringType : t.flags & TypeFlags.NumberLike ? globalNumberType : t.flags & TypeFlags.BigIntLike ? getGlobalBigIntType() : t.flags & TypeFlags.BooleanLike ? globalBooleanType : t.flags & TypeFlags.ESSymbolLike ? getGlobalESSymbolType() : t.flags & TypeFlags.NonPrimitive ? emptyObjectType : t.flags & TypeFlags.Index ? stringNumberSymbolType : t.flags & TypeFlags.Unknown && !strictNullChecks ? emptyObjectType : t; } function getReducedApparentType(type: Type): Type { // Since getApparentType may return a non-reduced union or intersection type, we need to perform // type reduction both before and after obtaining the apparent type. For example, given a type parameter // 'T extends A | B', the type 'T & X' becomes 'A & X | B & X' after obtaining the apparent type, and // that type may need further reduction to remove empty intersections. return getReducedType(getApparentType(getReducedType(type))); } function createUnionOrIntersectionProperty(containingType: UnionOrIntersectionType, name: __String, skipObjectFunctionPropertyAugment?: boolean): Symbol | undefined { let singleProp: Symbol | undefined; let propSet: Map | undefined; let indexTypes: Type[] | undefined; const isUnion = containingType.flags & TypeFlags.Union; // Flags we want to propagate to the result if they exist in all source symbols let optionalFlag: SymbolFlags | undefined; let syntheticFlag = CheckFlags.SyntheticMethod; let checkFlags = isUnion ? 0 : CheckFlags.Readonly; let mergedInstantiations = false; for (const current of containingType.types) { const type = getApparentType(current); if (!(isErrorType(type) || type.flags & TypeFlags.Never)) { const prop = getPropertyOfType(type, name, skipObjectFunctionPropertyAugment); const modifiers = prop ? getDeclarationModifierFlagsFromSymbol(prop) : 0; if (prop) { if (prop.flags & SymbolFlags.ClassMember) { optionalFlag ??= isUnion ? SymbolFlags.None : SymbolFlags.Optional; if (isUnion) { optionalFlag |= prop.flags & SymbolFlags.Optional; } else { optionalFlag &= prop.flags; } } if (!singleProp) { singleProp = prop; } else if (prop !== singleProp) { const isInstantiation = (getTargetSymbol(prop) || prop) === (getTargetSymbol(singleProp) || singleProp); // If the symbols are instances of one another with identical types - consider the symbols // equivalent and just use the first one, which thus allows us to avoid eliding private // members when intersecting a (this-)instantiations of a class with its raw base or another instance if (isInstantiation && compareProperties(singleProp, prop, (a, b) => a === b ? Ternary.True : Ternary.False) === Ternary.True) { // If we merged instantiations of a generic type, we replicate the symbol parent resetting behavior we used // to do when we recorded multiple distinct symbols so that we still get, eg, `Array.length` printed // back and not `Array.length` when we're looking at a `.length` access on a `string[] | number[]` mergedInstantiations = !!singleProp.parent && !!length(getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(singleProp.parent)); } else { if (!propSet) { propSet = new Map(); propSet.set(getSymbolId(singleProp), singleProp); } const id = getSymbolId(prop); if (!propSet.has(id)) { propSet.set(id, prop); } } } if (isUnion && isReadonlySymbol(prop)) { checkFlags |= CheckFlags.Readonly; } else if (!isUnion && !isReadonlySymbol(prop)) { checkFlags &= ~CheckFlags.Readonly; } checkFlags |= (!(modifiers & ModifierFlags.NonPublicAccessibilityModifier) ? CheckFlags.ContainsPublic : 0) | (modifiers & ModifierFlags.Protected ? CheckFlags.ContainsProtected : 0) | (modifiers & ModifierFlags.Private ? CheckFlags.ContainsPrivate : 0) | (modifiers & ModifierFlags.Static ? CheckFlags.ContainsStatic : 0); if (!isPrototypeProperty(prop)) { syntheticFlag = CheckFlags.SyntheticProperty; } } else if (isUnion) { const indexInfo = !isLateBoundName(name) && getApplicableIndexInfoForName(type, name); if (indexInfo) { checkFlags |= CheckFlags.WritePartial | (indexInfo.isReadonly ? CheckFlags.Readonly : 0); indexTypes = append(indexTypes, isTupleType(type) ? getRestTypeOfTupleType(type) || undefinedType : indexInfo.type); } else if (isObjectLiteralType(type) && !(getObjectFlags(type) & ObjectFlags.ContainsSpread)) { checkFlags |= CheckFlags.WritePartial; indexTypes = append(indexTypes, undefinedType); } else { checkFlags |= CheckFlags.ReadPartial; } } } } if ( !singleProp || isUnion && (propSet || checkFlags & CheckFlags.Partial) && checkFlags & (CheckFlags.ContainsPrivate | CheckFlags.ContainsProtected) && !(propSet && getCommonDeclarationsOfSymbols(propSet.values())) ) { // No property was found, or, in a union, a property has a private or protected declaration in one // constituent, but is missing or has a different declaration in another constituent. return undefined; } if (!propSet && !(checkFlags & CheckFlags.ReadPartial) && !indexTypes) { if (mergedInstantiations) { // No symbol from a union/intersection should have a `.parent` set (since unions/intersections don't act as symbol parents) // Unless that parent is "reconstituted" from the "first value declaration" on the symbol (which is likely different than its instantiated parent!) // They also have a `.containingType` set, which affects some services endpoints behavior, like `getRootSymbol` const links = tryCast(singleProp, isTransientSymbol)?.links; const clone = createSymbolWithType(singleProp, links?.type); clone.parent = singleProp.valueDeclaration?.symbol?.parent; clone.links.containingType = containingType; clone.links.mapper = links?.mapper; clone.links.writeType = getWriteTypeOfSymbol(singleProp); return clone; } else { return singleProp; } } const props = propSet ? arrayFrom(propSet.values()) : [singleProp]; let declarations: Declaration[] | undefined; let firstType: Type | undefined; let nameType: Type | undefined; const propTypes: Type[] = []; let writeTypes: Type[] | undefined; let firstValueDeclaration: Declaration | undefined; let hasNonUniformValueDeclaration = false; for (const prop of props) { if (!firstValueDeclaration) { firstValueDeclaration = prop.valueDeclaration; } else if (prop.valueDeclaration && prop.valueDeclaration !== firstValueDeclaration) { hasNonUniformValueDeclaration = true; } declarations = addRange(declarations, prop.declarations); const type = getTypeOfSymbol(prop); if (!firstType) { firstType = type; nameType = getSymbolLinks(prop).nameType; } const writeType = getWriteTypeOfSymbol(prop); if (writeTypes || writeType !== type) { writeTypes = append(!writeTypes ? propTypes.slice() : writeTypes, writeType); } if (type !== firstType) { checkFlags |= CheckFlags.HasNonUniformType; } if (isLiteralType(type) || isPatternLiteralType(type)) { checkFlags |= CheckFlags.HasLiteralType; } if (type.flags & TypeFlags.Never && type !== uniqueLiteralType) { checkFlags |= CheckFlags.HasNeverType; } propTypes.push(type); } addRange(propTypes, indexTypes); const result = createSymbol(SymbolFlags.Property | (optionalFlag ?? 0), name, syntheticFlag | checkFlags); result.links.containingType = containingType; if (!hasNonUniformValueDeclaration && firstValueDeclaration) { result.valueDeclaration = firstValueDeclaration; // Inherit information about parent type. if (firstValueDeclaration.symbol.parent) { result.parent = firstValueDeclaration.symbol.parent; } } result.declarations = declarations; result.links.nameType = nameType; if (propTypes.length > 2) { // When `propTypes` has the potential to explode in size when normalized, defer normalization until absolutely needed result.links.checkFlags |= CheckFlags.DeferredType; result.links.deferralParent = containingType; result.links.deferralConstituents = propTypes; result.links.deferralWriteConstituents = writeTypes; } else { result.links.type = isUnion ? getUnionType(propTypes) : getIntersectionType(propTypes); if (writeTypes) { result.links.writeType = isUnion ? getUnionType(writeTypes) : getIntersectionType(writeTypes); } } return result; } // Return the symbol for a given property in a union or intersection type, or undefined if the property // does not exist in any constituent type. Note that the returned property may only be present in some // constituents, in which case the isPartial flag is set when the containing type is union type. We need // these partial properties when identifying discriminant properties, but otherwise they are filtered out // and do not appear to be present in the union type. function getUnionOrIntersectionProperty(type: UnionOrIntersectionType, name: __String, skipObjectFunctionPropertyAugment?: boolean): Symbol | undefined { let property = skipObjectFunctionPropertyAugment ? type.propertyCacheWithoutObjectFunctionPropertyAugment?.get(name) : type.propertyCache?.get(name); if (!property) { property = createUnionOrIntersectionProperty(type, name, skipObjectFunctionPropertyAugment); if (property) { const properties = skipObjectFunctionPropertyAugment ? type.propertyCacheWithoutObjectFunctionPropertyAugment ||= createSymbolTable() : type.propertyCache ||= createSymbolTable(); properties.set(name, property); // Propagate an entry from the non-augmented cache to the augmented cache unless the property is partial. if (skipObjectFunctionPropertyAugment && !(getCheckFlags(property) & CheckFlags.Partial) && !type.propertyCache?.get(name)) { const properties = type.propertyCache ||= createSymbolTable(); properties.set(name, property); } } } return property; } function getCommonDeclarationsOfSymbols(symbols: Iterable) { let commonDeclarations: Set | undefined; for (const symbol of symbols) { if (!symbol.declarations) { return undefined; } if (!commonDeclarations) { commonDeclarations = new Set(symbol.declarations); continue; } commonDeclarations.forEach(declaration => { if (!contains(symbol.declarations, declaration)) { commonDeclarations!.delete(declaration); } }); if (commonDeclarations.size === 0) { return undefined; } } return commonDeclarations; } function getPropertyOfUnionOrIntersectionType(type: UnionOrIntersectionType, name: __String, skipObjectFunctionPropertyAugment?: boolean): Symbol | undefined { const property = getUnionOrIntersectionProperty(type, name, skipObjectFunctionPropertyAugment); // We need to filter out partial properties in union types return property && !(getCheckFlags(property) & CheckFlags.ReadPartial) ? property : undefined; } /** * Return the reduced form of the given type. For a union type, it is a union of the normalized constituent types. * For an intersection of types containing one or more mututally exclusive discriminant properties, it is 'never'. * For all other types, it is simply the type itself. Discriminant properties are considered mutually exclusive when * no constituent property has type 'never', but the intersection of the constituent property types is 'never'. */ function getReducedType(type: Type): Type { if (type.flags & TypeFlags.Union && (type as UnionType).objectFlags & ObjectFlags.ContainsIntersections) { return (type as UnionType).resolvedReducedType || ((type as UnionType).resolvedReducedType = getReducedUnionType(type as UnionType)); } else if (type.flags & TypeFlags.Intersection) { if (!((type as IntersectionType).objectFlags & ObjectFlags.IsNeverIntersectionComputed)) { (type as IntersectionType).objectFlags |= ObjectFlags.IsNeverIntersectionComputed | (some(getPropertiesOfUnionOrIntersectionType(type as IntersectionType), isNeverReducedProperty) ? ObjectFlags.IsNeverIntersection : 0); } return (type as IntersectionType).objectFlags & ObjectFlags.IsNeverIntersection ? neverType : type; } return type; } function getReducedUnionType(unionType: UnionType) { const reducedTypes = sameMap(unionType.types, getReducedType); if (reducedTypes === unionType.types) { return unionType; } const reduced = getUnionType(reducedTypes); if (reduced.flags & TypeFlags.Union) { (reduced as UnionType).resolvedReducedType = reduced; } return reduced; } function isNeverReducedProperty(prop: Symbol) { return isDiscriminantWithNeverType(prop) || isConflictingPrivateProperty(prop); } function isDiscriminantWithNeverType(prop: Symbol) { // Return true for a synthetic non-optional property with non-uniform types, where at least one is // a literal type and none is never, that reduces to never. return !(prop.flags & SymbolFlags.Optional) && (getCheckFlags(prop) & (CheckFlags.Discriminant | CheckFlags.HasNeverType)) === CheckFlags.Discriminant && !!(getTypeOfSymbol(prop).flags & TypeFlags.Never); } function isConflictingPrivateProperty(prop: Symbol) { // Return true for a synthetic property with multiple declarations, at least one of which is private. return !prop.valueDeclaration && !!(getCheckFlags(prop) & CheckFlags.ContainsPrivate); } /** * A union type which is reducible upon instantiation (meaning some members are removed under certain instantiations) * must be kept generic, as that instantiation information needs to flow through the type system. By replacing all * type parameters in the union with a special never type that is treated as a literal in `getReducedType`, we can cause * the `getReducedType` logic to reduce the resulting type if possible (since only intersections with conflicting * literal-typed properties are reducible). */ function isGenericReducibleType(type: Type): boolean { return !!(type.flags & TypeFlags.Union && (type as UnionType).objectFlags & ObjectFlags.ContainsIntersections && some((type as UnionType).types, isGenericReducibleType) || type.flags & TypeFlags.Intersection && isReducibleIntersection(type as IntersectionType)); } function isReducibleIntersection(type: IntersectionType) { const uniqueFilled = type.uniqueLiteralFilledInstantiation || (type.uniqueLiteralFilledInstantiation = instantiateType(type, uniqueLiteralMapper)); return getReducedType(uniqueFilled) !== uniqueFilled; } function elaborateNeverIntersection(errorInfo: DiagnosticMessageChain | undefined, type: Type) { if (type.flags & TypeFlags.Intersection && getObjectFlags(type) & ObjectFlags.IsNeverIntersection) { const neverProp = find(getPropertiesOfUnionOrIntersectionType(type as IntersectionType), isDiscriminantWithNeverType); if (neverProp) { return chainDiagnosticMessages(errorInfo, Diagnostics.The_intersection_0_was_reduced_to_never_because_property_1_has_conflicting_types_in_some_constituents, typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.NoTypeReduction), symbolToString(neverProp)); } const privateProp = find(getPropertiesOfUnionOrIntersectionType(type as IntersectionType), isConflictingPrivateProperty); if (privateProp) { return chainDiagnosticMessages(errorInfo, Diagnostics.The_intersection_0_was_reduced_to_never_because_property_1_exists_in_multiple_constituents_and_is_private_in_some, typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.NoTypeReduction), symbolToString(privateProp)); } } return errorInfo; } /** * Return the symbol for the property with the given name in the given type. Creates synthetic union properties when * necessary, maps primitive types and type parameters are to their apparent types, and augments with properties from * Object and Function as appropriate. * * @param type a type to look up property from * @param name a name of property to look up in a given type */ function getPropertyOfType(type: Type, name: __String, skipObjectFunctionPropertyAugment?: boolean, includeTypeOnlyMembers?: boolean): Symbol | undefined { type = getReducedApparentType(type); if (type.flags & TypeFlags.Object) { const resolved = resolveStructuredTypeMembers(type as ObjectType); const symbol = resolved.members.get(name); if (symbol && !includeTypeOnlyMembers && type.symbol?.flags & SymbolFlags.ValueModule && getSymbolLinks(type.symbol).typeOnlyExportStarMap?.has(name)) { // If this is the type of a module, `resolved.members.get(name)` might have effectively skipped over // an `export type * from './foo'`, leaving `symbolIsValue` unable to see that the symbol is being // viewed through a type-only export. return undefined; } if (symbol && symbolIsValue(symbol, includeTypeOnlyMembers)) { return symbol; } if (skipObjectFunctionPropertyAugment) return undefined; const functionType = resolved === anyFunctionType ? globalFunctionType : resolved.callSignatures.length ? globalCallableFunctionType : resolved.constructSignatures.length ? globalNewableFunctionType : undefined; if (functionType) { const symbol = getPropertyOfObjectType(functionType, name); if (symbol) { return symbol; } } return getPropertyOfObjectType(globalObjectType, name); } if (type.flags & TypeFlags.Intersection) { const prop = getPropertyOfUnionOrIntersectionType(type as UnionOrIntersectionType, name, /*skipObjectFunctionPropertyAugment*/ true); if (prop) { return prop; } if (!skipObjectFunctionPropertyAugment) { return getPropertyOfUnionOrIntersectionType(type as UnionOrIntersectionType, name, skipObjectFunctionPropertyAugment); } return undefined; } if (type.flags & TypeFlags.Union) { return getPropertyOfUnionOrIntersectionType(type as UnionOrIntersectionType, name, skipObjectFunctionPropertyAugment); } return undefined; } function getSignaturesOfStructuredType(type: Type, kind: SignatureKind): readonly Signature[] { if (type.flags & TypeFlags.StructuredType) { const resolved = resolveStructuredTypeMembers(type as ObjectType); return kind === SignatureKind.Call ? resolved.callSignatures : resolved.constructSignatures; } return emptyArray; } /** * Return the signatures of the given kind in the given type. Creates synthetic union signatures when necessary and * maps primitive types and type parameters are to their apparent types. */ function getSignaturesOfType(type: Type, kind: SignatureKind): readonly Signature[] { const result = getSignaturesOfStructuredType(getReducedApparentType(type), kind); if (kind === SignatureKind.Call && !length(result) && type.flags & TypeFlags.Union) { if ((type as UnionType).arrayFallbackSignatures) { return (type as UnionType).arrayFallbackSignatures!; } // If the union is all different instantiations of a member of the global array type... let memberName: __String; if (everyType(type, t => !!t.symbol?.parent && isArrayOrTupleSymbol(t.symbol.parent) && (!memberName ? (memberName = t.symbol.escapedName, true) : memberName === t.symbol.escapedName))) { // Transform the type from `(A[] | B[])["member"]` to `(A | B)[]["member"]` (since we pretend array is covariant anyway) const arrayArg = mapType(type, t => getMappedType((isReadonlyArraySymbol(t.symbol.parent) ? globalReadonlyArrayType : globalArrayType).typeParameters![0], (t as AnonymousType).mapper!)); const arrayType = createArrayType(arrayArg, someType(type, t => isReadonlyArraySymbol(t.symbol.parent))); return (type as UnionType).arrayFallbackSignatures = getSignaturesOfType(getTypeOfPropertyOfType(arrayType, memberName!)!, kind); } (type as UnionType).arrayFallbackSignatures = result; } return result; } function isArrayOrTupleSymbol(symbol: Symbol | undefined) { if (!symbol || !globalArrayType.symbol || !globalReadonlyArrayType.symbol) { return false; } return !!getSymbolIfSameReference(symbol, globalArrayType.symbol) || !!getSymbolIfSameReference(symbol, globalReadonlyArrayType.symbol); } function isReadonlyArraySymbol(symbol: Symbol | undefined) { if (!symbol || !globalReadonlyArrayType.symbol) { return false; } return !!getSymbolIfSameReference(symbol, globalReadonlyArrayType.symbol); } function findIndexInfo(indexInfos: readonly IndexInfo[], keyType: Type) { return find(indexInfos, info => info.keyType === keyType); } function findApplicableIndexInfo(indexInfos: readonly IndexInfo[], keyType: Type) { // Index signatures for type 'string' are considered only when no other index signatures apply. let stringIndexInfo: IndexInfo | undefined; let applicableInfo: IndexInfo | undefined; let applicableInfos: IndexInfo[] | undefined; for (const info of indexInfos) { if (info.keyType === stringType) { stringIndexInfo = info; } else if (isApplicableIndexType(keyType, info.keyType)) { if (!applicableInfo) { applicableInfo = info; } else { (applicableInfos || (applicableInfos = [applicableInfo])).push(info); } } } // When more than one index signature is applicable we create a synthetic IndexInfo. Instead of computing // the intersected key type, we just use unknownType for the key type as nothing actually depends on the // keyType property of the returned IndexInfo. return applicableInfos ? createIndexInfo(unknownType, getIntersectionType(map(applicableInfos, info => info.type)), reduceLeft(applicableInfos, (isReadonly, info) => isReadonly && info.isReadonly, /*initial*/ true)) : applicableInfo ? applicableInfo : stringIndexInfo && isApplicableIndexType(keyType, stringType) ? stringIndexInfo : undefined; } function isApplicableIndexType(source: Type, target: Type): boolean { // A 'string' index signature applies to types assignable to 'string' or 'number', and a 'number' index // signature applies to types assignable to 'number', `${number}` and numeric string literal types. return isTypeAssignableTo(source, target) || target === stringType && isTypeAssignableTo(source, numberType) || target === numberType && (source === numericStringType || !!(source.flags & TypeFlags.StringLiteral) && isNumericLiteralName((source as StringLiteralType).value)); } function getIndexInfosOfStructuredType(type: Type): readonly IndexInfo[] { if (type.flags & TypeFlags.StructuredType) { const resolved = resolveStructuredTypeMembers(type as ObjectType); return resolved.indexInfos; } return emptyArray; } function getIndexInfosOfType(type: Type): readonly IndexInfo[] { return getIndexInfosOfStructuredType(getReducedApparentType(type)); } // Return the indexing info of the given kind in the given type. Creates synthetic union index types when necessary and // maps primitive types and type parameters are to their apparent types. function getIndexInfoOfType(type: Type, keyType: Type): IndexInfo | undefined { return findIndexInfo(getIndexInfosOfType(type), keyType); } // Return the index type of the given kind in the given type. Creates synthetic union index types when necessary and // maps primitive types and type parameters are to their apparent types. function getIndexTypeOfType(type: Type, keyType: Type): Type | undefined { return getIndexInfoOfType(type, keyType)?.type; } function getApplicableIndexInfos(type: Type, keyType: Type): IndexInfo[] { return getIndexInfosOfType(type).filter(info => isApplicableIndexType(keyType, info.keyType)); } function getApplicableIndexInfo(type: Type, keyType: Type): IndexInfo | undefined { return findApplicableIndexInfo(getIndexInfosOfType(type), keyType); } function getApplicableIndexInfoForName(type: Type, name: __String): IndexInfo | undefined { return getApplicableIndexInfo(type, isLateBoundName(name) ? esSymbolType : getStringLiteralType(unescapeLeadingUnderscores(name))); } // Return list of type parameters with duplicates removed (duplicate identifier errors are generated in the actual // type checking functions). function getTypeParametersFromDeclaration(declaration: DeclarationWithTypeParameters): readonly TypeParameter[] | undefined { let result: TypeParameter[] | undefined; for (const node of getEffectiveTypeParameterDeclarations(declaration)) { result = appendIfUnique(result, getDeclaredTypeOfTypeParameter(node.symbol)); } return result?.length ? result : isFunctionDeclaration(declaration) ? getSignatureOfTypeTag(declaration)?.typeParameters : undefined; } function symbolsToArray(symbols: SymbolTable): Symbol[] { const result: Symbol[] = []; symbols.forEach((symbol, id) => { if (!isReservedMemberName(id)) { result.push(symbol); } }); return result; } function tryFindAmbientModule(moduleName: string, withAugmentations: boolean) { if (isExternalModuleNameRelative(moduleName)) { return undefined; } const symbol = getSymbol(globals, '"' + moduleName + '"' as __String, SymbolFlags.ValueModule); // merged symbol is module declaration symbol combined with all augmentations return symbol && withAugmentations ? getMergedSymbol(symbol) : symbol; } function hasEffectiveQuestionToken(node: ParameterDeclaration | JSDocParameterTag | JSDocPropertyTag) { return hasQuestionToken(node) || isOptionalJSDocPropertyLikeTag(node) || isParameter(node) && isJSDocOptionalParameter(node); } function isOptionalParameter(node: ParameterDeclaration | JSDocParameterTag | JSDocPropertyTag) { if (hasEffectiveQuestionToken(node)) { return true; } if (!isParameter(node)) { return false; } if (node.initializer) { const signature = getSignatureFromDeclaration(node.parent); const parameterIndex = node.parent.parameters.indexOf(node); Debug.assert(parameterIndex >= 0); // Only consider syntactic or instantiated parameters as optional, not `void` parameters as this function is used // in grammar checks and checking for `void` too early results in parameter types widening too early // and causes some noImplicitAny errors to be lost. return parameterIndex >= getMinArgumentCount(signature, MinArgumentCountFlags.StrongArityForUntypedJS | MinArgumentCountFlags.VoidIsNonOptional); } const iife = getImmediatelyInvokedFunctionExpression(node.parent); if (iife) { return !node.type && !node.dotDotDotToken && node.parent.parameters.indexOf(node) >= getEffectiveCallArguments(iife).length; } return false; } function isOptionalPropertyDeclaration(node: Declaration) { return isPropertyDeclaration(node) && !hasAccessorModifier(node) && node.questionToken; } function createTypePredicate(kind: TypePredicateKind, parameterName: string | undefined, parameterIndex: number | undefined, type: Type | undefined): TypePredicate { return { kind, parameterName, parameterIndex, type } as TypePredicate; } /** * Gets the minimum number of type arguments needed to satisfy all non-optional type * parameters. */ function getMinTypeArgumentCount(typeParameters: readonly TypeParameter[] | undefined): number { let minTypeArgumentCount = 0; if (typeParameters) { for (let i = 0; i < typeParameters.length; i++) { if (!hasTypeParameterDefault(typeParameters[i])) { minTypeArgumentCount = i + 1; } } } return minTypeArgumentCount; } /** * Fill in default types for unsupplied type arguments. If `typeArguments` is undefined * when a default type is supplied, a new array will be created and returned. * * @param typeArguments The supplied type arguments. * @param typeParameters The requested type parameters. * @param minTypeArgumentCount The minimum number of required type arguments. */ function fillMissingTypeArguments(typeArguments: readonly Type[], typeParameters: readonly TypeParameter[] | undefined, minTypeArgumentCount: number, isJavaScriptImplicitAny: boolean): Type[]; function fillMissingTypeArguments(typeArguments: readonly Type[] | undefined, typeParameters: readonly TypeParameter[] | undefined, minTypeArgumentCount: number, isJavaScriptImplicitAny: boolean): Type[] | undefined; function fillMissingTypeArguments(typeArguments: readonly Type[] | undefined, typeParameters: readonly TypeParameter[] | undefined, minTypeArgumentCount: number, isJavaScriptImplicitAny: boolean) { const numTypeParameters = length(typeParameters); if (!numTypeParameters) { return []; } const numTypeArguments = length(typeArguments); if (isJavaScriptImplicitAny || (numTypeArguments >= minTypeArgumentCount && numTypeArguments <= numTypeParameters)) { const result = typeArguments ? typeArguments.slice() : []; // Map invalid forward references in default types to the error type for (let i = numTypeArguments; i < numTypeParameters; i++) { result[i] = errorType; } const baseDefaultType = getDefaultTypeArgumentType(isJavaScriptImplicitAny); for (let i = numTypeArguments; i < numTypeParameters; i++) { let defaultType = getDefaultFromTypeParameter(typeParameters![i]); if (isJavaScriptImplicitAny && defaultType && (isTypeIdenticalTo(defaultType, unknownType) || isTypeIdenticalTo(defaultType, emptyObjectType))) { defaultType = anyType; } result[i] = defaultType ? instantiateType(defaultType, createTypeMapper(typeParameters!, result)) : baseDefaultType; } result.length = typeParameters!.length; return result; } return typeArguments && typeArguments.slice(); } function getSignatureFromDeclaration(declaration: SignatureDeclaration | JSDocSignature): Signature { const links = getNodeLinks(declaration); if (!links.resolvedSignature) { const parameters: Symbol[] = []; let flags = SignatureFlags.None; let minArgumentCount = 0; let thisParameter: Symbol | undefined; let thisTag: JSDocThisTag | undefined = isInJSFile(declaration) ? getJSDocThisTag(declaration) : undefined; let hasThisParameter = false; const iife = getImmediatelyInvokedFunctionExpression(declaration); const isJSConstructSignature = isJSDocConstructSignature(declaration); const isUntypedSignatureInJSFile = !iife && isInJSFile(declaration) && isValueSignatureDeclaration(declaration) && !hasJSDocParameterTags(declaration) && !getJSDocType(declaration); if (isUntypedSignatureInJSFile) { flags |= SignatureFlags.IsUntypedSignatureInJSFile; } // If this is a JSDoc construct signature, then skip the first parameter in the // parameter list. The first parameter represents the return type of the construct // signature. for (let i = isJSConstructSignature ? 1 : 0; i < declaration.parameters.length; i++) { const param = declaration.parameters[i]; if (isInJSFile(param) && isJSDocThisTag(param)) { thisTag = param; continue; } let paramSymbol = param.symbol; const type = isJSDocParameterTag(param) ? (param.typeExpression && param.typeExpression.type) : param.type; // Include parameter symbol instead of property symbol in the signature if (paramSymbol && !!(paramSymbol.flags & SymbolFlags.Property) && !isBindingPattern(param.name)) { const resolvedSymbol = resolveName(param, paramSymbol.escapedName, SymbolFlags.Value, /*nameNotFoundMessage*/ undefined, /*isUse*/ false); paramSymbol = resolvedSymbol!; } if (i === 0 && paramSymbol.escapedName === InternalSymbolName.This) { hasThisParameter = true; thisParameter = param.symbol; } else { parameters.push(paramSymbol); } if (type && type.kind === SyntaxKind.LiteralType) { flags |= SignatureFlags.HasLiteralTypes; } // Record a new minimum argument count if this is not an optional parameter const isOptionalParameter = hasEffectiveQuestionToken(param) || isParameter(param) && param.initializer || isRestParameter(param) || iife && parameters.length > iife.arguments.length && !type; if (!isOptionalParameter) { minArgumentCount = parameters.length; } } // If only one accessor includes a this-type annotation, the other behaves as if it had the same type annotation if ( (declaration.kind === SyntaxKind.GetAccessor || declaration.kind === SyntaxKind.SetAccessor) && hasBindableName(declaration) && (!hasThisParameter || !thisParameter) ) { const otherKind = declaration.kind === SyntaxKind.GetAccessor ? SyntaxKind.SetAccessor : SyntaxKind.GetAccessor; const other = getDeclarationOfKind(getSymbolOfDeclaration(declaration), otherKind); if (other) { thisParameter = getAnnotatedAccessorThisParameter(other); } } if (thisTag && thisTag.typeExpression) { thisParameter = createSymbolWithType(createSymbol(SymbolFlags.FunctionScopedVariable, InternalSymbolName.This), getTypeFromTypeNode(thisTag.typeExpression)); } const hostDeclaration = isJSDocSignature(declaration) ? getEffectiveJSDocHost(declaration) : declaration; const classType = hostDeclaration && isConstructorDeclaration(hostDeclaration) ? getDeclaredTypeOfClassOrInterface(getMergedSymbol((hostDeclaration.parent as ClassDeclaration).symbol)) : undefined; const typeParameters = classType ? classType.localTypeParameters : getTypeParametersFromDeclaration(declaration); if (hasRestParameter(declaration) || isInJSFile(declaration) && maybeAddJsSyntheticRestParameter(declaration, parameters)) { flags |= SignatureFlags.HasRestParameter; } if ( isConstructorTypeNode(declaration) && hasSyntacticModifier(declaration, ModifierFlags.Abstract) || isConstructorDeclaration(declaration) && hasSyntacticModifier(declaration.parent, ModifierFlags.Abstract) ) { flags |= SignatureFlags.Abstract; } links.resolvedSignature = createSignature(declaration, typeParameters, thisParameter, parameters, /*resolvedReturnType*/ undefined, /*resolvedTypePredicate*/ undefined, minArgumentCount, flags); } return links.resolvedSignature; } /** * A JS function gets a synthetic rest parameter if it references `arguments` AND: * 1. It has no parameters but at least one `@param` with a type that starts with `...` * OR * 2. It has at least one parameter, and the last parameter has a matching `@param` with a type that starts with `...` */ function maybeAddJsSyntheticRestParameter(declaration: SignatureDeclaration | JSDocSignature, parameters: Symbol[]): boolean { if (isJSDocSignature(declaration) || !containsArgumentsReference(declaration)) { return false; } const lastParam = lastOrUndefined(declaration.parameters); const lastParamTags = lastParam ? getJSDocParameterTags(lastParam) : getJSDocTags(declaration).filter(isJSDocParameterTag); const lastParamVariadicType = firstDefined(lastParamTags, p => p.typeExpression && isJSDocVariadicType(p.typeExpression.type) ? p.typeExpression.type : undefined); const syntheticArgsSymbol = createSymbol(SymbolFlags.Variable, "args" as __String, CheckFlags.RestParameter); if (lastParamVariadicType) { // Parameter has effective annotation, lock in type syntheticArgsSymbol.links.type = createArrayType(getTypeFromTypeNode(lastParamVariadicType.type)); } else { // Parameter has no annotation // By using a `DeferredType` symbol, we allow the type of this rest arg to be overriden by contextual type assignment so long as its type hasn't been // cached by `getTypeOfSymbol` yet. syntheticArgsSymbol.links.checkFlags |= CheckFlags.DeferredType; syntheticArgsSymbol.links.deferralParent = neverType; syntheticArgsSymbol.links.deferralConstituents = [anyArrayType]; syntheticArgsSymbol.links.deferralWriteConstituents = [anyArrayType]; } if (lastParamVariadicType) { // Replace the last parameter with a rest parameter. parameters.pop(); } parameters.push(syntheticArgsSymbol); return true; } function getSignatureOfTypeTag(node: SignatureDeclaration | JSDocSignature) { // should be attached to a function declaration or expression if (!(isInJSFile(node) && isFunctionLikeDeclaration(node))) return undefined; const typeTag = getJSDocTypeTag(node); return typeTag?.typeExpression && getSingleCallSignature(getTypeFromTypeNode(typeTag.typeExpression)); } function getParameterTypeOfTypeTag(func: FunctionLikeDeclaration, parameter: ParameterDeclaration) { const signature = getSignatureOfTypeTag(func); if (!signature) return undefined; const pos = func.parameters.indexOf(parameter); return parameter.dotDotDotToken ? getRestTypeAtPosition(signature, pos) : getTypeAtPosition(signature, pos); } function getReturnTypeOfTypeTag(node: SignatureDeclaration | JSDocSignature) { const signature = getSignatureOfTypeTag(node); return signature && getReturnTypeOfSignature(signature); } function containsArgumentsReference(declaration: SignatureDeclaration): boolean { const links = getNodeLinks(declaration); if (links.containsArgumentsReference === undefined) { if (links.flags & NodeCheckFlags.CaptureArguments) { links.containsArgumentsReference = true; } else { links.containsArgumentsReference = traverse((declaration as FunctionLikeDeclaration).body!); } } return links.containsArgumentsReference; function traverse(node: Node): boolean { if (!node) return false; switch (node.kind) { case SyntaxKind.Identifier: return (node as Identifier).escapedText === argumentsSymbol.escapedName && getReferencedValueSymbol(node as Identifier) === argumentsSymbol; case SyntaxKind.PropertyDeclaration: case SyntaxKind.MethodDeclaration: case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: return (node as NamedDeclaration).name!.kind === SyntaxKind.ComputedPropertyName && traverse((node as NamedDeclaration).name!); case SyntaxKind.PropertyAccessExpression: case SyntaxKind.ElementAccessExpression: return traverse((node as PropertyAccessExpression | ElementAccessExpression).expression); case SyntaxKind.PropertyAssignment: return traverse((node as PropertyAssignment).initializer); default: return !nodeStartsNewLexicalEnvironment(node) && !isPartOfTypeNode(node) && !!forEachChild(node, traverse); } } } function getSignaturesOfSymbol(symbol: Symbol | undefined): Signature[] { if (!symbol || !symbol.declarations) return emptyArray; const result: Signature[] = []; for (let i = 0; i < symbol.declarations.length; i++) { const decl = symbol.declarations[i]; if (!isFunctionLike(decl)) continue; // Don't include signature if node is the implementation of an overloaded function. A node is considered // an implementation node if it has a body and the previous node is of the same kind and immediately // precedes the implementation node (i.e. has the same parent and ends where the implementation starts). if (i > 0 && (decl as FunctionLikeDeclaration).body) { const previous = symbol.declarations[i - 1]; if (decl.parent === previous.parent && decl.kind === previous.kind && decl.pos === previous.end) { continue; } } if (isInJSFile(decl) && decl.jsDoc) { const tags = getJSDocOverloadTags(decl); if (length(tags)) { for (const tag of tags) { const jsDocSignature = tag.typeExpression; if (jsDocSignature.type === undefined && !isConstructorDeclaration(decl)) { reportImplicitAny(jsDocSignature, anyType); } result.push(getSignatureFromDeclaration(jsDocSignature)); } continue; } } // If this is a function or method declaration, get the signature from the @type tag for the sake of optional parameters. // Exclude contextually-typed kinds because we already apply the @type tag to the context, plus applying it here to the initializer would supress checks that the two are compatible. result.push( (!isFunctionExpressionOrArrowFunction(decl) && !isObjectLiteralMethod(decl) && getSignatureOfTypeTag(decl)) || getSignatureFromDeclaration(decl), ); } return result; } function resolveExternalModuleTypeByLiteral(name: StringLiteral) { const moduleSym = resolveExternalModuleName(name, name); if (moduleSym) { const resolvedModuleSymbol = resolveExternalModuleSymbol(moduleSym); if (resolvedModuleSymbol) { return getTypeOfSymbol(resolvedModuleSymbol); } } return anyType; } function getThisTypeOfSignature(signature: Signature): Type | undefined { if (signature.thisParameter) { return getTypeOfSymbol(signature.thisParameter); } } function getTypePredicateOfSignature(signature: Signature): TypePredicate | undefined { if (!signature.resolvedTypePredicate) { if (signature.target) { const targetTypePredicate = getTypePredicateOfSignature(signature.target); signature.resolvedTypePredicate = targetTypePredicate ? instantiateTypePredicate(targetTypePredicate, signature.mapper!) : noTypePredicate; } else if (signature.compositeSignatures) { signature.resolvedTypePredicate = getUnionOrIntersectionTypePredicate(signature.compositeSignatures, signature.compositeKind) || noTypePredicate; } else { const type = signature.declaration && getEffectiveReturnTypeNode(signature.declaration); let jsdocPredicate: TypePredicate | undefined; if (!type) { const jsdocSignature = getSignatureOfTypeTag(signature.declaration!); if (jsdocSignature && signature !== jsdocSignature) { jsdocPredicate = getTypePredicateOfSignature(jsdocSignature); } } if (type || jsdocPredicate) { signature.resolvedTypePredicate = type && isTypePredicateNode(type) ? createTypePredicateFromTypePredicateNode(type, signature) : jsdocPredicate || noTypePredicate; } else if (signature.declaration && isFunctionLikeDeclaration(signature.declaration) && (!signature.resolvedReturnType || signature.resolvedReturnType.flags & TypeFlags.Boolean) && getParameterCount(signature) > 0) { const { declaration } = signature; signature.resolvedTypePredicate = noTypePredicate; // avoid infinite loop signature.resolvedTypePredicate = getTypePredicateFromBody(declaration) || noTypePredicate; } else { signature.resolvedTypePredicate = noTypePredicate; } } Debug.assert(!!signature.resolvedTypePredicate); } return signature.resolvedTypePredicate === noTypePredicate ? undefined : signature.resolvedTypePredicate; } function createTypePredicateFromTypePredicateNode(node: TypePredicateNode, signature: Signature): TypePredicate { const parameterName = node.parameterName; const type = node.type && getTypeFromTypeNode(node.type); return parameterName.kind === SyntaxKind.ThisType ? createTypePredicate(node.assertsModifier ? TypePredicateKind.AssertsThis : TypePredicateKind.This, /*parameterName*/ undefined, /*parameterIndex*/ undefined, type) : createTypePredicate(node.assertsModifier ? TypePredicateKind.AssertsIdentifier : TypePredicateKind.Identifier, parameterName.escapedText as string, findIndex(signature.parameters, p => p.escapedName === parameterName.escapedText), type); } function getUnionOrIntersectionType(types: Type[], kind: TypeFlags | undefined, unionReduction?: UnionReduction) { return kind !== TypeFlags.Intersection ? getUnionType(types, unionReduction) : getIntersectionType(types); } function getReturnTypeOfSignature(signature: Signature): Type { if (!signature.resolvedReturnType) { if (!pushTypeResolution(signature, TypeSystemPropertyName.ResolvedReturnType)) { return errorType; } let type = signature.target ? instantiateType(getReturnTypeOfSignature(signature.target), signature.mapper) : signature.compositeSignatures ? instantiateType(getUnionOrIntersectionType(map(signature.compositeSignatures, getReturnTypeOfSignature), signature.compositeKind, UnionReduction.Subtype), signature.mapper) : getReturnTypeFromAnnotation(signature.declaration!) || (nodeIsMissing((signature.declaration as FunctionLikeDeclaration).body) ? anyType : getReturnTypeFromBody(signature.declaration as FunctionLikeDeclaration)); if (signature.flags & SignatureFlags.IsInnerCallChain) { type = addOptionalTypeMarker(type); } else if (signature.flags & SignatureFlags.IsOuterCallChain) { type = getOptionalType(type); } if (!popTypeResolution()) { if (signature.declaration) { const typeNode = getEffectiveReturnTypeNode(signature.declaration); if (typeNode) { error(typeNode, Diagnostics.Return_type_annotation_circularly_references_itself); } else if (noImplicitAny) { const declaration = signature.declaration as Declaration; const name = getNameOfDeclaration(declaration); if (name) { error(name, Diagnostics._0_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions, declarationNameToString(name)); } else { error(declaration, Diagnostics.Function_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions); } } } type = anyType; } signature.resolvedReturnType ??= type; } return signature.resolvedReturnType; } function getReturnTypeFromAnnotation(declaration: SignatureDeclaration | JSDocSignature) { if (declaration.kind === SyntaxKind.Constructor) { return getDeclaredTypeOfClassOrInterface(getMergedSymbol((declaration.parent as ClassDeclaration).symbol)); } const typeNode = getEffectiveReturnTypeNode(declaration); if (isJSDocSignature(declaration)) { const root = getJSDocRoot(declaration); if (root && isConstructorDeclaration(root.parent) && !typeNode) { return getDeclaredTypeOfClassOrInterface(getMergedSymbol((root.parent.parent as ClassDeclaration).symbol)); } } if (isJSDocConstructSignature(declaration)) { return getTypeFromTypeNode((declaration.parameters[0] as ParameterDeclaration).type!); // TODO: GH#18217 } if (typeNode) { return getTypeFromTypeNode(typeNode); } if (declaration.kind === SyntaxKind.GetAccessor && hasBindableName(declaration)) { const jsDocType = isInJSFile(declaration) && getTypeForDeclarationFromJSDocComment(declaration); if (jsDocType) { return jsDocType; } const setter = getDeclarationOfKind(getSymbolOfDeclaration(declaration), SyntaxKind.SetAccessor); const setterType = getAnnotatedAccessorType(setter); if (setterType) { return setterType; } } return getReturnTypeOfTypeTag(declaration); } function isResolvingReturnTypeOfSignature(signature: Signature): boolean { return signature.compositeSignatures && some(signature.compositeSignatures, isResolvingReturnTypeOfSignature) || !signature.resolvedReturnType && findResolutionCycleStartIndex(signature, TypeSystemPropertyName.ResolvedReturnType) >= 0; } function getRestTypeOfSignature(signature: Signature): Type { return tryGetRestTypeOfSignature(signature) || anyType; } function tryGetRestTypeOfSignature(signature: Signature): Type | undefined { if (signatureHasRestParameter(signature)) { const sigRestType = getTypeOfSymbol(signature.parameters[signature.parameters.length - 1]); const restType = isTupleType(sigRestType) ? getRestTypeOfTupleType(sigRestType) : sigRestType; return restType && getIndexTypeOfType(restType, numberType); } return undefined; } function getSignatureInstantiation(signature: Signature, typeArguments: readonly Type[] | undefined, isJavascript: boolean, inferredTypeParameters?: readonly TypeParameter[]): Signature { const instantiatedSignature = getSignatureInstantiationWithoutFillingInTypeArguments(signature, fillMissingTypeArguments(typeArguments, signature.typeParameters, getMinTypeArgumentCount(signature.typeParameters), isJavascript)); if (inferredTypeParameters) { const returnSignature = getSingleCallOrConstructSignature(getReturnTypeOfSignature(instantiatedSignature)); if (returnSignature) { const newReturnSignature = cloneSignature(returnSignature); newReturnSignature.typeParameters = inferredTypeParameters; const newInstantiatedSignature = cloneSignature(instantiatedSignature); newInstantiatedSignature.resolvedReturnType = getOrCreateTypeFromSignature(newReturnSignature); return newInstantiatedSignature; } } return instantiatedSignature; } function getSignatureInstantiationWithoutFillingInTypeArguments(signature: Signature, typeArguments: readonly Type[] | undefined): Signature { const instantiations = signature.instantiations || (signature.instantiations = new Map()); const id = getTypeListId(typeArguments); let instantiation = instantiations.get(id); if (!instantiation) { instantiations.set(id, instantiation = createSignatureInstantiation(signature, typeArguments)); } return instantiation; } function createSignatureInstantiation(signature: Signature, typeArguments: readonly Type[] | undefined): Signature { return instantiateSignature(signature, createSignatureTypeMapper(signature, typeArguments), /*eraseTypeParameters*/ true); } function getTypeParametersForMapper(signature: Signature) { return sameMap(signature.typeParameters, tp => tp.mapper ? instantiateType(tp, tp.mapper) : tp); } function createSignatureTypeMapper(signature: Signature, typeArguments: readonly Type[] | undefined): TypeMapper { return createTypeMapper(getTypeParametersForMapper(signature)!, typeArguments); } function getErasedSignature(signature: Signature): Signature { return signature.typeParameters ? signature.erasedSignatureCache || (signature.erasedSignatureCache = createErasedSignature(signature)) : signature; } function createErasedSignature(signature: Signature) { // Create an instantiation of the signature where all type arguments are the any type. return instantiateSignature(signature, createTypeEraser(signature.typeParameters!), /*eraseTypeParameters*/ true); } function getCanonicalSignature(signature: Signature): Signature { return signature.typeParameters ? signature.canonicalSignatureCache || (signature.canonicalSignatureCache = createCanonicalSignature(signature)) : signature; } function createCanonicalSignature(signature: Signature) { // Create an instantiation of the signature where each unconstrained type parameter is replaced with // its original. When a generic class or interface is instantiated, each generic method in the class or // interface is instantiated with a fresh set of cloned type parameters (which we need to handle scenarios // where different generations of the same type parameter are in scope). This leads to a lot of new type // identities, and potentially a lot of work comparing those identities, so here we create an instantiation // that uses the original type identities for all unconstrained type parameters. return getSignatureInstantiation( signature, map(signature.typeParameters, tp => tp.target && !getConstraintOfTypeParameter(tp.target) ? tp.target : tp), isInJSFile(signature.declaration), ); } function getImplementationSignature(signature: Signature) { return signature.typeParameters ? signature.implementationSignatureCache ||= createImplementationSignature(signature) : signature; } function createImplementationSignature(signature: Signature) { return signature.typeParameters ? instantiateSignature(signature, createTypeMapper([], [])) : signature; } function getBaseSignature(signature: Signature) { const typeParameters = signature.typeParameters; if (typeParameters) { if (signature.baseSignatureCache) { return signature.baseSignatureCache; } const typeEraser = createTypeEraser(typeParameters); const baseConstraintMapper = createTypeMapper(typeParameters, map(typeParameters, tp => getConstraintOfTypeParameter(tp) || unknownType)); let baseConstraints: readonly Type[] = map(typeParameters, tp => instantiateType(tp, baseConstraintMapper) || unknownType); // Run N type params thru the immediate constraint mapper up to N times // This way any noncircular interdependent type parameters are definitely resolved to their external dependencies for (let i = 0; i < typeParameters.length - 1; i++) { baseConstraints = instantiateTypes(baseConstraints, baseConstraintMapper); } // and then apply a type eraser to remove any remaining circularly dependent type parameters baseConstraints = instantiateTypes(baseConstraints, typeEraser); return signature.baseSignatureCache = instantiateSignature(signature, createTypeMapper(typeParameters, baseConstraints), /*eraseTypeParameters*/ true); } return signature; } function getOrCreateTypeFromSignature(signature: Signature, outerTypeParameters?: TypeParameter[]): ObjectType { // There are two ways to declare a construct signature, one is by declaring a class constructor // using the constructor keyword, and the other is declaring a bare construct signature in an // object type literal or interface (using the new keyword). Each way of declaring a constructor // will result in a different declaration kind. if (!signature.isolatedSignatureType) { const kind = signature.declaration?.kind; // If declaration is undefined, it is likely to be the signature of the default constructor. const isConstructor = kind === undefined || kind === SyntaxKind.Constructor || kind === SyntaxKind.ConstructSignature || kind === SyntaxKind.ConstructorType; // The type must have a symbol with a `Function` flag and a declaration in order to be correctly flagged as possibly containing // type variables by `couldContainTypeVariables` const type = createObjectType(ObjectFlags.Anonymous | ObjectFlags.SingleSignatureType, createSymbol(SymbolFlags.Function, InternalSymbolName.Function)) as SingleSignatureType; if (signature.declaration && !nodeIsSynthesized(signature.declaration)) { // skip synthetic declarations - keeping those around could be bad, since they lack a parent pointer type.symbol.declarations = [signature.declaration]; type.symbol.valueDeclaration = signature.declaration; } outerTypeParameters ||= signature.declaration && getOuterTypeParameters(signature.declaration, /*includeThisTypes*/ true); type.outerTypeParameters = outerTypeParameters; type.members = emptySymbols; type.properties = emptyArray; type.callSignatures = !isConstructor ? [signature] : emptyArray; type.constructSignatures = isConstructor ? [signature] : emptyArray; type.indexInfos = emptyArray; signature.isolatedSignatureType = type; } return signature.isolatedSignatureType; } function getIndexSymbol(symbol: Symbol): Symbol | undefined { return symbol.members ? getIndexSymbolFromSymbolTable(symbol.members) : undefined; } function getIndexSymbolFromSymbolTable(symbolTable: SymbolTable): Symbol | undefined { return symbolTable.get(InternalSymbolName.Index); } function createIndexInfo(keyType: Type, type: Type, isReadonly: boolean, declaration?: IndexSignatureDeclaration): IndexInfo { return { keyType, type, isReadonly, declaration }; } function getIndexInfosOfSymbol(symbol: Symbol): IndexInfo[] { const indexSymbol = getIndexSymbol(symbol); return indexSymbol ? getIndexInfosOfIndexSymbol(indexSymbol) : emptyArray; } function getIndexInfosOfIndexSymbol(indexSymbol: Symbol): IndexInfo[] { if (indexSymbol.declarations) { const indexInfos: IndexInfo[] = []; for (const declaration of (indexSymbol.declarations as IndexSignatureDeclaration[])) { if (declaration.parameters.length === 1) { const parameter = declaration.parameters[0]; if (parameter.type) { forEachType(getTypeFromTypeNode(parameter.type), keyType => { if (isValidIndexKeyType(keyType) && !findIndexInfo(indexInfos, keyType)) { indexInfos.push(createIndexInfo(keyType, declaration.type ? getTypeFromTypeNode(declaration.type) : anyType, hasEffectiveModifier(declaration, ModifierFlags.Readonly), declaration)); } }); } } } return indexInfos; } return emptyArray; } function isValidIndexKeyType(type: Type): boolean { return !!(type.flags & (TypeFlags.String | TypeFlags.Number | TypeFlags.ESSymbol)) || isPatternLiteralType(type) || !!(type.flags & TypeFlags.Intersection) && !isGenericType(type) && some((type as IntersectionType).types, isValidIndexKeyType); } function getConstraintDeclaration(type: TypeParameter): TypeNode | undefined { return mapDefined(filter(type.symbol && type.symbol.declarations, isTypeParameterDeclaration), getEffectiveConstraintOfTypeParameter)[0]; } function getInferredTypeParameterConstraint(typeParameter: TypeParameter, omitTypeReferences?: boolean) { let inferences: Type[] | undefined; if (typeParameter.symbol?.declarations) { for (const declaration of typeParameter.symbol.declarations) { if (declaration.parent.kind === SyntaxKind.InferType) { // When an 'infer T' declaration is immediately contained in a type reference node // (such as 'Foo'), T's constraint is inferred from the constraint of the // corresponding type parameter in 'Foo'. When multiple 'infer T' declarations are // present, we form an intersection of the inferred constraint types. const [childTypeParameter = declaration.parent, grandParent] = walkUpParenthesizedTypesAndGetParentAndChild(declaration.parent.parent); if (grandParent.kind === SyntaxKind.TypeReference && !omitTypeReferences) { const typeReference = grandParent as TypeReferenceNode; const typeParameters = getTypeParametersForTypeReferenceOrImport(typeReference); if (typeParameters) { const index = typeReference.typeArguments!.indexOf(childTypeParameter as TypeNode); if (index < typeParameters.length) { const declaredConstraint = getConstraintOfTypeParameter(typeParameters[index]); if (declaredConstraint) { // Type parameter constraints can reference other type parameters so // constraints need to be instantiated. If instantiation produces the // type parameter itself, we discard that inference. For example, in // type Foo = [T, U]; // type Bar = T extends Foo ? Foo : T; // the instantiated constraint for U is X, so we discard that inference. const mapper = makeDeferredTypeMapper( typeParameters, typeParameters.map((_, index) => () => { return getEffectiveTypeArgumentAtIndex(typeReference, typeParameters, index); }), ); const constraint = instantiateType(declaredConstraint, mapper); if (constraint !== typeParameter) { inferences = append(inferences, constraint); } } } } } // When an 'infer T' declaration is immediately contained in a rest parameter declaration, a rest type // or a named rest tuple element, we infer an 'unknown[]' constraint. else if ( grandParent.kind === SyntaxKind.Parameter && (grandParent as ParameterDeclaration).dotDotDotToken || grandParent.kind === SyntaxKind.RestType || grandParent.kind === SyntaxKind.NamedTupleMember && (grandParent as NamedTupleMember).dotDotDotToken ) { inferences = append(inferences, createArrayType(unknownType)); } // When an 'infer T' declaration is immediately contained in a string template type, we infer a 'string' // constraint. else if (grandParent.kind === SyntaxKind.TemplateLiteralTypeSpan) { inferences = append(inferences, stringType); } // When an 'infer T' declaration is in the constraint position of a mapped type, we infer a 'keyof any' // constraint. else if (grandParent.kind === SyntaxKind.TypeParameter && grandParent.parent.kind === SyntaxKind.MappedType) { inferences = append(inferences, stringNumberSymbolType); } // When an 'infer T' declaration is the template of a mapped type, and that mapped type is the extends // clause of a conditional whose check type is also a mapped type, give it a constraint equal to the template // of the check type's mapped type else if ( grandParent.kind === SyntaxKind.MappedType && (grandParent as MappedTypeNode).type && skipParentheses((grandParent as MappedTypeNode).type!) === declaration.parent && grandParent.parent.kind === SyntaxKind.ConditionalType && (grandParent.parent as ConditionalTypeNode).extendsType === grandParent && (grandParent.parent as ConditionalTypeNode).checkType.kind === SyntaxKind.MappedType && ((grandParent.parent as ConditionalTypeNode).checkType as MappedTypeNode).type ) { const checkMappedType = (grandParent.parent as ConditionalTypeNode).checkType as MappedTypeNode; const nodeType = getTypeFromTypeNode(checkMappedType.type!); inferences = append(inferences, instantiateType(nodeType, makeUnaryTypeMapper(getDeclaredTypeOfTypeParameter(getSymbolOfDeclaration(checkMappedType.typeParameter)), checkMappedType.typeParameter.constraint ? getTypeFromTypeNode(checkMappedType.typeParameter.constraint) : stringNumberSymbolType))); } } } } return inferences && getIntersectionType(inferences); } /** This is a worker function. Use getConstraintOfTypeParameter which guards against circular constraints. */ function getConstraintFromTypeParameter(typeParameter: TypeParameter): Type | undefined { if (!typeParameter.constraint) { if (typeParameter.target) { const targetConstraint = getConstraintOfTypeParameter(typeParameter.target); typeParameter.constraint = targetConstraint ? instantiateType(targetConstraint, typeParameter.mapper) : noConstraintType; } else { const constraintDeclaration = getConstraintDeclaration(typeParameter); if (!constraintDeclaration) { typeParameter.constraint = getInferredTypeParameterConstraint(typeParameter) || noConstraintType; } else { let type = getTypeFromTypeNode(constraintDeclaration); if (type.flags & TypeFlags.Any && !isErrorType(type)) { // Allow errorType to propegate to keep downstream errors suppressed // use stringNumberSymbolType as the base constraint for mapped type key constraints (unknown isn;t assignable to that, but `any` was), // use unknown otherwise type = constraintDeclaration.parent.parent.kind === SyntaxKind.MappedType ? stringNumberSymbolType : unknownType; } typeParameter.constraint = type; } } } return typeParameter.constraint === noConstraintType ? undefined : typeParameter.constraint; } function getParentSymbolOfTypeParameter(typeParameter: TypeParameter): Symbol | undefined { const tp = getDeclarationOfKind(typeParameter.symbol, SyntaxKind.TypeParameter)!; const host = isJSDocTemplateTag(tp.parent) ? getEffectiveContainerForJSDocTemplateTag(tp.parent) : tp.parent; return host && getSymbolOfNode(host); } function getTypeListId(types: readonly Type[] | undefined) { let result = ""; if (types) { const length = types.length; let i = 0; while (i < length) { const startId = types[i].id; let count = 1; while (i + count < length && types[i + count].id === startId + count) { count++; } if (result.length) { result += ","; } result += startId; if (count > 1) { result += ":" + count; } i += count; } } return result; } function getAliasId(aliasSymbol: Symbol | undefined, aliasTypeArguments: readonly Type[] | undefined) { return aliasSymbol ? `@${getSymbolId(aliasSymbol)}` + (aliasTypeArguments ? `:${getTypeListId(aliasTypeArguments)}` : "") : ""; } // This function is used to propagate certain flags when creating new object type references and union types. // It is only necessary to do so if a constituent type might be the undefined type, the null type, the type // of an object literal or a non-inferrable type. This is because there are operations in the type checker // that care about the presence of such types at arbitrary depth in a containing type. function getPropagatingFlagsOfTypes(types: readonly Type[], excludeKinds?: TypeFlags): ObjectFlags { let result: ObjectFlags = 0; for (const type of types) { if (excludeKinds === undefined || !(type.flags & excludeKinds)) { result |= getObjectFlags(type); } } return result & ObjectFlags.PropagatingFlags; } function tryCreateTypeReference(target: GenericType, typeArguments: readonly Type[] | undefined): Type { if (some(typeArguments) && target === emptyGenericType) { return unknownType; } return createTypeReference(target, typeArguments); } function createTypeReference(target: GenericType, typeArguments: readonly Type[] | undefined): TypeReference { const id = getTypeListId(typeArguments); let type = target.instantiations.get(id); if (!type) { type = createObjectType(ObjectFlags.Reference, target.symbol) as TypeReference; target.instantiations.set(id, type); type.objectFlags |= typeArguments ? getPropagatingFlagsOfTypes(typeArguments) : 0; type.target = target; type.resolvedTypeArguments = typeArguments; } return type; } function cloneTypeReference(source: TypeReference): TypeReference { const type = createTypeWithSymbol(source.flags, source.symbol) as TypeReference; type.objectFlags = source.objectFlags; type.target = source.target; type.resolvedTypeArguments = source.resolvedTypeArguments; return type; } function createDeferredTypeReference(target: GenericType, node: TypeReferenceNode | ArrayTypeNode | TupleTypeNode, mapper?: TypeMapper, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]): DeferredTypeReference { if (!aliasSymbol) { aliasSymbol = getAliasSymbolForTypeNode(node); const localAliasTypeArguments = getTypeArgumentsForAliasSymbol(aliasSymbol); aliasTypeArguments = mapper ? instantiateTypes(localAliasTypeArguments, mapper) : localAliasTypeArguments; } const type = createObjectType(ObjectFlags.Reference, target.symbol) as DeferredTypeReference; type.target = target; type.node = node; type.mapper = mapper; type.aliasSymbol = aliasSymbol; type.aliasTypeArguments = aliasTypeArguments; return type; } function getTypeArguments(type: TypeReference): readonly Type[] { if (!type.resolvedTypeArguments) { if (!pushTypeResolution(type, TypeSystemPropertyName.ResolvedTypeArguments)) { return concatenate(type.target.outerTypeParameters, type.target.localTypeParameters?.map(() => errorType)) || emptyArray; } const node = type.node; const typeArguments = !node ? emptyArray : node.kind === SyntaxKind.TypeReference ? concatenate(type.target.outerTypeParameters, getEffectiveTypeArguments(node, type.target.localTypeParameters!)) : node.kind === SyntaxKind.ArrayType ? [getTypeFromTypeNode(node.elementType)] : map(node.elements, getTypeFromTypeNode); if (popTypeResolution()) { type.resolvedTypeArguments ??= type.mapper ? instantiateTypes(typeArguments, type.mapper) : typeArguments; } else { type.resolvedTypeArguments ??= concatenate(type.target.outerTypeParameters, type.target.localTypeParameters?.map(() => errorType) || emptyArray); error( type.node || currentNode, type.target.symbol ? Diagnostics.Type_arguments_for_0_circularly_reference_themselves : Diagnostics.Tuple_type_arguments_circularly_reference_themselves, type.target.symbol && symbolToString(type.target.symbol), ); } } return type.resolvedTypeArguments; } function getTypeReferenceArity(type: TypeReference): number { return length(type.target.typeParameters); } /** * Get type from type-reference that reference to class or interface */ function getTypeFromClassOrInterfaceReference(node: NodeWithTypeArguments, symbol: Symbol): Type { const type = getDeclaredTypeOfSymbol(getMergedSymbol(symbol)) as InterfaceType; const typeParameters = type.localTypeParameters; if (typeParameters) { const numTypeArguments = length(node.typeArguments); const minTypeArgumentCount = getMinTypeArgumentCount(typeParameters); const isJs = isInJSFile(node); const isJsImplicitAny = !noImplicitAny && isJs; if (!isJsImplicitAny && (numTypeArguments < minTypeArgumentCount || numTypeArguments > typeParameters.length)) { const missingAugmentsTag = isJs && isExpressionWithTypeArguments(node) && !isJSDocAugmentsTag(node.parent); const diag = minTypeArgumentCount === typeParameters.length ? missingAugmentsTag ? Diagnostics.Expected_0_type_arguments_provide_these_with_an_extends_tag : Diagnostics.Generic_type_0_requires_1_type_argument_s : missingAugmentsTag ? Diagnostics.Expected_0_1_type_arguments_provide_these_with_an_extends_tag : Diagnostics.Generic_type_0_requires_between_1_and_2_type_arguments; const typeStr = typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType); error(node, diag, typeStr, minTypeArgumentCount, typeParameters.length); if (!isJs) { // TODO: Adopt same permissive behavior in TS as in JS to reduce follow-on editing experience failures (requires editing fillMissingTypeArguments) return errorType; } } if (node.kind === SyntaxKind.TypeReference && isDeferredTypeReferenceNode(node as TypeReferenceNode, length(node.typeArguments) !== typeParameters.length)) { return createDeferredTypeReference(type as GenericType, node as TypeReferenceNode, /*mapper*/ undefined); } // In a type reference, the outer type parameters of the referenced class or interface are automatically // supplied as type arguments and the type reference only specifies arguments for the local type parameters // of the class or interface. const typeArguments = concatenate(type.outerTypeParameters, fillMissingTypeArguments(typeArgumentsFromTypeReferenceNode(node), typeParameters, minTypeArgumentCount, isJs)); return createTypeReference(type as GenericType, typeArguments); } return checkNoTypeArguments(node, symbol) ? type : errorType; } function getTypeAliasInstantiation(symbol: Symbol, typeArguments: readonly Type[] | undefined, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]): Type { const type = getDeclaredTypeOfSymbol(symbol); if (type === intrinsicMarkerType) { const typeKind = intrinsicTypeKinds.get(symbol.escapedName as string); if (typeKind !== undefined && typeArguments && typeArguments.length === 1) { return typeKind === IntrinsicTypeKind.NoInfer ? getNoInferType(typeArguments[0]) : getStringMappingType(symbol, typeArguments[0]); } } const links = getSymbolLinks(symbol); const typeParameters = links.typeParameters!; const id = getTypeListId(typeArguments) + getAliasId(aliasSymbol, aliasTypeArguments); let instantiation = links.instantiations!.get(id); if (!instantiation) { links.instantiations!.set(id, instantiation = instantiateTypeWithAlias(type, createTypeMapper(typeParameters, fillMissingTypeArguments(typeArguments, typeParameters, getMinTypeArgumentCount(typeParameters), isInJSFile(symbol.valueDeclaration))), aliasSymbol, aliasTypeArguments)); } return instantiation; } /** * Get type from reference to type alias. When a type alias is generic, the declared type of the type alias may include * references to the type parameters of the alias. We replace those with the actual type arguments by instantiating the * declared type. Instantiations are cached using the type identities of the type arguments as the key. */ function getTypeFromTypeAliasReference(node: NodeWithTypeArguments, symbol: Symbol): Type { if (getCheckFlags(symbol) & CheckFlags.Unresolved) { const typeArguments = typeArgumentsFromTypeReferenceNode(node); const id = getAliasId(symbol, typeArguments); let errorType = errorTypes.get(id); if (!errorType) { errorType = createIntrinsicType(TypeFlags.Any, "error", /*objectFlags*/ undefined, `alias ${id}`); errorType.aliasSymbol = symbol; errorType.aliasTypeArguments = typeArguments; errorTypes.set(id, errorType); } return errorType; } const type = getDeclaredTypeOfSymbol(symbol); const typeParameters = getSymbolLinks(symbol).typeParameters; if (typeParameters) { const numTypeArguments = length(node.typeArguments); const minTypeArgumentCount = getMinTypeArgumentCount(typeParameters); if (numTypeArguments < minTypeArgumentCount || numTypeArguments > typeParameters.length) { error( node, minTypeArgumentCount === typeParameters.length ? Diagnostics.Generic_type_0_requires_1_type_argument_s : Diagnostics.Generic_type_0_requires_between_1_and_2_type_arguments, symbolToString(symbol), minTypeArgumentCount, typeParameters.length, ); return errorType; } // We refrain from associating a local type alias with an instantiation of a top-level type alias // because the local alias may end up being referenced in an inferred return type where it is not // accessible--which in turn may lead to a large structural expansion of the type when generating // a .d.ts file. See #43622 for an example. const aliasSymbol = getAliasSymbolForTypeNode(node); let newAliasSymbol = aliasSymbol && (isLocalTypeAlias(symbol) || !isLocalTypeAlias(aliasSymbol)) ? aliasSymbol : undefined; let aliasTypeArguments: Type[] | undefined; if (newAliasSymbol) { aliasTypeArguments = getTypeArgumentsForAliasSymbol(newAliasSymbol); } else if (isTypeReferenceType(node)) { const aliasSymbol = resolveTypeReferenceName(node, SymbolFlags.Alias, /*ignoreErrors*/ true); // refers to an alias import/export/reexport - by making sure we use the target as an aliasSymbol, // we ensure the exported symbol is used to refer to the type when it's reserialized later if (aliasSymbol && aliasSymbol !== unknownSymbol) { const resolved = resolveAlias(aliasSymbol); if (resolved && resolved.flags & SymbolFlags.TypeAlias) { newAliasSymbol = resolved; aliasTypeArguments = typeArgumentsFromTypeReferenceNode(node) || (typeParameters ? [] : undefined); } } } return getTypeAliasInstantiation(symbol, typeArgumentsFromTypeReferenceNode(node), newAliasSymbol, aliasTypeArguments); } return checkNoTypeArguments(node, symbol) ? type : errorType; } function isLocalTypeAlias(symbol: Symbol) { const declaration = symbol.declarations?.find(isTypeAlias); return !!(declaration && getContainingFunction(declaration)); } function getTypeReferenceName(node: TypeReferenceType): EntityNameOrEntityNameExpression | undefined { switch (node.kind) { case SyntaxKind.TypeReference: return node.typeName; case SyntaxKind.ExpressionWithTypeArguments: // We only support expressions that are simple qualified names. For other // expressions this produces undefined. const expr = node.expression; if (isEntityNameExpression(expr)) { return expr; } // fall through; } return undefined; } function getSymbolPath(symbol: Symbol): string { return symbol.parent ? `${getSymbolPath(symbol.parent)}.${symbol.escapedName}` : symbol.escapedName as string; } function getUnresolvedSymbolForEntityName(name: EntityNameOrEntityNameExpression) { const identifier = name.kind === SyntaxKind.QualifiedName ? name.right : name.kind === SyntaxKind.PropertyAccessExpression ? name.name : name; const text = identifier.escapedText; if (text) { const parentSymbol = name.kind === SyntaxKind.QualifiedName ? getUnresolvedSymbolForEntityName(name.left) : name.kind === SyntaxKind.PropertyAccessExpression ? getUnresolvedSymbolForEntityName(name.expression) : undefined; const path = parentSymbol ? `${getSymbolPath(parentSymbol)}.${text}` : text as string; let result = unresolvedSymbols.get(path); if (!result) { unresolvedSymbols.set(path, result = createSymbol(SymbolFlags.TypeAlias, text, CheckFlags.Unresolved)); result.parent = parentSymbol; result.links.declaredType = unresolvedType; } return result; } return unknownSymbol; } function resolveTypeReferenceName(typeReference: TypeReferenceType, meaning: SymbolFlags, ignoreErrors?: boolean) { const name = getTypeReferenceName(typeReference); if (!name) { return unknownSymbol; } const symbol = resolveEntityName(name, meaning, ignoreErrors); return symbol && symbol !== unknownSymbol ? symbol : ignoreErrors ? unknownSymbol : getUnresolvedSymbolForEntityName(name); } function getTypeReferenceType(node: NodeWithTypeArguments, symbol: Symbol): Type { if (symbol === unknownSymbol) { return errorType; } symbol = getExpandoSymbol(symbol) || symbol; if (symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { return getTypeFromClassOrInterfaceReference(node, symbol); } if (symbol.flags & SymbolFlags.TypeAlias) { return getTypeFromTypeAliasReference(node, symbol); } // Get type from reference to named type that cannot be generic (enum or type parameter) const res = tryGetDeclaredTypeOfSymbol(symbol); if (res) { return checkNoTypeArguments(node, symbol) ? getRegularTypeOfLiteralType(res) : errorType; } if (symbol.flags & SymbolFlags.Value && isJSDocTypeReference(node)) { const jsdocType = getTypeFromJSDocValueReference(node, symbol); if (jsdocType) { return jsdocType; } else { // Resolve the type reference as a Type for the purpose of reporting errors. resolveTypeReferenceName(node, SymbolFlags.Type); return getTypeOfSymbol(symbol); } } return errorType; } /** * A JSdoc TypeReference may be to a value, but resolve it as a type anyway. * Example: import('./b').ConstructorFunction */ function getTypeFromJSDocValueReference(node: NodeWithTypeArguments, symbol: Symbol): Type | undefined { const links = getNodeLinks(node); if (!links.resolvedJSDocType) { const valueType = getTypeOfSymbol(symbol); let typeType = valueType; if (symbol.valueDeclaration) { const isImportTypeWithQualifier = node.kind === SyntaxKind.ImportType && (node as ImportTypeNode).qualifier; // valueType might not have a symbol, eg, {import('./b').STRING_LITERAL} if (valueType.symbol && valueType.symbol !== symbol && isImportTypeWithQualifier) { typeType = getTypeReferenceType(node, valueType.symbol); } } links.resolvedJSDocType = typeType; } return links.resolvedJSDocType; } function getNoInferType(type: Type) { return isNoInferTargetType(type) ? getOrCreateSubstitutionType(type, unknownType) : type; } function isNoInferTargetType(type: Type): boolean { // This is effectively a more conservative and predictable form of couldContainTypeVariables. We want to // preserve NoInfer only for types that could contain type variables, but we don't want to exhaustively // examine all object type members. return !!(type.flags & TypeFlags.UnionOrIntersection && some((type as UnionOrIntersectionType).types, isNoInferTargetType) || type.flags & TypeFlags.Substitution && !isNoInferType(type) && isNoInferTargetType((type as SubstitutionType).baseType) || type.flags & TypeFlags.Object && !isEmptyAnonymousObjectType(type) || type.flags & (TypeFlags.Instantiable & ~TypeFlags.Substitution) && !isPatternLiteralType(type)); } function isNoInferType(type: Type) { // A NoInfer type is represented as a substitution type with a TypeFlags.Unknown constraint. return !!(type.flags & TypeFlags.Substitution && (type as SubstitutionType).constraint.flags & TypeFlags.Unknown); } function getSubstitutionType(baseType: Type, constraint: Type) { return constraint.flags & TypeFlags.AnyOrUnknown || constraint === baseType || baseType.flags & TypeFlags.Any ? baseType : getOrCreateSubstitutionType(baseType, constraint); } function getOrCreateSubstitutionType(baseType: Type, constraint: Type) { const id = `${getTypeId(baseType)}>${getTypeId(constraint)}`; const cached = substitutionTypes.get(id); if (cached) { return cached; } const result = createType(TypeFlags.Substitution) as SubstitutionType; result.baseType = baseType; result.constraint = constraint; substitutionTypes.set(id, result); return result; } function getSubstitutionIntersection(substitutionType: SubstitutionType) { return isNoInferType(substitutionType) ? substitutionType.baseType : getIntersectionType([substitutionType.constraint, substitutionType.baseType]); } function isUnaryTupleTypeNode(node: TypeNode) { return node.kind === SyntaxKind.TupleType && (node as TupleTypeNode).elements.length === 1; } function getImpliedConstraint(type: Type, checkNode: TypeNode, extendsNode: TypeNode): Type | undefined { return isUnaryTupleTypeNode(checkNode) && isUnaryTupleTypeNode(extendsNode) ? getImpliedConstraint(type, (checkNode as TupleTypeNode).elements[0], (extendsNode as TupleTypeNode).elements[0]) : getActualTypeVariable(getTypeFromTypeNode(checkNode)) === getActualTypeVariable(type) ? getTypeFromTypeNode(extendsNode) : undefined; } function getConditionalFlowTypeOfType(type: Type, node: Node) { let constraints: Type[] | undefined; let covariant = true; while (node && !isStatement(node) && node.kind !== SyntaxKind.JSDoc) { const parent = node.parent; // only consider variance flipped by parameter locations - `keyof` types would usually be considered variance inverting, but // often get used in indexed accesses where they behave sortof invariantly, but our checking is lax if (parent.kind === SyntaxKind.Parameter) { covariant = !covariant; } // Always substitute on type parameters, regardless of variance, since even // in contravariant positions, they may rely on substituted constraints to be valid if ((covariant || type.flags & TypeFlags.TypeVariable) && parent.kind === SyntaxKind.ConditionalType && node === (parent as ConditionalTypeNode).trueType) { const constraint = getImpliedConstraint(type, (parent as ConditionalTypeNode).checkType, (parent as ConditionalTypeNode).extendsType); if (constraint) { constraints = append(constraints, constraint); } } // Given a homomorphic mapped type { [K in keyof T]: XXX }, where T is constrained to an array or tuple type, in the // template type XXX, K has an added constraint of number | `${number}`. else if (type.flags & TypeFlags.TypeParameter && parent.kind === SyntaxKind.MappedType && !(parent as MappedTypeNode).nameType && node === (parent as MappedTypeNode).type) { const mappedType = getTypeFromTypeNode(parent as TypeNode) as MappedType; if (getTypeParameterFromMappedType(mappedType) === getActualTypeVariable(type)) { const typeParameter = getHomomorphicTypeVariable(mappedType); if (typeParameter) { const constraint = getConstraintOfTypeParameter(typeParameter); if (constraint && everyType(constraint, isArrayOrTupleType)) { constraints = append(constraints, getUnionType([numberType, numericStringType])); } } } } node = parent; } return constraints ? getSubstitutionType(type, getIntersectionType(constraints)) : type; } function isJSDocTypeReference(node: Node): node is TypeReferenceNode { return !!(node.flags & NodeFlags.JSDoc) && (node.kind === SyntaxKind.TypeReference || node.kind === SyntaxKind.ImportType); } function checkNoTypeArguments(node: NodeWithTypeArguments, symbol?: Symbol) { if (node.typeArguments) { error(node, Diagnostics.Type_0_is_not_generic, symbol ? symbolToString(symbol) : (node as TypeReferenceNode).typeName ? declarationNameToString((node as TypeReferenceNode).typeName) : anon); return false; } return true; } function getIntendedTypeFromJSDocTypeReference(node: TypeReferenceNode): Type | undefined { if (isIdentifier(node.typeName)) { const typeArgs = node.typeArguments; switch (node.typeName.escapedText) { case "String": checkNoTypeArguments(node); return stringType; case "Number": checkNoTypeArguments(node); return numberType; case "Boolean": checkNoTypeArguments(node); return booleanType; case "Void": checkNoTypeArguments(node); return voidType; case "Undefined": checkNoTypeArguments(node); return undefinedType; case "Null": checkNoTypeArguments(node); return nullType; case "Function": case "function": checkNoTypeArguments(node); return globalFunctionType; case "array": return (!typeArgs || !typeArgs.length) && !noImplicitAny ? anyArrayType : undefined; case "promise": return (!typeArgs || !typeArgs.length) && !noImplicitAny ? createPromiseType(anyType) : undefined; case "Object": if (typeArgs && typeArgs.length === 2) { if (isJSDocIndexSignature(node)) { const indexed = getTypeFromTypeNode(typeArgs[0]); const target = getTypeFromTypeNode(typeArgs[1]); const indexInfo = indexed === stringType || indexed === numberType ? [createIndexInfo(indexed, target, /*isReadonly*/ false)] : emptyArray; return createAnonymousType(/*symbol*/ undefined, emptySymbols, emptyArray, emptyArray, indexInfo); } return anyType; } checkNoTypeArguments(node); return !noImplicitAny ? anyType : undefined; } } } function getTypeFromJSDocNullableTypeNode(node: JSDocNullableType) { const type = getTypeFromTypeNode(node.type); return strictNullChecks ? getNullableType(type, TypeFlags.Null) : type; } function getTypeFromTypeReference(node: TypeReferenceType): Type { const links = getNodeLinks(node); if (!links.resolvedType) { // handle LS queries on the `const` in `x as const` by resolving to the type of `x` if (isConstTypeReference(node) && isAssertionExpression(node.parent)) { links.resolvedSymbol = unknownSymbol; return links.resolvedType = checkExpressionCached(node.parent.expression); } let symbol: Symbol | undefined; let type: Type | undefined; const meaning = SymbolFlags.Type; if (isJSDocTypeReference(node)) { type = getIntendedTypeFromJSDocTypeReference(node); if (!type) { symbol = resolveTypeReferenceName(node, meaning, /*ignoreErrors*/ true); if (symbol === unknownSymbol) { symbol = resolveTypeReferenceName(node, meaning | SymbolFlags.Value); } else { resolveTypeReferenceName(node, meaning); // Resolve again to mark errors, if any } type = getTypeReferenceType(node, symbol); } } if (!type) { symbol = resolveTypeReferenceName(node, meaning); type = getTypeReferenceType(node, symbol); } // Cache both the resolved symbol and the resolved type. The resolved symbol is needed when we check the // type reference in checkTypeReferenceNode. links.resolvedSymbol = symbol; links.resolvedType = type; } return links.resolvedType; } function typeArgumentsFromTypeReferenceNode(node: NodeWithTypeArguments): Type[] | undefined { return map(node.typeArguments, getTypeFromTypeNode); } function getTypeFromTypeQueryNode(node: TypeQueryNode): Type { const links = getNodeLinks(node); if (!links.resolvedType) { // TypeScript 1.0 spec (April 2014): 3.6.3 // The expression is processed as an identifier expression (section 4.3) // or property access expression(section 4.10), // the widened type(section 3.9) of which becomes the result. const type = checkExpressionWithTypeArguments(node); links.resolvedType = getRegularTypeOfLiteralType(getWidenedType(type)); } return links.resolvedType; } function getTypeOfGlobalSymbol(symbol: Symbol | undefined, arity: number): ObjectType { function getTypeDeclaration(symbol: Symbol): Declaration | undefined { const declarations = symbol.declarations; if (declarations) { for (const declaration of declarations) { switch (declaration.kind) { case SyntaxKind.ClassDeclaration: case SyntaxKind.InterfaceDeclaration: case SyntaxKind.EnumDeclaration: return declaration; } } } } if (!symbol) { return arity ? emptyGenericType : emptyObjectType; } const type = getDeclaredTypeOfSymbol(symbol); if (!(type.flags & TypeFlags.Object)) { error(getTypeDeclaration(symbol), Diagnostics.Global_type_0_must_be_a_class_or_interface_type, symbolName(symbol)); return arity ? emptyGenericType : emptyObjectType; } if (length((type as InterfaceType).typeParameters) !== arity) { error(getTypeDeclaration(symbol), Diagnostics.Global_type_0_must_have_1_type_parameter_s, symbolName(symbol), arity); return arity ? emptyGenericType : emptyObjectType; } return type as ObjectType; } function getGlobalValueSymbol(name: __String, reportErrors: boolean): Symbol | undefined { return getGlobalSymbol(name, SymbolFlags.Value, reportErrors ? Diagnostics.Cannot_find_global_value_0 : undefined); } function getGlobalTypeSymbol(name: __String, reportErrors: boolean): Symbol | undefined { return getGlobalSymbol(name, SymbolFlags.Type, reportErrors ? Diagnostics.Cannot_find_global_type_0 : undefined); } function getGlobalTypeAliasSymbol(name: __String, arity: number, reportErrors: boolean): Symbol | undefined { const symbol = getGlobalSymbol(name, SymbolFlags.Type, reportErrors ? Diagnostics.Cannot_find_global_type_0 : undefined); if (symbol) { // Resolve the declared type of the symbol. This resolves type parameters for the type // alias so that we can check arity. getDeclaredTypeOfSymbol(symbol); if (length(getSymbolLinks(symbol).typeParameters) !== arity) { const decl = symbol.declarations && find(symbol.declarations, isTypeAliasDeclaration); error(decl, Diagnostics.Global_type_0_must_have_1_type_parameter_s, symbolName(symbol), arity); return undefined; } } return symbol; } function getGlobalSymbol(name: __String, meaning: SymbolFlags, diagnostic: DiagnosticMessage | undefined): Symbol | undefined { // Don't track references for global symbols anyway, so value if `isReference` is arbitrary return resolveName(/*location*/ undefined, name, meaning, diagnostic, /*isUse*/ false, /*excludeGlobals*/ false); } function getGlobalType(name: __String, arity: 0, reportErrors: true): ObjectType; function getGlobalType(name: __String, arity: 0, reportErrors: boolean): ObjectType | undefined; function getGlobalType(name: __String, arity: number, reportErrors: true): GenericType; function getGlobalType(name: __String, arity: number, reportErrors: boolean): GenericType | undefined; function getGlobalType(name: __String, arity: number, reportErrors: boolean): ObjectType | undefined { const symbol = getGlobalTypeSymbol(name, reportErrors); return symbol || reportErrors ? getTypeOfGlobalSymbol(symbol, arity) : undefined; } function getGlobalBuiltinTypes(typeNames: readonly string[], arity: 0): ObjectType[]; function getGlobalBuiltinTypes(typeNames: readonly string[], arity: number): GenericType[]; function getGlobalBuiltinTypes(typeNames: readonly string[], arity: number) { let types: Type[] | undefined; for (const typeName of typeNames) { types = append(types, getGlobalType(typeName as __String, arity, /*reportErrors*/ false)); } return types ?? emptyArray; } function getGlobalTypedPropertyDescriptorType() { // We always report an error, so store a result in the event we could not resolve the symbol to prevent reporting it multiple times return deferredGlobalTypedPropertyDescriptorType ||= getGlobalType("TypedPropertyDescriptor" as __String, /*arity*/ 1, /*reportErrors*/ true) || emptyGenericType; } function getGlobalTemplateStringsArrayType() { // We always report an error, so store a result in the event we could not resolve the symbol to prevent reporting it multiple times return deferredGlobalTemplateStringsArrayType ||= getGlobalType("TemplateStringsArray" as __String, /*arity*/ 0, /*reportErrors*/ true) || emptyObjectType; } function getGlobalImportMetaType() { // We always report an error, so store a result in the event we could not resolve the symbol to prevent reporting it multiple times return deferredGlobalImportMetaType ||= getGlobalType("ImportMeta" as __String, /*arity*/ 0, /*reportErrors*/ true) || emptyObjectType; } function getGlobalImportMetaExpressionType() { if (!deferredGlobalImportMetaExpressionType) { // Create a synthetic type `ImportMetaExpression { meta: MetaProperty }` const symbol = createSymbol(SymbolFlags.None, "ImportMetaExpression" as __String); const importMetaType = getGlobalImportMetaType(); const metaPropertySymbol = createSymbol(SymbolFlags.Property, "meta" as __String, CheckFlags.Readonly); metaPropertySymbol.parent = symbol; metaPropertySymbol.links.type = importMetaType; const members = createSymbolTable([metaPropertySymbol]); symbol.members = members; deferredGlobalImportMetaExpressionType = createAnonymousType(symbol, members, emptyArray, emptyArray, emptyArray); } return deferredGlobalImportMetaExpressionType; } function getGlobalImportCallOptionsType(reportErrors: boolean) { return (deferredGlobalImportCallOptionsType ||= getGlobalType("ImportCallOptions" as __String, /*arity*/ 0, reportErrors)) || emptyObjectType; } function getGlobalImportAttributesType(reportErrors: boolean) { return (deferredGlobalImportAttributesType ||= getGlobalType("ImportAttributes" as __String, /*arity*/ 0, reportErrors)) || emptyObjectType; } function getGlobalESSymbolConstructorSymbol(reportErrors: boolean): Symbol | undefined { return deferredGlobalESSymbolConstructorSymbol ||= getGlobalValueSymbol("Symbol" as __String, reportErrors); } function getGlobalESSymbolConstructorTypeSymbol(reportErrors: boolean): Symbol | undefined { return deferredGlobalESSymbolConstructorTypeSymbol ||= getGlobalTypeSymbol("SymbolConstructor" as __String, reportErrors); } function getGlobalESSymbolType() { return (deferredGlobalESSymbolType ||= getGlobalType("Symbol" as __String, /*arity*/ 0, /*reportErrors*/ false)) || emptyObjectType; } function getGlobalPromiseType(reportErrors: boolean) { return (deferredGlobalPromiseType ||= getGlobalType("Promise" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType; } function getGlobalPromiseLikeType(reportErrors: boolean) { return (deferredGlobalPromiseLikeType ||= getGlobalType("PromiseLike" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType; } function getGlobalPromiseConstructorSymbol(reportErrors: boolean): Symbol | undefined { return deferredGlobalPromiseConstructorSymbol ||= getGlobalValueSymbol("Promise" as __String, reportErrors); } function getGlobalPromiseConstructorLikeType(reportErrors: boolean) { return (deferredGlobalPromiseConstructorLikeType ||= getGlobalType("PromiseConstructorLike" as __String, /*arity*/ 0, reportErrors)) || emptyObjectType; } function getGlobalAsyncIterableType(reportErrors: boolean) { return (deferredGlobalAsyncIterableType ||= getGlobalType("AsyncIterable" as __String, /*arity*/ 3, reportErrors)) || emptyGenericType; } function getGlobalAsyncIteratorType(reportErrors: boolean) { return (deferredGlobalAsyncIteratorType ||= getGlobalType("AsyncIterator" as __String, /*arity*/ 3, reportErrors)) || emptyGenericType; } function getGlobalAsyncIterableIteratorType(reportErrors: boolean) { return (deferredGlobalAsyncIterableIteratorType ||= getGlobalType("AsyncIterableIterator" as __String, /*arity*/ 3, reportErrors)) || emptyGenericType; } function getGlobalBuiltinAsyncIteratorTypes() { // NOTE: This list does not include all built-in async iterator types, only those that are likely to be encountered frequently. return deferredGlobalBuiltinAsyncIteratorTypes ??= getGlobalBuiltinTypes(["ReadableStreamAsyncIterator"], 1); } function getGlobalAsyncIteratorObjectType(reportErrors: boolean) { return (deferredGlobalAsyncIteratorObjectType ||= getGlobalType("AsyncIteratorObject" as __String, /*arity*/ 3, reportErrors)) || emptyGenericType; } function getGlobalAsyncGeneratorType(reportErrors: boolean) { return (deferredGlobalAsyncGeneratorType ||= getGlobalType("AsyncGenerator" as __String, /*arity*/ 3, reportErrors)) || emptyGenericType; } function getGlobalIterableType(reportErrors: boolean) { return (deferredGlobalIterableType ||= getGlobalType("Iterable" as __String, /*arity*/ 3, reportErrors)) || emptyGenericType; } function getGlobalIteratorType(reportErrors: boolean) { return (deferredGlobalIteratorType ||= getGlobalType("Iterator" as __String, /*arity*/ 3, reportErrors)) || emptyGenericType; } function getGlobalIterableIteratorType(reportErrors: boolean) { return (deferredGlobalIterableIteratorType ||= getGlobalType("IterableIterator" as __String, /*arity*/ 3, reportErrors)) || emptyGenericType; } function getBuiltinIteratorReturnType() { return strictBuiltinIteratorReturn ? undefinedType : anyType; } function getGlobalBuiltinIteratorTypes() { // NOTE: This list does not include all built-in iterator types, only those that are likely to be encountered frequently. return deferredGlobalBuiltinIteratorTypes ??= getGlobalBuiltinTypes(["ArrayIterator", "MapIterator", "SetIterator", "StringIterator"], 1); } function getGlobalIteratorObjectType(reportErrors: boolean) { return (deferredGlobalIteratorObjectType ||= getGlobalType("IteratorObject" as __String, /*arity*/ 3, reportErrors)) || emptyGenericType; } function getGlobalGeneratorType(reportErrors: boolean) { return (deferredGlobalGeneratorType ||= getGlobalType("Generator" as __String, /*arity*/ 3, reportErrors)) || emptyGenericType; } function getGlobalIteratorYieldResultType(reportErrors: boolean) { return (deferredGlobalIteratorYieldResultType ||= getGlobalType("IteratorYieldResult" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType; } function getGlobalIteratorReturnResultType(reportErrors: boolean) { return (deferredGlobalIteratorReturnResultType ||= getGlobalType("IteratorReturnResult" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType; } function getGlobalDisposableType(reportErrors: boolean) { return (deferredGlobalDisposableType ||= getGlobalType("Disposable" as __String, /*arity*/ 0, reportErrors)) || emptyObjectType; } function getGlobalAsyncDisposableType(reportErrors: boolean) { return (deferredGlobalAsyncDisposableType ||= getGlobalType("AsyncDisposable" as __String, /*arity*/ 0, reportErrors)) || emptyObjectType; } function getGlobalTypeOrUndefined(name: __String, arity = 0): ObjectType | undefined { const symbol = getGlobalSymbol(name, SymbolFlags.Type, /*diagnostic*/ undefined); return symbol && getTypeOfGlobalSymbol(symbol, arity) as GenericType; } function getGlobalExtractSymbol(): Symbol | undefined { // We always report an error, so cache a result in the event we could not resolve the symbol to prevent reporting it multiple times deferredGlobalExtractSymbol ||= getGlobalTypeAliasSymbol("Extract" as __String, /*arity*/ 2, /*reportErrors*/ true) || unknownSymbol; return deferredGlobalExtractSymbol === unknownSymbol ? undefined : deferredGlobalExtractSymbol; } function getGlobalOmitSymbol(): Symbol | undefined { // We always report an error, so cache a result in the event we could not resolve the symbol to prevent reporting it multiple times deferredGlobalOmitSymbol ||= getGlobalTypeAliasSymbol("Omit" as __String, /*arity*/ 2, /*reportErrors*/ true) || unknownSymbol; return deferredGlobalOmitSymbol === unknownSymbol ? undefined : deferredGlobalOmitSymbol; } function getGlobalAwaitedSymbol(reportErrors: boolean): Symbol | undefined { // Only cache `unknownSymbol` if we are reporting errors so that we don't report the error more than once. deferredGlobalAwaitedSymbol ||= getGlobalTypeAliasSymbol("Awaited" as __String, /*arity*/ 1, reportErrors) || (reportErrors ? unknownSymbol : undefined); return deferredGlobalAwaitedSymbol === unknownSymbol ? undefined : deferredGlobalAwaitedSymbol; } function getGlobalBigIntType() { return (deferredGlobalBigIntType ||= getGlobalType("BigInt" as __String, /*arity*/ 0, /*reportErrors*/ false)) || emptyObjectType; } function getGlobalClassDecoratorContextType(reportErrors: boolean) { return (deferredGlobalClassDecoratorContextType ??= getGlobalType("ClassDecoratorContext" as __String, /*arity*/ 1, reportErrors)) ?? emptyGenericType; } function getGlobalClassMethodDecoratorContextType(reportErrors: boolean) { return (deferredGlobalClassMethodDecoratorContextType ??= getGlobalType("ClassMethodDecoratorContext" as __String, /*arity*/ 2, reportErrors)) ?? emptyGenericType; } function getGlobalClassGetterDecoratorContextType(reportErrors: boolean) { return (deferredGlobalClassGetterDecoratorContextType ??= getGlobalType("ClassGetterDecoratorContext" as __String, /*arity*/ 2, reportErrors)) ?? emptyGenericType; } function getGlobalClassSetterDecoratorContextType(reportErrors: boolean) { return (deferredGlobalClassSetterDecoratorContextType ??= getGlobalType("ClassSetterDecoratorContext" as __String, /*arity*/ 2, reportErrors)) ?? emptyGenericType; } function getGlobalClassAccessorDecoratorContextType(reportErrors: boolean) { return (deferredGlobalClassAccessorDecoratorContextType ??= getGlobalType("ClassAccessorDecoratorContext" as __String, /*arity*/ 2, reportErrors)) ?? emptyGenericType; } function getGlobalClassAccessorDecoratorTargetType(reportErrors: boolean) { return (deferredGlobalClassAccessorDecoratorTargetType ??= getGlobalType("ClassAccessorDecoratorTarget" as __String, /*arity*/ 2, reportErrors)) ?? emptyGenericType; } function getGlobalClassAccessorDecoratorResultType(reportErrors: boolean) { return (deferredGlobalClassAccessorDecoratorResultType ??= getGlobalType("ClassAccessorDecoratorResult" as __String, /*arity*/ 2, reportErrors)) ?? emptyGenericType; } function getGlobalClassFieldDecoratorContextType(reportErrors: boolean) { return (deferredGlobalClassFieldDecoratorContextType ??= getGlobalType("ClassFieldDecoratorContext" as __String, /*arity*/ 2, reportErrors)) ?? emptyGenericType; } function getGlobalNaNSymbol(): Symbol | undefined { return (deferredGlobalNaNSymbol ||= getGlobalValueSymbol("NaN" as __String, /*reportErrors*/ false)); } function getGlobalRecordSymbol(): Symbol | undefined { deferredGlobalRecordSymbol ||= getGlobalTypeAliasSymbol("Record" as __String, /*arity*/ 2, /*reportErrors*/ true) || unknownSymbol; return deferredGlobalRecordSymbol === unknownSymbol ? undefined : deferredGlobalRecordSymbol; } /** * Instantiates a global type that is generic with some element type, and returns that instantiation. */ function createTypeFromGenericGlobalType(genericGlobalType: GenericType, typeArguments: readonly Type[]): ObjectType { return genericGlobalType !== emptyGenericType ? createTypeReference(genericGlobalType, typeArguments) : emptyObjectType; } function createTypedPropertyDescriptorType(propertyType: Type): Type { return createTypeFromGenericGlobalType(getGlobalTypedPropertyDescriptorType(), [propertyType]); } function createIterableType(iteratedType: Type): Type { return createTypeFromGenericGlobalType(getGlobalIterableType(/*reportErrors*/ true), [iteratedType, voidType, undefinedType]); } function createArrayType(elementType: Type, readonly?: boolean): ObjectType { return createTypeFromGenericGlobalType(readonly ? globalReadonlyArrayType : globalArrayType, [elementType]); } function getTupleElementFlags(node: TypeNode) { switch (node.kind) { case SyntaxKind.OptionalType: return ElementFlags.Optional; case SyntaxKind.RestType: return getRestTypeElementFlags(node as RestTypeNode); case SyntaxKind.NamedTupleMember: return (node as NamedTupleMember).questionToken ? ElementFlags.Optional : (node as NamedTupleMember).dotDotDotToken ? getRestTypeElementFlags(node as NamedTupleMember) : ElementFlags.Required; default: return ElementFlags.Required; } } function getRestTypeElementFlags(node: RestTypeNode | NamedTupleMember) { return getArrayElementTypeNode(node.type) ? ElementFlags.Rest : ElementFlags.Variadic; } function getArrayOrTupleTargetType(node: ArrayTypeNode | TupleTypeNode): GenericType { const readonly = isReadonlyTypeOperator(node.parent); const elementType = getArrayElementTypeNode(node); if (elementType) { return readonly ? globalReadonlyArrayType : globalArrayType; } const elementFlags = map((node as TupleTypeNode).elements, getTupleElementFlags); return getTupleTargetType(elementFlags, readonly, map((node as TupleTypeNode).elements, memberIfLabeledElementDeclaration)); } function memberIfLabeledElementDeclaration(member: Node): NamedTupleMember | ParameterDeclaration | undefined { return isNamedTupleMember(member) || isParameter(member) ? member : undefined; } // Return true if the given type reference node is directly aliased or if it needs to be deferred // because it is possibly contained in a circular chain of eagerly resolved types. function isDeferredTypeReferenceNode(node: TypeReferenceNode | ArrayTypeNode | TupleTypeNode, hasDefaultTypeArguments?: boolean) { return !!getAliasSymbolForTypeNode(node) || isResolvedByTypeAlias(node) && ( node.kind === SyntaxKind.ArrayType ? mayResolveTypeAlias(node.elementType) : node.kind === SyntaxKind.TupleType ? some(node.elements, mayResolveTypeAlias) : hasDefaultTypeArguments || some(node.typeArguments, mayResolveTypeAlias) ); } // Return true when the given node is transitively contained in type constructs that eagerly // resolve their constituent types. We include SyntaxKind.TypeReference because type arguments // of type aliases are eagerly resolved. function isResolvedByTypeAlias(node: Node): boolean { const parent = node.parent; switch (parent.kind) { case SyntaxKind.ParenthesizedType: case SyntaxKind.NamedTupleMember: case SyntaxKind.TypeReference: case SyntaxKind.UnionType: case SyntaxKind.IntersectionType: case SyntaxKind.IndexedAccessType: case SyntaxKind.ConditionalType: case SyntaxKind.TypeOperator: case SyntaxKind.ArrayType: case SyntaxKind.TupleType: return isResolvedByTypeAlias(parent); case SyntaxKind.TypeAliasDeclaration: return true; } return false; } // Return true if resolving the given node (i.e. getTypeFromTypeNode) possibly causes resolution // of a type alias. function mayResolveTypeAlias(node: Node): boolean { switch (node.kind) { case SyntaxKind.TypeReference: return isJSDocTypeReference(node) || !!(resolveTypeReferenceName(node as TypeReferenceNode, SymbolFlags.Type).flags & SymbolFlags.TypeAlias); case SyntaxKind.TypeQuery: return true; case SyntaxKind.TypeOperator: return (node as TypeOperatorNode).operator !== SyntaxKind.UniqueKeyword && mayResolveTypeAlias((node as TypeOperatorNode).type); case SyntaxKind.ParenthesizedType: case SyntaxKind.OptionalType: case SyntaxKind.NamedTupleMember: case SyntaxKind.JSDocOptionalType: case SyntaxKind.JSDocNullableType: case SyntaxKind.JSDocNonNullableType: case SyntaxKind.JSDocTypeExpression: return mayResolveTypeAlias((node as ParenthesizedTypeNode | OptionalTypeNode | JSDocTypeReferencingNode | NamedTupleMember).type); case SyntaxKind.RestType: return (node as RestTypeNode).type.kind !== SyntaxKind.ArrayType || mayResolveTypeAlias(((node as RestTypeNode).type as ArrayTypeNode).elementType); case SyntaxKind.UnionType: case SyntaxKind.IntersectionType: return some((node as UnionOrIntersectionTypeNode).types, mayResolveTypeAlias); case SyntaxKind.IndexedAccessType: return mayResolveTypeAlias((node as IndexedAccessTypeNode).objectType) || mayResolveTypeAlias((node as IndexedAccessTypeNode).indexType); case SyntaxKind.ConditionalType: return mayResolveTypeAlias((node as ConditionalTypeNode).checkType) || mayResolveTypeAlias((node as ConditionalTypeNode).extendsType) || mayResolveTypeAlias((node as ConditionalTypeNode).trueType) || mayResolveTypeAlias((node as ConditionalTypeNode).falseType); } return false; } function getTypeFromArrayOrTupleTypeNode(node: ArrayTypeNode | TupleTypeNode): Type { const links = getNodeLinks(node); if (!links.resolvedType) { const target = getArrayOrTupleTargetType(node); if (target === emptyGenericType) { links.resolvedType = emptyObjectType; } else if (!(node.kind === SyntaxKind.TupleType && some(node.elements, e => !!(getTupleElementFlags(e) & ElementFlags.Variadic))) && isDeferredTypeReferenceNode(node)) { links.resolvedType = node.kind === SyntaxKind.TupleType && node.elements.length === 0 ? target : createDeferredTypeReference(target, node, /*mapper*/ undefined); } else { const elementTypes = node.kind === SyntaxKind.ArrayType ? [getTypeFromTypeNode(node.elementType)] : map(node.elements, getTypeFromTypeNode); links.resolvedType = createNormalizedTypeReference(target, elementTypes); } } return links.resolvedType; } function isReadonlyTypeOperator(node: Node) { return isTypeOperatorNode(node) && node.operator === SyntaxKind.ReadonlyKeyword; } function createTupleType(elementTypes: readonly Type[], elementFlags?: readonly ElementFlags[], readonly = false, namedMemberDeclarations: readonly (NamedTupleMember | ParameterDeclaration | undefined)[] = []) { const tupleTarget = getTupleTargetType(elementFlags || map(elementTypes, _ => ElementFlags.Required), readonly, namedMemberDeclarations); return tupleTarget === emptyGenericType ? emptyObjectType : elementTypes.length ? createNormalizedTypeReference(tupleTarget, elementTypes) : tupleTarget; } function getTupleTargetType(elementFlags: readonly ElementFlags[], readonly: boolean, namedMemberDeclarations: readonly (NamedTupleMember | ParameterDeclaration | undefined)[]): GenericType { if (elementFlags.length === 1 && elementFlags[0] & ElementFlags.Rest) { // [...X[]] is equivalent to just X[] return readonly ? globalReadonlyArrayType : globalArrayType; } const key = map(elementFlags, f => f & ElementFlags.Required ? "#" : f & ElementFlags.Optional ? "?" : f & ElementFlags.Rest ? "." : "*").join() + (readonly ? "R" : "") + (some(namedMemberDeclarations, node => !!node) ? "," + map(namedMemberDeclarations, node => node ? getNodeId(node) : "_").join(",") : ""); let type = tupleTypes.get(key); if (!type) { tupleTypes.set(key, type = createTupleTargetType(elementFlags, readonly, namedMemberDeclarations)); } return type; } // We represent tuple types as type references to synthesized generic interface types created by // this function. The types are of the form: // // interface Tuple extends Array { 0: T0, 1: T1, 2: T2, ... } // // Note that the generic type created by this function has no symbol associated with it. The same // is true for each of the synthesized type parameters. function createTupleTargetType(elementFlags: readonly ElementFlags[], readonly: boolean, namedMemberDeclarations: readonly (NamedTupleMember | ParameterDeclaration | undefined)[]): TupleType { const arity = elementFlags.length; const minLength = countWhere(elementFlags, f => !!(f & (ElementFlags.Required | ElementFlags.Variadic))); let typeParameters: TypeParameter[] | undefined; const properties: Symbol[] = []; let combinedFlags = 0 as ElementFlags; if (arity) { typeParameters = new Array(arity); for (let i = 0; i < arity; i++) { const typeParameter = typeParameters[i] = createTypeParameter(); const flags = elementFlags[i]; combinedFlags |= flags; if (!(combinedFlags & ElementFlags.Variable)) { const property = createSymbol(SymbolFlags.Property | (flags & ElementFlags.Optional ? SymbolFlags.Optional : 0), "" + i as __String, readonly ? CheckFlags.Readonly : 0); property.links.tupleLabelDeclaration = namedMemberDeclarations?.[i]; property.links.type = typeParameter; properties.push(property); } } } const fixedLength = properties.length; const lengthSymbol = createSymbol(SymbolFlags.Property, "length" as __String, readonly ? CheckFlags.Readonly : 0); if (combinedFlags & ElementFlags.Variable) { lengthSymbol.links.type = numberType; } else { const literalTypes = []; for (let i = minLength; i <= arity; i++) literalTypes.push(getNumberLiteralType(i)); lengthSymbol.links.type = getUnionType(literalTypes); } properties.push(lengthSymbol); const type = createObjectType(ObjectFlags.Tuple | ObjectFlags.Reference) as TupleType & InterfaceTypeWithDeclaredMembers; type.typeParameters = typeParameters; type.outerTypeParameters = undefined; type.localTypeParameters = typeParameters; type.instantiations = new Map(); type.instantiations.set(getTypeListId(type.typeParameters), type as GenericType); type.target = type as GenericType; type.resolvedTypeArguments = type.typeParameters; type.thisType = createTypeParameter(); type.thisType.isThisType = true; type.thisType.constraint = type; type.declaredProperties = properties; type.declaredCallSignatures = emptyArray; type.declaredConstructSignatures = emptyArray; type.declaredIndexInfos = emptyArray; type.elementFlags = elementFlags; type.minLength = minLength; type.fixedLength = fixedLength; type.hasRestElement = !!(combinedFlags & ElementFlags.Variable); type.combinedFlags = combinedFlags; type.readonly = readonly; type.labeledElementDeclarations = namedMemberDeclarations; return type; } function createNormalizedTypeReference(target: GenericType, typeArguments: readonly Type[] | undefined) { return target.objectFlags & ObjectFlags.Tuple ? createNormalizedTupleType(target as TupleType, typeArguments!) : createTypeReference(target, typeArguments); } function createNormalizedTupleType(target: TupleType, elementTypes: readonly Type[]): Type { if (!(target.combinedFlags & ElementFlags.NonRequired)) { // No need to normalize when we only have regular required elements return createTypeReference(target, elementTypes); } if (target.combinedFlags & ElementFlags.Variadic) { // Transform [A, ...(X | Y | Z)] into [A, ...X] | [A, ...Y] | [A, ...Z] const unionIndex = findIndex(elementTypes, (t, i) => !!(target.elementFlags[i] & ElementFlags.Variadic && t.flags & (TypeFlags.Never | TypeFlags.Union))); if (unionIndex >= 0) { return checkCrossProductUnion(map(elementTypes, (t, i) => target.elementFlags[i] & ElementFlags.Variadic ? t : unknownType)) ? mapType(elementTypes[unionIndex], t => createNormalizedTupleType(target, replaceElement(elementTypes, unionIndex, t))) : errorType; } } // We have optional, rest, or variadic elements that may need normalizing. Normalization ensures that all variadic // elements are generic and that the tuple type has one of the following layouts, disregarding variadic elements: // (1) Zero or more required elements, followed by zero or more optional elements, followed by zero or one rest element. // (2) Zero or more required elements, followed by a rest element, followed by zero or more required elements. // In either layout, zero or more generic variadic elements may be present at any location. const expandedTypes: Type[] = []; const expandedFlags: ElementFlags[] = []; const expandedDeclarations: (NamedTupleMember | ParameterDeclaration | undefined)[] = []; let lastRequiredIndex = -1; let firstRestIndex = -1; let lastOptionalOrRestIndex = -1; for (let i = 0; i < elementTypes.length; i++) { const type = elementTypes[i]; const flags = target.elementFlags[i]; if (flags & ElementFlags.Variadic) { if (type.flags & TypeFlags.Any) { addElement(type, ElementFlags.Rest, target.labeledElementDeclarations?.[i]); } else if (type.flags & TypeFlags.InstantiableNonPrimitive || isGenericMappedType(type)) { // Generic variadic elements stay as they are. addElement(type, ElementFlags.Variadic, target.labeledElementDeclarations?.[i]); } else if (isTupleType(type)) { const elements = getElementTypes(type); if (elements.length + expandedTypes.length >= 10_000) { error( currentNode, isPartOfTypeNode(currentNode!) ? Diagnostics.Type_produces_a_tuple_type_that_is_too_large_to_represent : Diagnostics.Expression_produces_a_tuple_type_that_is_too_large_to_represent, ); return errorType; } // Spread variadic elements with tuple types into the resulting tuple. forEach(elements, (t, n) => addElement(t, type.target.elementFlags[n], type.target.labeledElementDeclarations?.[n])); } else { // Treat everything else as an array type and create a rest element. addElement(isArrayLikeType(type) && getIndexTypeOfType(type, numberType) || errorType, ElementFlags.Rest, target.labeledElementDeclarations?.[i]); } } else { // Copy other element kinds with no change. addElement(type, flags, target.labeledElementDeclarations?.[i]); } } // Turn optional elements preceding the last required element into required elements for (let i = 0; i < lastRequiredIndex; i++) { if (expandedFlags[i] & ElementFlags.Optional) expandedFlags[i] = ElementFlags.Required; } if (firstRestIndex >= 0 && firstRestIndex < lastOptionalOrRestIndex) { // Turn elements between first rest and last optional/rest into a single rest element expandedTypes[firstRestIndex] = getUnionType(sameMap(expandedTypes.slice(firstRestIndex, lastOptionalOrRestIndex + 1), (t, i) => expandedFlags[firstRestIndex + i] & ElementFlags.Variadic ? getIndexedAccessType(t, numberType) : t)); expandedTypes.splice(firstRestIndex + 1, lastOptionalOrRestIndex - firstRestIndex); expandedFlags.splice(firstRestIndex + 1, lastOptionalOrRestIndex - firstRestIndex); expandedDeclarations.splice(firstRestIndex + 1, lastOptionalOrRestIndex - firstRestIndex); } const tupleTarget = getTupleTargetType(expandedFlags, target.readonly, expandedDeclarations); return tupleTarget === emptyGenericType ? emptyObjectType : expandedFlags.length ? createTypeReference(tupleTarget, expandedTypes) : tupleTarget; function addElement(type: Type, flags: ElementFlags, declaration: NamedTupleMember | ParameterDeclaration | undefined) { if (flags & ElementFlags.Required) { lastRequiredIndex = expandedFlags.length; } if (flags & ElementFlags.Rest && firstRestIndex < 0) { firstRestIndex = expandedFlags.length; } if (flags & (ElementFlags.Optional | ElementFlags.Rest)) { lastOptionalOrRestIndex = expandedFlags.length; } expandedTypes.push(flags & ElementFlags.Optional ? addOptionality(type, /*isProperty*/ true) : type); expandedFlags.push(flags); expandedDeclarations.push(declaration); } } function sliceTupleType(type: TupleTypeReference, index: number, endSkipCount = 0) { const target = type.target; const endIndex = getTypeReferenceArity(type) - endSkipCount; return index > target.fixedLength ? getRestArrayTypeOfTupleType(type) || createTupleType(emptyArray) : createTupleType(getTypeArguments(type).slice(index, endIndex), target.elementFlags.slice(index, endIndex), /*readonly*/ false, target.labeledElementDeclarations && target.labeledElementDeclarations.slice(index, endIndex)); } function getKnownKeysOfTupleType(type: TupleTypeReference) { return getUnionType(append(arrayOf(type.target.fixedLength, i => getStringLiteralType("" + i)), getIndexType(type.target.readonly ? globalReadonlyArrayType : globalArrayType))); } // Return count of starting consecutive tuple elements of the given kind(s) function getStartElementCount(type: TupleType, flags: ElementFlags) { const index = findIndex(type.elementFlags, f => !(f & flags)); return index >= 0 ? index : type.elementFlags.length; } // Return count of ending consecutive tuple elements of the given kind(s) function getEndElementCount(type: TupleType, flags: ElementFlags) { return type.elementFlags.length - findLastIndex(type.elementFlags, f => !(f & flags)) - 1; } function getTotalFixedElementCount(type: TupleType) { return type.fixedLength + getEndElementCount(type, ElementFlags.Fixed); } function getElementTypes(type: TupleTypeReference): readonly Type[] { const typeArguments = getTypeArguments(type); const arity = getTypeReferenceArity(type); return typeArguments.length === arity ? typeArguments : typeArguments.slice(0, arity); } function getTypeFromOptionalTypeNode(node: OptionalTypeNode): Type { return addOptionality(getTypeFromTypeNode(node.type), /*isProperty*/ true); } function getTypeId(type: Type): TypeId { return type.id; } function containsType(types: readonly Type[], type: Type): boolean { return binarySearch(types, type, getTypeId, compareValues) >= 0; } function insertType(types: Type[], type: Type): boolean { const index = binarySearch(types, type, getTypeId, compareValues); if (index < 0) { types.splice(~index, 0, type); return true; } return false; } function addTypeToUnion(typeSet: Type[], includes: TypeFlags, type: Type) { const flags = type.flags; // We ignore 'never' types in unions if (!(flags & TypeFlags.Never)) { includes |= flags & TypeFlags.IncludesMask; if (flags & TypeFlags.Instantiable) includes |= TypeFlags.IncludesInstantiable; if (flags & TypeFlags.Intersection && getObjectFlags(type) & ObjectFlags.IsConstrainedTypeVariable) includes |= TypeFlags.IncludesConstrainedTypeVariable; if (type === wildcardType) includes |= TypeFlags.IncludesWildcard; if (isErrorType(type)) includes |= TypeFlags.IncludesError; if (!strictNullChecks && flags & TypeFlags.Nullable) { if (!(getObjectFlags(type) & ObjectFlags.ContainsWideningType)) includes |= TypeFlags.IncludesNonWideningType; } else { const len = typeSet.length; const index = len && type.id > typeSet[len - 1].id ? ~len : binarySearch(typeSet, type, getTypeId, compareValues); if (index < 0) { typeSet.splice(~index, 0, type); } } } return includes; } // Add the given types to the given type set. Order is preserved, duplicates are removed, // and nested types of the given kind are flattened into the set. function addTypesToUnion(typeSet: Type[], includes: TypeFlags, types: readonly Type[]): TypeFlags { let lastType: Type | undefined; for (const type of types) { // We skip the type if it is the same as the last type we processed. This simple test particularly // saves a lot of work for large lists of the same union type, such as when resolving `Record[A]`, // where A and B are large union types. if (type !== lastType) { includes = type.flags & TypeFlags.Union ? addTypesToUnion(typeSet, includes | (isNamedUnionType(type) ? TypeFlags.Union : 0), (type as UnionType).types) : addTypeToUnion(typeSet, includes, type); lastType = type; } } return includes; } function removeSubtypes(types: Type[], hasObjectTypes: boolean): Type[] | undefined { // [] and [T] immediately reduce to [] and [T] respectively if (types.length < 2) { return types; } const id = getTypeListId(types); const match = subtypeReductionCache.get(id); if (match) { return match; } // We assume that redundant primitive types have already been removed from the types array and that there // are no any and unknown types in the array. Thus, the only possible supertypes for primitive types are empty // object types, and if none of those are present we can exclude primitive types from the subtype check. const hasEmptyObject = hasObjectTypes && some(types, t => !!(t.flags & TypeFlags.Object) && !isGenericMappedType(t) && isEmptyResolvedType(resolveStructuredTypeMembers(t as ObjectType))); const len = types.length; let i = len; let count = 0; while (i > 0) { i--; const source = types[i]; if (hasEmptyObject || source.flags & TypeFlags.StructuredOrInstantiable) { // A type parameter with a union constraint may be a subtype of some union, but not a subtype of the // individual constituents of that union. For example, `T extends A | B` is a subtype of `A | B`, but not // a subtype of just `A` or just `B`. When we encounter such a type parameter, we therefore check if the // type parameter is a subtype of a union of all the other types. if (source.flags & TypeFlags.TypeParameter && getBaseConstraintOrType(source).flags & TypeFlags.Union) { if (isTypeRelatedTo(source, getUnionType(map(types, t => t === source ? neverType : t)), strictSubtypeRelation)) { orderedRemoveItemAt(types, i); } continue; } // Find the first property with a unit type, if any. When constituents have a property by the same name // but of a different unit type, we can quickly disqualify them from subtype checks. This helps subtype // reduction of large discriminated union types. const keyProperty = source.flags & (TypeFlags.Object | TypeFlags.Intersection | TypeFlags.InstantiableNonPrimitive) ? find(getPropertiesOfType(source), p => isUnitType(getTypeOfSymbol(p))) : undefined; const keyPropertyType = keyProperty && getRegularTypeOfLiteralType(getTypeOfSymbol(keyProperty)); for (const target of types) { if (source !== target) { if (count === 100000) { // After 100000 subtype checks we estimate the remaining amount of work by assuming the // same ratio of checks per element. If the estimated number of remaining type checks is // greater than 1M we deem the union type too complex to represent. This for example // caps union types at 1000 unique object types. const estimatedCount = (count / (len - i)) * len; if (estimatedCount > 1000000) { tracing?.instant(tracing.Phase.CheckTypes, "removeSubtypes_DepthLimit", { typeIds: types.map(t => t.id) }); error(currentNode, Diagnostics.Expression_produces_a_union_type_that_is_too_complex_to_represent); return undefined; } } count++; if (keyProperty && target.flags & (TypeFlags.Object | TypeFlags.Intersection | TypeFlags.InstantiableNonPrimitive)) { const t = getTypeOfPropertyOfType(target, keyProperty.escapedName); if (t && isUnitType(t) && getRegularTypeOfLiteralType(t) !== keyPropertyType) { continue; } } if ( isTypeRelatedTo(source, target, strictSubtypeRelation) && ( !(getObjectFlags(getTargetType(source)) & ObjectFlags.Class) || !(getObjectFlags(getTargetType(target)) & ObjectFlags.Class) || isTypeDerivedFrom(source, target) ) ) { orderedRemoveItemAt(types, i); break; } } } } } subtypeReductionCache.set(id, types); return types; } function removeRedundantLiteralTypes(types: Type[], includes: TypeFlags, reduceVoidUndefined: boolean) { let i = types.length; while (i > 0) { i--; const t = types[i]; const flags = t.flags; const remove = flags & (TypeFlags.StringLiteral | TypeFlags.TemplateLiteral | TypeFlags.StringMapping) && includes & TypeFlags.String || flags & TypeFlags.NumberLiteral && includes & TypeFlags.Number || flags & TypeFlags.BigIntLiteral && includes & TypeFlags.BigInt || flags & TypeFlags.UniqueESSymbol && includes & TypeFlags.ESSymbol || reduceVoidUndefined && flags & TypeFlags.Undefined && includes & TypeFlags.Void || isFreshLiteralType(t) && containsType(types, (t as LiteralType).regularType); if (remove) { orderedRemoveItemAt(types, i); } } } function removeStringLiteralsMatchedByTemplateLiterals(types: Type[]) { const templates = filter(types, isPatternLiteralType) as (TemplateLiteralType | StringMappingType)[]; if (templates.length) { let i = types.length; while (i > 0) { i--; const t = types[i]; if (t.flags & TypeFlags.StringLiteral && some(templates, template => isTypeMatchedByTemplateLiteralOrStringMapping(t, template))) { orderedRemoveItemAt(types, i); } } } } function isTypeMatchedByTemplateLiteralOrStringMapping(type: Type, template: TemplateLiteralType | StringMappingType) { return template.flags & TypeFlags.TemplateLiteral ? isTypeMatchedByTemplateLiteralType(type, template as TemplateLiteralType) : isMemberOfStringMapping(type, template); } function removeConstrainedTypeVariables(types: Type[]) { const typeVariables: TypeVariable[] = []; // First collect a list of the type variables occurring in constraining intersections. for (const type of types) { if (type.flags & TypeFlags.Intersection && getObjectFlags(type) & ObjectFlags.IsConstrainedTypeVariable) { const index = (type as IntersectionType).types[0].flags & TypeFlags.TypeVariable ? 0 : 1; pushIfUnique(typeVariables, (type as IntersectionType).types[index]); } } // For each type variable, check if the constraining intersections for that type variable fully // cover the constraint of the type variable; if so, remove the constraining intersections and // substitute the type variable. for (const typeVariable of typeVariables) { const primitives: Type[] = []; // First collect the primitive types from the constraining intersections. for (const type of types) { if (type.flags & TypeFlags.Intersection && getObjectFlags(type) & ObjectFlags.IsConstrainedTypeVariable) { const index = (type as IntersectionType).types[0].flags & TypeFlags.TypeVariable ? 0 : 1; if ((type as IntersectionType).types[index] === typeVariable) { insertType(primitives, (type as IntersectionType).types[1 - index]); } } } // If every constituent in the type variable's constraint is covered by an intersection of the type // variable and that constituent, remove those intersections and substitute the type variable. const constraint = getBaseConstraintOfType(typeVariable)!; if (everyType(constraint, t => containsType(primitives, t))) { let i = types.length; while (i > 0) { i--; const type = types[i]; if (type.flags & TypeFlags.Intersection && getObjectFlags(type) & ObjectFlags.IsConstrainedTypeVariable) { const index = (type as IntersectionType).types[0].flags & TypeFlags.TypeVariable ? 0 : 1; if ((type as IntersectionType).types[index] === typeVariable && containsType(primitives, (type as IntersectionType).types[1 - index])) { orderedRemoveItemAt(types, i); } } } insertType(types, typeVariable); } } } function isNamedUnionType(type: Type) { return !!(type.flags & TypeFlags.Union && (type.aliasSymbol || (type as UnionType).origin)); } function addNamedUnions(namedUnions: Type[], types: readonly Type[]) { for (const t of types) { if (t.flags & TypeFlags.Union) { const origin = (t as UnionType).origin; if (t.aliasSymbol || origin && !(origin.flags & TypeFlags.Union)) { pushIfUnique(namedUnions, t); } else if (origin && origin.flags & TypeFlags.Union) { addNamedUnions(namedUnions, (origin as UnionType).types); } } } } function createOriginUnionOrIntersectionType(flags: TypeFlags, types: Type[]) { const result = createOriginType(flags) as UnionOrIntersectionType; result.types = types; return result; } // We sort and deduplicate the constituent types based on object identity. If the subtypeReduction // flag is specified we also reduce the constituent type set to only include types that aren't subtypes // of other types. Subtype reduction is expensive for large union types and is possible only when union // types are known not to circularly reference themselves (as is the case with union types created by // expression constructs such as array literals and the || and ?: operators). Named types can // circularly reference themselves and therefore cannot be subtype reduced during their declaration. // For example, "type Item = string | (() => Item" is a named type that circularly references itself. function getUnionType(types: readonly Type[], unionReduction: UnionReduction = UnionReduction.Literal, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[], origin?: Type): Type { if (types.length === 0) { return neverType; } if (types.length === 1) { return types[0]; } // We optimize for the common case of unioning a union type with some other type (such as `undefined`). if (types.length === 2 && !origin && (types[0].flags & TypeFlags.Union || types[1].flags & TypeFlags.Union)) { const infix = unionReduction === UnionReduction.None ? "N" : unionReduction === UnionReduction.Subtype ? "S" : "L"; const index = types[0].id < types[1].id ? 0 : 1; const id = types[index].id + infix + types[1 - index].id + getAliasId(aliasSymbol, aliasTypeArguments); let type = unionOfUnionTypes.get(id); if (!type) { type = getUnionTypeWorker(types, unionReduction, aliasSymbol, aliasTypeArguments, /*origin*/ undefined); unionOfUnionTypes.set(id, type); } return type; } return getUnionTypeWorker(types, unionReduction, aliasSymbol, aliasTypeArguments, origin); } function getUnionTypeWorker(types: readonly Type[], unionReduction: UnionReduction, aliasSymbol: Symbol | undefined, aliasTypeArguments: readonly Type[] | undefined, origin: Type | undefined): Type { let typeSet: Type[] | undefined = []; const includes = addTypesToUnion(typeSet, 0 as TypeFlags, types); if (unionReduction !== UnionReduction.None) { if (includes & TypeFlags.AnyOrUnknown) { return includes & TypeFlags.Any ? includes & TypeFlags.IncludesWildcard ? wildcardType : includes & TypeFlags.IncludesError ? errorType : anyType : unknownType; } if (includes & TypeFlags.Undefined) { // If type set contains both undefinedType and missingType, remove missingType if (typeSet.length >= 2 && typeSet[0] === undefinedType && typeSet[1] === missingType) { orderedRemoveItemAt(typeSet, 1); } } if (includes & (TypeFlags.Enum | TypeFlags.Literal | TypeFlags.UniqueESSymbol | TypeFlags.TemplateLiteral | TypeFlags.StringMapping) || includes & TypeFlags.Void && includes & TypeFlags.Undefined) { removeRedundantLiteralTypes(typeSet, includes, !!(unionReduction & UnionReduction.Subtype)); } if (includes & TypeFlags.StringLiteral && includes & (TypeFlags.TemplateLiteral | TypeFlags.StringMapping)) { removeStringLiteralsMatchedByTemplateLiterals(typeSet); } if (includes & TypeFlags.IncludesConstrainedTypeVariable) { removeConstrainedTypeVariables(typeSet); } if (unionReduction === UnionReduction.Subtype) { typeSet = removeSubtypes(typeSet, !!(includes & TypeFlags.Object)); if (!typeSet) { return errorType; } } if (typeSet.length === 0) { return includes & TypeFlags.Null ? includes & TypeFlags.IncludesNonWideningType ? nullType : nullWideningType : includes & TypeFlags.Undefined ? includes & TypeFlags.IncludesNonWideningType ? undefinedType : undefinedWideningType : neverType; } } if (!origin && includes & TypeFlags.Union) { const namedUnions: Type[] = []; addNamedUnions(namedUnions, types); const reducedTypes: Type[] = []; for (const t of typeSet) { if (!some(namedUnions, union => containsType((union as UnionType).types, t))) { reducedTypes.push(t); } } if (!aliasSymbol && namedUnions.length === 1 && reducedTypes.length === 0) { return namedUnions[0]; } // We create a denormalized origin type only when the union was created from one or more named unions // (unions with alias symbols or origins) and when there is no overlap between those named unions. const namedTypesCount = reduceLeft(namedUnions, (sum, union) => sum + (union as UnionType).types.length, 0); if (namedTypesCount + reducedTypes.length === typeSet.length) { for (const t of namedUnions) { insertType(reducedTypes, t); } origin = createOriginUnionOrIntersectionType(TypeFlags.Union, reducedTypes); } } const objectFlags = (includes & TypeFlags.NotPrimitiveUnion ? 0 : ObjectFlags.PrimitiveUnion) | (includes & TypeFlags.Intersection ? ObjectFlags.ContainsIntersections : 0); return getUnionTypeFromSortedList(typeSet, objectFlags, aliasSymbol, aliasTypeArguments, origin); } function getUnionOrIntersectionTypePredicate(signatures: readonly Signature[], kind: TypeFlags | undefined): TypePredicate | undefined { let last: TypePredicate | undefined; const types: Type[] = []; for (const sig of signatures) { const pred = getTypePredicateOfSignature(sig); if (pred) { // Constituent type predicates must all have matching kinds. We don't create composite type predicates for assertions. if (pred.kind !== TypePredicateKind.This && pred.kind !== TypePredicateKind.Identifier || last && !typePredicateKindsMatch(last, pred)) { return undefined; } last = pred; types.push(pred.type); } else { // In composite union signatures we permit and ignore signatures with a return type `false`. const returnType = kind !== TypeFlags.Intersection ? getReturnTypeOfSignature(sig) : undefined; if (returnType !== falseType && returnType !== regularFalseType) { return undefined; } } } if (!last) { return undefined; } const compositeType = getUnionOrIntersectionType(types, kind); return createTypePredicate(last.kind, last.parameterName, last.parameterIndex, compositeType); } function typePredicateKindsMatch(a: TypePredicate, b: TypePredicate): boolean { return a.kind === b.kind && a.parameterIndex === b.parameterIndex; } // This function assumes the constituent type list is sorted and deduplicated. function getUnionTypeFromSortedList(types: Type[], precomputedObjectFlags: ObjectFlags, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[], origin?: Type): Type { if (types.length === 0) { return neverType; } if (types.length === 1) { return types[0]; } const typeKey = !origin ? getTypeListId(types) : origin.flags & TypeFlags.Union ? `|${getTypeListId((origin as UnionType).types)}` : origin.flags & TypeFlags.Intersection ? `&${getTypeListId((origin as IntersectionType).types)}` : `#${(origin as IndexType).type.id}|${getTypeListId(types)}`; // origin type id alone is insufficient, as `keyof x` may resolve to multiple WIP values while `x` is still resolving const id = typeKey + getAliasId(aliasSymbol, aliasTypeArguments); let type = unionTypes.get(id); if (!type) { type = createType(TypeFlags.Union) as UnionType; type.objectFlags = precomputedObjectFlags | getPropagatingFlagsOfTypes(types, /*excludeKinds*/ TypeFlags.Nullable); type.types = types; type.origin = origin; type.aliasSymbol = aliasSymbol; type.aliasTypeArguments = aliasTypeArguments; if (types.length === 2 && types[0].flags & TypeFlags.BooleanLiteral && types[1].flags & TypeFlags.BooleanLiteral) { type.flags |= TypeFlags.Boolean; (type as UnionType & IntrinsicType).intrinsicName = "boolean"; } unionTypes.set(id, type); } return type; } function getTypeFromUnionTypeNode(node: UnionTypeNode): Type { const links = getNodeLinks(node); if (!links.resolvedType) { const aliasSymbol = getAliasSymbolForTypeNode(node); links.resolvedType = getUnionType(map(node.types, getTypeFromTypeNode), UnionReduction.Literal, aliasSymbol, getTypeArgumentsForAliasSymbol(aliasSymbol)); } return links.resolvedType; } function addTypeToIntersection(typeSet: Map, includes: TypeFlags, type: Type) { const flags = type.flags; if (flags & TypeFlags.Intersection) { return addTypesToIntersection(typeSet, includes, (type as IntersectionType).types); } if (isEmptyAnonymousObjectType(type)) { if (!(includes & TypeFlags.IncludesEmptyObject)) { includes |= TypeFlags.IncludesEmptyObject; typeSet.set(type.id.toString(), type); } } else { if (flags & TypeFlags.AnyOrUnknown) { if (type === wildcardType) includes |= TypeFlags.IncludesWildcard; if (isErrorType(type)) includes |= TypeFlags.IncludesError; } else if (strictNullChecks || !(flags & TypeFlags.Nullable)) { if (type === missingType) { includes |= TypeFlags.IncludesMissingType; type = undefinedType; } if (!typeSet.has(type.id.toString())) { if (type.flags & TypeFlags.Unit && includes & TypeFlags.Unit) { // We have seen two distinct unit types which means we should reduce to an // empty intersection. Adding TypeFlags.NonPrimitive causes that to happen. includes |= TypeFlags.NonPrimitive; } typeSet.set(type.id.toString(), type); } } includes |= flags & TypeFlags.IncludesMask; } return includes; } // Add the given types to the given type set. Order is preserved, freshness is removed from literal // types, duplicates are removed, and nested types of the given kind are flattened into the set. function addTypesToIntersection(typeSet: Map, includes: TypeFlags, types: readonly Type[]) { for (const type of types) { includes = addTypeToIntersection(typeSet, includes, getRegularTypeOfLiteralType(type)); } return includes; } function removeRedundantSupertypes(types: Type[], includes: TypeFlags) { let i = types.length; while (i > 0) { i--; const t = types[i]; const remove = t.flags & TypeFlags.String && includes & (TypeFlags.StringLiteral | TypeFlags.TemplateLiteral | TypeFlags.StringMapping) || t.flags & TypeFlags.Number && includes & TypeFlags.NumberLiteral || t.flags & TypeFlags.BigInt && includes & TypeFlags.BigIntLiteral || t.flags & TypeFlags.ESSymbol && includes & TypeFlags.UniqueESSymbol || t.flags & TypeFlags.Void && includes & TypeFlags.Undefined || isEmptyAnonymousObjectType(t) && includes & TypeFlags.DefinitelyNonNullable; if (remove) { orderedRemoveItemAt(types, i); } } } // Check that the given type has a match in every union. A given type is matched by // an identical type, and a literal type is additionally matched by its corresponding // primitive type, and missingType is matched by undefinedType (and vice versa). function eachUnionContains(unionTypes: UnionType[], type: Type) { for (const u of unionTypes) { if (!containsType(u.types, type)) { if (type === missingType) { return containsType(u.types, undefinedType); } if (type === undefinedType) { return containsType(u.types, missingType); } const primitive = type.flags & TypeFlags.StringLiteral ? stringType : type.flags & (TypeFlags.Enum | TypeFlags.NumberLiteral) ? numberType : type.flags & TypeFlags.BigIntLiteral ? bigintType : type.flags & TypeFlags.UniqueESSymbol ? esSymbolType : undefined; if (!primitive || !containsType(u.types, primitive)) { return false; } } } return true; } /** * Returns true if the intersection of the template literals and string literals is the empty set, * for example `get${string}` & "setX", and should reduce to never. */ function extractRedundantTemplateLiterals(types: Type[]): boolean { let i = types.length; const literals = filter(types, t => !!(t.flags & TypeFlags.StringLiteral)); while (i > 0) { i--; const t = types[i]; if (!(t.flags & (TypeFlags.TemplateLiteral | TypeFlags.StringMapping))) continue; for (const t2 of literals) { if (isTypeSubtypeOf(t2, t)) { // For example, `get${T}` & "getX" is just "getX", and Lowercase & "foo" is just "foo" orderedRemoveItemAt(types, i); break; } else if (isPatternLiteralType(t)) { return true; } } } return false; } function removeFromEach(types: Type[], flag: TypeFlags) { for (let i = 0; i < types.length; i++) { types[i] = filterType(types[i], t => !(t.flags & flag)); } } // If the given list of types contains more than one union of primitive types, replace the // first with a union containing an intersection of those primitive types, then remove the // other unions and return true. Otherwise, do nothing and return false. function intersectUnionsOfPrimitiveTypes(types: Type[]) { let unionTypes: UnionType[] | undefined; const index = findIndex(types, t => !!(getObjectFlags(t) & ObjectFlags.PrimitiveUnion)); if (index < 0) { return false; } let i = index + 1; // Remove all but the first union of primitive types and collect them in // the unionTypes array. while (i < types.length) { const t = types[i]; if (getObjectFlags(t) & ObjectFlags.PrimitiveUnion) { (unionTypes || (unionTypes = [types[index] as UnionType])).push(t as UnionType); orderedRemoveItemAt(types, i); } else { i++; } } // Return false if there was only one union of primitive types if (!unionTypes) { return false; } // We have more than one union of primitive types, now intersect them. For each // type in each union we check if the type is matched in every union and if so // we include it in the result. const checked: Type[] = []; const result: Type[] = []; for (const u of unionTypes) { for (const t of u.types) { if (insertType(checked, t)) { if (eachUnionContains(unionTypes, t)) { // undefinedType/missingType should always come sorted first so we leverage that here if (t === undefinedType && result.length && result[0] === missingType) { continue; } if (t === missingType && result.length && result[0] === undefinedType) { result[0] = missingType; continue; } insertType(result, t); } } } } // Finally replace the first union with the result types[index] = getUnionTypeFromSortedList(result, ObjectFlags.PrimitiveUnion); return true; } function createIntersectionType(types: Type[], objectFlags: ObjectFlags, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]) { const result = createType(TypeFlags.Intersection) as IntersectionType; result.objectFlags = objectFlags | getPropagatingFlagsOfTypes(types, /*excludeKinds*/ TypeFlags.Nullable); result.types = types; result.aliasSymbol = aliasSymbol; result.aliasTypeArguments = aliasTypeArguments; return result; } // We normalize combinations of intersection and union types based on the distributive property of the '&' // operator. Specifically, because X & (A | B) is equivalent to X & A | X & B, we can transform intersection // types with union type constituents into equivalent union types with intersection type constituents and // effectively ensure that union types are always at the top level in type representations. // // We do not perform structural deduplication on intersection types. Intersection types are created only by the & // type operator and we can't reduce those because we want to support recursive intersection types. For example, // a type alias of the form "type List = T & { next: List }" cannot be reduced during its declaration. // Also, unlike union types, the order of the constituent types is preserved in order that overload resolution // for intersections of types with signatures can be deterministic. function getIntersectionType(types: readonly Type[], flags = IntersectionFlags.None, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]): Type { const typeMembershipMap = new Map(); const includes = addTypesToIntersection(typeMembershipMap, 0 as TypeFlags, types); const typeSet: Type[] = arrayFrom(typeMembershipMap.values()); let objectFlags = ObjectFlags.None; // An intersection type is considered empty if it contains // the type never, or // more than one unit type or, // an object type and a nullable type (null or undefined), or // a string-like type and a type known to be non-string-like, or // a number-like type and a type known to be non-number-like, or // a symbol-like type and a type known to be non-symbol-like, or // a void-like type and a type known to be non-void-like, or // a non-primitive type and a type known to be primitive. if (includes & TypeFlags.Never) { return contains(typeSet, silentNeverType) ? silentNeverType : neverType; } if ( strictNullChecks && includes & TypeFlags.Nullable && in