/// /// /* @internal */ namespace ts { let nextSymbolId = 1; let nextNodeId = 1; let nextMergeId = 1; let nextFlowId = 1; const ambientModuleSymbolRegex = /^".+"$/; export function getNodeId(node: Node): number { if (!node.id) { node.id = nextNodeId; nextNodeId++; } return node.id; } export function getSymbolId(symbol: Symbol): number { if (!symbol.id) { symbol.id = nextSymbolId; nextSymbolId++; } return symbol.id; } export function isInstantiatedModule(node: ModuleDeclaration, preserveConstEnums: boolean) { const moduleState = getModuleInstanceState(node); return moduleState === ModuleInstanceState.Instantiated || (preserveConstEnums && moduleState === ModuleInstanceState.ConstEnumOnly); } export function createTypeChecker(host: TypeCheckerHost, produceDiagnostics: boolean): TypeChecker { const impl = new TypeCheckerImpl(host, produceDiagnostics); return impl.checker; } export const enum TypeFacts { None = 0, TypeofEQString = 1 << 0, TypeofEQNumber = 1 << 1, TypeofEQBoolean = 1 << 2, TypeofEQSymbol = 1 << 3, TypeofEQObject = 1 << 4, TypeofEQFunction = 1 << 5, TypeofEQHostObject = 1 << 6, TypeofNEString = 1 << 7, TypeofNENumber = 1 << 8, TypeofNEBoolean = 1 << 9, TypeofNESymbol = 1 << 10, TypeofNEObject = 1 << 11, TypeofNEFunction = 1 << 12, TypeofNEHostObject = 1 << 13, EQUndefined = 1 << 14, EQNull = 1 << 15, EQUndefinedOrNull = 1 << 16, NEUndefined = 1 << 17, NENull = 1 << 18, NEUndefinedOrNull = 1 << 19, Truthy = 1 << 20, Falsy = 1 << 21, Discriminatable = 1 << 22, All = (1 << 23) - 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 | 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 | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull, BaseNumberFacts = BaseNumberStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, NumberStrictFacts = BaseNumberStrictFacts | Truthy | Falsy, NumberFacts = BaseNumberFacts | Truthy, ZeroStrictFacts = BaseNumberStrictFacts | Falsy, ZeroFacts = BaseNumberFacts, NonZeroStrictFacts = BaseNumberStrictFacts | Truthy, NonZeroFacts = BaseNumberFacts | Truthy, BaseBooleanStrictFacts = TypeofEQBoolean | TypeofNEString | TypeofNENumber | 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 | TypeofNEBoolean | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull | Truthy, SymbolFacts = SymbolStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, ObjectStrictFacts = TypeofEQObject | TypeofEQHostObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | NEUndefined | NENull | NEUndefinedOrNull | Truthy | Discriminatable, ObjectFacts = ObjectStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, FunctionStrictFacts = TypeofEQFunction | TypeofEQHostObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | NEUndefined | NENull | NEUndefinedOrNull | Truthy | Discriminatable, FunctionFacts = FunctionStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, UndefinedFacts = TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | EQUndefined | EQUndefinedOrNull | NENull | Falsy, NullFacts = TypeofEQObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | TypeofNEHostObject | EQNull | EQUndefinedOrNull | NEUndefined | Falsy } export const enum TypeSystemPropertyName { Type, ResolvedBaseConstructorType, DeclaredType, ResolvedReturnType } export const enum CheckMode { Normal = 0, SkipContextSensitive = 1, Inferential = 2 } export type TypeSystemEntity = Symbol | Type | Signature; export interface ExportCollisionTracker { specifierText: string; exportsWithDuplicate: ExportDeclaration[]; } export type ExportCollisionTrackerTable = UnderscoreEscapedMap; export interface TypeSet extends Array { containsAny?: boolean; containsUndefined?: boolean; containsNull?: boolean; containsNever?: boolean; containsNonWideningType?: boolean; containsString?: boolean; containsNumber?: boolean; containsStringOrNumberLiteral?: boolean; containsObjectType?: boolean; containsEmptyObject?: boolean; unionIndex?: number; } export type ErrorReporter = (message: DiagnosticMessage, arg0?: string, arg1?: string) => void; export class TypeCheckerImpl { constructor(host: TypeCheckerHost, produceDiagnostics: boolean) { let anyThis: any = this; for (let key in anyThis) { let v = anyThis[key]; if (v instanceof Function) { anyThis[key] = v.bind(this); } } let __conv_self__ = this; this.host = host; this.produceDiagnostics = produceDiagnostics; this.Symbol = objectAllocator.getSymbolConstructor(); this.Type = objectAllocator.getTypeConstructor(); this.Signature = objectAllocator.getSignatureConstructor(); this.typeCount = 0; this.symbolCount = 0; this.enumCount = 0; this.symbolInstantiationDepth = 0; this.emptySymbols = createSymbolTable(); this.compilerOptions = host.getCompilerOptions(); this.languageVersion = getEmitScriptTarget(__conv_self__.compilerOptions); this.modulekind = getEmitModuleKind(__conv_self__.compilerOptions); this.noUnusedIdentifiers = !!__conv_self__.compilerOptions.noUnusedLocals || !!__conv_self__.compilerOptions.noUnusedParameters; this.allowSyntheticDefaultImports = typeof __conv_self__.compilerOptions.allowSyntheticDefaultImports !== "undefined" ? __conv_self__.compilerOptions.allowSyntheticDefaultImports : __conv_self__.modulekind === ModuleKind.System; this.strictNullChecks = __conv_self__.compilerOptions.strictNullChecks === undefined ? __conv_self__.compilerOptions.strict : __conv_self__.compilerOptions.strictNullChecks; this.noImplicitAny = __conv_self__.compilerOptions.noImplicitAny === undefined ? __conv_self__.compilerOptions.strict : __conv_self__.compilerOptions.noImplicitAny; this.noImplicitThis = __conv_self__.compilerOptions.noImplicitThis === undefined ? __conv_self__.compilerOptions.strict : __conv_self__.compilerOptions.noImplicitThis; this.emitResolver = __conv_self__.createResolver(); this.nodeBuilder = __conv_self__.createNodeBuilder(); this.undefinedSymbol = __conv_self__.createSymbol(SymbolFlags.Property, ("undefined" as __String)); this.undefinedSymbol.declarations = []; this.argumentsSymbol = __conv_self__.createSymbol(SymbolFlags.Property, ("arguments" as __String)); this.tupleTypes = []; this.unionTypes = createMap(); this.intersectionTypes = createMap(); this.literalTypes = createMap(); this.indexedAccessTypes = createMap(); this.evolvingArrayTypes = []; this.unknownSymbol = __conv_self__.createSymbol(SymbolFlags.Property, ("unknown" as __String)); this.resolvingSymbol = __conv_self__.createSymbol(0, InternalSymbolName.Resolving); this.checker = this.createTypeChecker(host); this.anyType = __conv_self__.createIntrinsicType(TypeFlags.Any, "any"); this.autoType = __conv_self__.createIntrinsicType(TypeFlags.Any, "any"); this.unknownType = __conv_self__.createIntrinsicType(TypeFlags.Any, "unknown"); this.undefinedType = __conv_self__.createIntrinsicType(TypeFlags.Undefined, "undefined"); this.undefinedWideningType = __conv_self__.strictNullChecks ? __conv_self__.undefinedType : __conv_self__.createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsWideningType, "undefined"); this.nullType = __conv_self__.createIntrinsicType(TypeFlags.Null, "null"); this.nullWideningType = __conv_self__.strictNullChecks ? __conv_self__.nullType : __conv_self__.createIntrinsicType(TypeFlags.Null | TypeFlags.ContainsWideningType, "null"); this.stringType = __conv_self__.createIntrinsicType(TypeFlags.String, "string"); this.numberType = __conv_self__.createIntrinsicType(TypeFlags.Number, "number"); this.trueType = __conv_self__.createIntrinsicType(TypeFlags.BooleanLiteral, "true"); this.falseType = __conv_self__.createIntrinsicType(TypeFlags.BooleanLiteral, "false"); this.booleanType = __conv_self__.createBooleanType([__conv_self__.trueType, __conv_self__.falseType]); this.esSymbolType = __conv_self__.createIntrinsicType(TypeFlags.ESSymbol, "symbol"); this.voidType = __conv_self__.createIntrinsicType(TypeFlags.Void, "void"); this.neverType = __conv_self__.createIntrinsicType(TypeFlags.Never, "never"); this.silentNeverType = __conv_self__.createIntrinsicType(TypeFlags.Never, "never"); this.nonPrimitiveType = __conv_self__.createIntrinsicType(TypeFlags.NonPrimitive, "object"); this.emptyObjectType = __conv_self__.createAnonymousType(undefined, __conv_self__.emptySymbols, emptyArray, emptyArray, undefined, undefined); this.emptyTypeLiteralSymbol = __conv_self__.createSymbol(SymbolFlags.TypeLiteral, InternalSymbolName.Type); this.emptyTypeLiteralSymbol.members = createSymbolTable(); this.emptyTypeLiteralType = __conv_self__.createAnonymousType(__conv_self__.emptyTypeLiteralSymbol, __conv_self__.emptySymbols, emptyArray, emptyArray, undefined, undefined); this.emptyGenericType = (__conv_self__.createAnonymousType(undefined, __conv_self__.emptySymbols, emptyArray, emptyArray, undefined, undefined)); this.emptyGenericType.instantiations = createMap(); this.anyFunctionType = __conv_self__.createAnonymousType(undefined, __conv_self__.emptySymbols, emptyArray, emptyArray, undefined, undefined); // The anyFunctionType contains the anyFunctionType by definition. The flag is further propagated // in getPropagatingFlagsOfTypes, and it is checked in inferFromTypes. this.anyFunctionType.flags |= TypeFlags.ContainsAnyFunctionType; this.noConstraintType = __conv_self__.createAnonymousType(undefined, __conv_self__.emptySymbols, emptyArray, emptyArray, undefined, undefined); this.circularConstraintType = __conv_self__.createAnonymousType(undefined, __conv_self__.emptySymbols, emptyArray, emptyArray, undefined, undefined); this.anySignature = __conv_self__.createSignature(undefined, undefined, undefined, emptyArray, __conv_self__.anyType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false); this.unknownSignature = __conv_self__.createSignature(undefined, undefined, undefined, emptyArray, __conv_self__.unknownType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false); this.resolvingSignature = __conv_self__.createSignature(undefined, undefined, undefined, emptyArray, __conv_self__.anyType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false); this.silentNeverSignature = __conv_self__.createSignature(undefined, undefined, undefined, emptyArray, __conv_self__.silentNeverType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false); this.enumNumberIndexInfo = __conv_self__.createIndexInfo(__conv_self__.stringType, /*isReadonly*/ true); this.jsObjectLiteralIndexInfo = __conv_self__.createIndexInfo(__conv_self__.anyType, /*isReadonly*/ false); this.globals = createSymbolTable(); this.flowLoopStart = 0; this.flowLoopCount = 0; this.visitedFlowCount = 0; this.emptyStringType = __conv_self__.getLiteralType(""); this.zeroType = __conv_self__.getLiteralType(0); this.resolutionTargets = []; this.resolutionResults = []; this.resolutionPropertyNames = []; this.suggestionCount = 0; this.maximumSuggestionCount = 10; this.mergedSymbols = []; this.symbolLinks = []; this.nodeLinks = []; this.flowLoopCaches = []; this.flowLoopNodes = []; this.flowLoopKeys = []; this.flowLoopTypes = []; this.visitedFlowNodes = []; this.visitedFlowTypes = []; this.potentialThisCollisions = []; this.potentialNewTargetCollisions = []; this.awaitedTypeStack = []; this.diagnostics = createDiagnosticCollection(); this.typeofEQFacts = createMapFromTemplate({ "string": TypeFacts.TypeofEQString, "number": TypeFacts.TypeofEQNumber, "boolean": TypeFacts.TypeofEQBoolean, "symbol": TypeFacts.TypeofEQSymbol, "undefined": TypeFacts.EQUndefined, "object": TypeFacts.TypeofEQObject, "function": TypeFacts.TypeofEQFunction }); this.typeofNEFacts = createMapFromTemplate({ "string": TypeFacts.TypeofNEString, "number": TypeFacts.TypeofNENumber, "boolean": TypeFacts.TypeofNEBoolean, "symbol": TypeFacts.TypeofNESymbol, "undefined": TypeFacts.NEUndefined, "object": TypeFacts.TypeofNEObject, "function": TypeFacts.TypeofNEFunction }); this.typeofTypesByName = createMapFromTemplate({ "string": __conv_self__.stringType, "number": __conv_self__.numberType, "boolean": __conv_self__.booleanType, "symbol": __conv_self__.esSymbolType, "undefined": __conv_self__.undefinedType }); this.typeofType = __conv_self__.createTypeofType(); this._hasComputedJsxElementPropertiesName = false; this._hasComputedJsxElementChildrenPropertyName = false; this.jsxTypes = createUnderscoreEscapedMap(); this.JsxNames = { JSX: "JSX" as __String, IntrinsicElements: "IntrinsicElements" as __String, ElementClass: "ElementClass" as __String, ElementAttributesPropertyNameContainer: "ElementAttributesProperty" as __String, ElementChildrenAttributeNameContainer: "ElementChildrenAttribute" as __String, Element: "Element" as __String, IntrinsicAttributes: "IntrinsicAttributes" as __String, IntrinsicClassAttributes: "IntrinsicClassAttributes" as __String }; this.subtypeRelation = createMap(); this.assignableRelation = createMap(); this.comparableRelation = createMap(); this.identityRelation = createMap(); this.enumRelation = createMap(); // This is for caching the result of getSymbolDisplayBuilder. Do not access directly. this.builtinGlobals = createSymbolTable(); this.builtinGlobals.set(this.undefinedSymbol.escapedName, this.undefinedSymbol); /** Things we lazy load from the JSX namespace */ this.jsxTypes = createUnderscoreEscapedMap(); this.checker = this.createTypeChecker(host); this.initializeTypeChecker(); } private createTypeChecker(host: TypeCheckerHost): TypeChecker { const __conv_self__ = this; const checkerObj: TypeChecker = { getNodeCount: () => sum(host.getSourceFiles(), "nodeCount"), getIdentifierCount: () => sum(host.getSourceFiles(), "identifierCount"), getSymbolCount: () => sum(host.getSourceFiles(), "symbolCount") + __conv_self__.symbolCount, getTypeCount: () => __conv_self__.typeCount, isUndefinedSymbol: symbol => symbol === __conv_self__.undefinedSymbol, isArgumentsSymbol: symbol => symbol === __conv_self__.argumentsSymbol, isUnknownSymbol: symbol => symbol === __conv_self__.unknownSymbol, getMergedSymbol: this.getMergedSymbol, getDiagnostics: this.getDiagnostics, getGlobalDiagnostics: this.getGlobalDiagnostics, getTypeOfSymbolAtLocation: (symbol, location) => { location = getParseTreeNode(location); return location ? __conv_self__.getTypeOfSymbolAtLocation(symbol, location) : __conv_self__.unknownType; }, getSymbolsOfParameterPropertyDeclaration: (parameter, parameterName) => { parameter = getParseTreeNode(parameter, isParameter); Debug.assert(parameter !== undefined, "Cannot get symbols of a synthetic parameter that cannot be resolved to a parse-tree node."); return __conv_self__.getSymbolsOfParameterPropertyDeclaration(parameter, escapeLeadingUnderscores(parameterName)); }, getDeclaredTypeOfSymbol: this.getDeclaredTypeOfSymbol, getPropertiesOfType: this.getPropertiesOfType, getPropertyOfType: (type, name) => __conv_self__.getPropertyOfType(type, escapeLeadingUnderscores(name)), getIndexInfoOfType: this.getIndexInfoOfType, getSignaturesOfType: this.getSignaturesOfType, getIndexTypeOfType: this.getIndexTypeOfType, getBaseTypes: this.getBaseTypes, getBaseTypeOfLiteralType: this.getBaseTypeOfLiteralType, getWidenedType: this.getWidenedType, getTypeFromTypeNode: node => { node = getParseTreeNode(node, isTypeNode); return node ? __conv_self__.getTypeFromTypeNode(node) : __conv_self__.unknownType; }, getParameterType: this.getTypeAtPosition, getReturnTypeOfSignature: this.getReturnTypeOfSignature, getNullableType: this.getNullableType, getNonNullableType: this.getNonNullableType, typeToTypeNode: this.nodeBuilder.typeToTypeNode, indexInfoToIndexSignatureDeclaration: this.nodeBuilder.indexInfoToIndexSignatureDeclaration, signatureToSignatureDeclaration: this.nodeBuilder.signatureToSignatureDeclaration, getSymbolsInScope: (location, meaning) => { location = getParseTreeNode(location); return location ? __conv_self__.getSymbolsInScope(location, meaning) : []; }, getSymbolAtLocation: node => { node = getParseTreeNode(node); return node ? __conv_self__.getSymbolAtLocation(node) : undefined; }, getShorthandAssignmentValueSymbol: node => { node = getParseTreeNode(node); return node ? __conv_self__.getShorthandAssignmentValueSymbol(node) : undefined; }, getExportSpecifierLocalTargetSymbol: node => { node = getParseTreeNode(node, isExportSpecifier); return node ? __conv_self__.getExportSpecifierLocalTargetSymbol(node) : undefined; }, getTypeAtLocation: node => { node = getParseTreeNode(node); return node ? __conv_self__.getTypeOfNode(node) : __conv_self__.unknownType; }, getPropertySymbolOfDestructuringAssignment: location => { location = getParseTreeNode(location, isIdentifier); return location ? __conv_self__.getPropertySymbolOfDestructuringAssignment(location) : undefined; }, signatureToString: (signature, enclosingDeclaration?, flags?, kind?) => { return __conv_self__.signatureToString(signature, getParseTreeNode(enclosingDeclaration), flags, kind); }, typeToString: (type, enclosingDeclaration?, flags?) => { return __conv_self__.typeToString(type, getParseTreeNode(enclosingDeclaration), flags); }, getSymbolDisplayBuilder: this.getSymbolDisplayBuilder, symbolToString: (symbol, enclosingDeclaration?, meaning?) => { return __conv_self__.symbolToString(symbol, getParseTreeNode(enclosingDeclaration), meaning); }, getAugmentedPropertiesOfType: this.getAugmentedPropertiesOfType, getRootSymbols: this.getRootSymbols, getContextualType: node => { node = getParseTreeNode(node, isExpression); return node ? __conv_self__.getContextualType(node) : undefined; }, getFullyQualifiedName: this.getFullyQualifiedName, getResolvedSignature: (node, candidatesOutArray, theArgumentCount) => { node = getParseTreeNode(node, isCallLikeExpression); __conv_self__.apparentArgumentCount = theArgumentCount; const res = node ? __conv_self__.getResolvedSignature(node, candidatesOutArray) : undefined; __conv_self__.apparentArgumentCount = undefined; return res; }, getConstantValue: node => { node = getParseTreeNode(node, __conv_self__.canHaveConstantValue); return node ? __conv_self__.getConstantValue(node) : undefined; }, isValidPropertyAccess: (node, propertyName) => { node = getParseTreeNode(node, isPropertyAccessOrQualifiedName); return node ? __conv_self__.isValidPropertyAccess(node, escapeLeadingUnderscores(propertyName)) : false; }, getSignatureFromDeclaration: declaration => { declaration = getParseTreeNode(declaration, isFunctionLike); return declaration ? __conv_self__.getSignatureFromDeclaration(declaration) : undefined; }, isImplementationOfOverload: node => { const parsed = getParseTreeNode(node, isFunctionLike); return parsed ? __conv_self__.isImplementationOfOverload(parsed) : undefined; }, getImmediateAliasedSymbol: symbol => { Debug.assert((symbol.flags & SymbolFlags.Alias) !== 0, "Should only get Alias here."); const links = __conv_self__.getSymbolLinks(symbol); if (!links.immediateTarget) { const node = __conv_self__.getDeclarationOfAliasSymbol(symbol); Debug.assert(!!node); links.immediateTarget = __conv_self__.getTargetOfAliasDeclaration(node, /*dontRecursivelyResolve*/ true); } return links.immediateTarget; }, getAliasedSymbol: this.resolveAlias, getEmitResolver: this.getEmitResolver, getExportsOfModule: this.getExportsOfModuleAsArray, getExportsAndPropertiesOfModule: this.getExportsAndPropertiesOfModule, getAmbientModules: this.getAmbientModules, getAllAttributesTypeFromJsxOpeningLikeElement: node => { node = getParseTreeNode(node, isJsxOpeningLikeElement); return node ? __conv_self__.getAllAttributesTypeFromJsxOpeningLikeElement(node) : undefined; }, getJsxIntrinsicTagNames: this.getJsxIntrinsicTagNames, isOptionalParameter: node => { node = getParseTreeNode(node, isParameter); return node ? __conv_self__.isOptionalParameter(node) : false; }, tryGetMemberInModuleExports: (name, symbol) => __conv_self__.tryGetMemberInModuleExports(escapeLeadingUnderscores(name), symbol), tryGetMemberInModuleExportsAndProperties: (name, symbol) => __conv_self__.tryGetMemberInModuleExportsAndProperties(escapeLeadingUnderscores(name), symbol), tryFindAmbientModuleWithoutAugmentations: moduleName => { // we deliberately exclude augmentations // since we are only interested in declarations of the module itself return __conv_self__.tryFindAmbientModule(moduleName, /*withAugmentations*/ false); }, getApparentType: this.getApparentType, getAllPossiblePropertiesOfType: this.getAllPossiblePropertiesOfType, getSuggestionForNonexistentProperty: (node, type) => unescapeLeadingUnderscores(__conv_self__.getSuggestionForNonexistentProperty(node, type)), getSuggestionForNonexistentSymbol: (location, name, meaning) => unescapeLeadingUnderscores(__conv_self__.getSuggestionForNonexistentSymbol(location, escapeLeadingUnderscores(name), meaning)), getBaseConstraintOfType: this.getBaseConstraintOfType, resolveName: (name, location, meaning) => { return __conv_self__.resolveName(location, escapeLeadingUnderscores(name), meaning, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined); }, getJsxNamespace: () => unescapeLeadingUnderscores(__conv_self__.getJsxNamespace()), //advancedChecker: this, }; const chAny: any = checkerObj; for (let key in chAny) { const val: any = chAny[key]; if (val instanceof Function) { chAny[key] = val.bind(this); } } return checkerObj; } public checker: TypeChecker; public host: TypeCheckerHost; public produceDiagnostics: boolean; // 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). public cancellationToken: CancellationToken; public requestedExternalEmitHelpers: ExternalEmitHelpers; public externalHelpersModule: Symbol; public Symbol: new (flags: SymbolFlags, name: __String) => Symbol; public Type: new (checker: TypeChecker, flags: TypeFlags) => Type; public Signature: new (checker: TypeChecker) => Signature; public typeCount: number; public symbolCount: number; public enumCount: number; public symbolInstantiationDepth: number; public emptySymbols: SymbolTable; public compilerOptions: CompilerOptions; public languageVersion: ScriptTarget; public modulekind: ModuleKind; public noUnusedIdentifiers: boolean; public allowSyntheticDefaultImports: boolean; public strictNullChecks: boolean; public noImplicitAny: boolean; public noImplicitThis: boolean; public emitResolver: EmitResolver; public nodeBuilder = (false as true) && this.createNodeBuilder(); public undefinedSymbol = (false as true) && this.createSymbol(SymbolFlags.Property, ("undefined" as __String)); public argumentsSymbol = (false as true) && this.createSymbol(SymbolFlags.Property, ("arguments" as __String)); /** This will be set during calls to `getResolvedSignature` where services determines an apparent number of arguments greater than what is actually provided. */ public apparentArgumentCount: number | undefined; // 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. public tupleTypes: GenericType[] = (false as true) && []; public unionTypes = (false as true) && createMap(); public intersectionTypes = (false as true) && createMap(); public literalTypes = (false as true) && createMap(); public indexedAccessTypes = (false as true) && createMap(); public evolvingArrayTypes: EvolvingArrayType[] = (false as true) && []; public unknownSymbol = (false as true) && this.createSymbol(SymbolFlags.Property, ("unknown" as __String)); public resolvingSymbol = (false as true) && this.createSymbol(0, InternalSymbolName.Resolving); public anyType = (false as true) && this.createIntrinsicType(TypeFlags.Any, "any"); public autoType = (false as true) && this.createIntrinsicType(TypeFlags.Any, "any"); public unknownType = (false as true) && this.createIntrinsicType(TypeFlags.Any, "unknown"); public undefinedType = (false as true) && this.createIntrinsicType(TypeFlags.Undefined, "undefined"); public undefinedWideningType = (false as true) && (this.strictNullChecks ? this.undefinedType : this.createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsWideningType, "undefined")); public nullType = (false as true) && this.createIntrinsicType(TypeFlags.Null, "null"); public nullWideningType = (false as true) && (this.strictNullChecks ? this.nullType : this.createIntrinsicType(TypeFlags.Null | TypeFlags.ContainsWideningType, "null")); public stringType = (false as true) && this.createIntrinsicType(TypeFlags.String, "string"); public numberType = (false as true) && this.createIntrinsicType(TypeFlags.Number, "number"); public trueType = (false as true) && this.createIntrinsicType(TypeFlags.BooleanLiteral, "true"); public falseType = (false as true) && this.createIntrinsicType(TypeFlags.BooleanLiteral, "false"); public booleanType = (false as true) && this.createBooleanType([this.trueType, this.falseType]); public esSymbolType = (false as true) && this.createIntrinsicType(TypeFlags.ESSymbol, "symbol"); public voidType = (false as true) && this.createIntrinsicType(TypeFlags.Void, "void"); public neverType = (false as true) && this.createIntrinsicType(TypeFlags.Never, "never"); public silentNeverType = (false as true) && this.createIntrinsicType(TypeFlags.Never, "never"); public nonPrimitiveType = (false as true) && this.createIntrinsicType(TypeFlags.NonPrimitive, "object"); public emptyObjectType = (false as true) && this.createAnonymousType(undefined, this.emptySymbols, emptyArray, emptyArray, undefined, undefined); public emptyTypeLiteralSymbol = (false as true) && this.createSymbol(SymbolFlags.TypeLiteral, InternalSymbolName.Type); public emptyTypeLiteralType = (false as true) && this.createAnonymousType(this.emptyTypeLiteralSymbol, this.emptySymbols, emptyArray, emptyArray, undefined, undefined); public emptyGenericType = (false as true) && (this.createAnonymousType(undefined, this.emptySymbols, emptyArray, emptyArray, undefined, undefined)); public anyFunctionType = (false as true) && this.createAnonymousType(undefined, this.emptySymbols, emptyArray, emptyArray, undefined, undefined); // The anyFunctionType contains the anyFunctionType by definition. The flag is further propagated // in getPropagatingFlagsOfTypes, and it is checked in inferFromTypes. public noConstraintType = (false as true) && this.createAnonymousType(undefined, this.emptySymbols, emptyArray, emptyArray, undefined, undefined); public circularConstraintType = (false as true) && this.createAnonymousType(undefined, this.emptySymbols, emptyArray, emptyArray, undefined, undefined); public anySignature = (false as true) && this.createSignature(undefined, undefined, undefined, emptyArray, this.anyType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false); public unknownSignature = (false as true) && this.createSignature(undefined, undefined, undefined, emptyArray, this.unknownType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false); public resolvingSignature = (false as true) && this.createSignature(undefined, undefined, undefined, emptyArray, this.anyType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false); public silentNeverSignature = (false as true) && this.createSignature(undefined, undefined, undefined, emptyArray, this.silentNeverType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false); public enumNumberIndexInfo = (false as true) && this.createIndexInfo(this.stringType, /*isReadonly*/ true); public jsObjectLiteralIndexInfo = (false as true) && this.createIndexInfo(this.anyType, /*isReadonly*/ false); public globals = (false as true) && createSymbolTable(); /** * 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. */ public patternAmbientModules: PatternAmbientModule[]; public globalObjectType: ObjectType; public globalFunctionType: ObjectType; public globalArrayType: GenericType; public globalReadonlyArrayType: GenericType; public globalStringType: ObjectType; public globalNumberType: ObjectType; public globalBooleanType: ObjectType; public globalRegExpType: ObjectType; public globalThisType: GenericType; public anyArrayType: Type; public autoArrayType: Type; public anyReadonlyArrayType: Type; // 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 public deferredGlobalESSymbolConstructorSymbol: Symbol; public deferredGlobalESSymbolType: ObjectType; public deferredGlobalTypedPropertyDescriptorType: GenericType; public deferredGlobalPromiseType: GenericType; public deferredGlobalPromiseConstructorSymbol: Symbol; public deferredGlobalPromiseConstructorLikeType: ObjectType; public deferredGlobalIterableType: GenericType; public deferredGlobalIteratorType: GenericType; public deferredGlobalIterableIteratorType: GenericType; public deferredGlobalAsyncIterableType: GenericType; public deferredGlobalAsyncIteratorType: GenericType; public deferredGlobalAsyncIterableIteratorType: GenericType; public deferredGlobalTemplateStringsArrayType: ObjectType; public deferredJsxElementClassType: Type; public deferredJsxElementType: Type; public deferredJsxStatelessElementType: Type; public deferredNodes: Node[]; public deferredUnusedIdentifierNodes: Node[]; public flowLoopStart = (false as true) && 0; public flowLoopCount = (false as true) && 0; public visitedFlowCount = (false as true) && 0; public emptyStringType = (false as true) && this.getLiteralType(""); public zeroType = (false as true) && this.getLiteralType(0); public resolutionTargets: TypeSystemEntity[] = (false as true) && []; public resolutionResults: boolean[] = (false as true) && []; public resolutionPropertyNames: TypeSystemPropertyName[] = (false as true) && []; public suggestionCount = (false as true) && 0; public maximumSuggestionCount = (false as true) && 10; public mergedSymbols: Symbol[] = (false as true) && []; public symbolLinks: SymbolLinks[] = (false as true) && []; public nodeLinks: NodeLinks[] = (false as true) && []; public flowLoopCaches: Map[] = (false as true) && []; public flowLoopNodes: FlowNode[] = (false as true) && []; public flowLoopKeys: string[] = (false as true) && []; public flowLoopTypes: Type[][] = (false as true) && []; public visitedFlowNodes: FlowNode[] = (false as true) && []; public visitedFlowTypes: FlowType[] = (false as true) && []; public potentialThisCollisions: Node[] = (false as true) && []; public potentialNewTargetCollisions: Node[] = (false as true) && []; public awaitedTypeStack: number[] = (false as true) && []; public diagnostics = (false as true) && createDiagnosticCollection(); public typeofEQFacts = (false as true) && createMapFromTemplate({ "string": TypeFacts.TypeofEQString, "number": TypeFacts.TypeofEQNumber, "boolean": TypeFacts.TypeofEQBoolean, "symbol": TypeFacts.TypeofEQSymbol, "undefined": TypeFacts.EQUndefined, "object": TypeFacts.TypeofEQObject, "function": TypeFacts.TypeofEQFunction }); public typeofNEFacts = (false as true) && createMapFromTemplate({ "string": TypeFacts.TypeofNEString, "number": TypeFacts.TypeofNENumber, "boolean": TypeFacts.TypeofNEBoolean, "symbol": TypeFacts.TypeofNESymbol, "undefined": TypeFacts.NEUndefined, "object": TypeFacts.TypeofNEObject, "function": TypeFacts.TypeofNEFunction }); public typeofTypesByName = (false as true) && createMapFromTemplate({ "string": this.stringType, "number": this.numberType, "boolean": this.booleanType, "symbol": this.esSymbolType, "undefined": this.undefinedType }); public typeofType = (false as true) && this.createTypeofType(); public _jsxNamespace: __String; public _jsxFactoryEntity: EntityName; public _jsxElementPropertiesName: __String; public _hasComputedJsxElementPropertiesName: boolean; public _jsxElementChildrenPropertyName: __String; public _hasComputedJsxElementChildrenPropertyName: boolean; /** Things we lazy load from the JSX namespace */ public jsxTypes = (false as true) && createUnderscoreEscapedMap(); public JsxNames = (false as true) && { JSX: "JSX" as __String, IntrinsicElements: "IntrinsicElements" as __String, ElementClass: "ElementClass" as __String, ElementAttributesPropertyNameContainer: "ElementAttributesProperty" as __String, ElementChildrenAttributeNameContainer: "ElementChildrenAttribute" as __String, Element: "Element" as __String, IntrinsicAttributes: "IntrinsicAttributes" as __String, IntrinsicClassAttributes: "IntrinsicClassAttributes" as __String }; public subtypeRelation = (false as true) && createMap(); public assignableRelation = (false as true) && createMap(); public comparableRelation = (false as true) && createMap(); public identityRelation = (false as true) && createMap(); public enumRelation = (false as true) && createMap(); // This is for caching the result of getSymbolDisplayBuilder. Do not access directly. public _displayBuilder: SymbolDisplayBuilder; public builtinGlobals = (false as true) && createSymbolTable(); public getJsxNamespace(): __String { if (!this._jsxNamespace) { this._jsxNamespace = ("React" as __String); if (this.compilerOptions.jsxFactory) { this._jsxFactoryEntity = parseIsolatedEntityName(this.compilerOptions.jsxFactory, this.languageVersion); if (this._jsxFactoryEntity) { this._jsxNamespace = this.getFirstIdentifier(this._jsxFactoryEntity).escapedText; } } else if (this.compilerOptions.reactNamespace) { this._jsxNamespace = escapeLeadingUnderscores(this.compilerOptions.reactNamespace); } } return this._jsxNamespace; } public getEmitResolver(sourceFile: SourceFile, cancellationToken: CancellationToken) { // 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. this.getDiagnostics(sourceFile, cancellationToken); return this.emitResolver; } public error(location: Node, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number): void { const diagnostic = location ? createDiagnosticForNode(location, message, arg0, arg1, arg2) : createCompilerDiagnostic(message, arg0, arg1, arg2); this.diagnostics.add(diagnostic); } public createSymbol(flags: SymbolFlags, name: __String) { this.symbolCount++; const symbol = (new this.Symbol(flags | SymbolFlags.Transient, name)); symbol.checkFlags = 0; return symbol; } public isTransientSymbol(symbol: Symbol): symbol is TransientSymbol { return (symbol.flags & SymbolFlags.Transient) !== 0; } public 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; } public recordMergedSymbol(target: Symbol, source: Symbol) { if (!source.mergeId) { source.mergeId = nextMergeId; nextMergeId++; } this.mergedSymbols[source.mergeId] = target; } public cloneSymbol(symbol: Symbol): Symbol { const result = this.createSymbol(symbol.flags, symbol.escapedName); result.declarations = symbol.declarations.slice(0); result.parent = symbol.parent; if (symbol.valueDeclaration) result.valueDeclaration = symbol.valueDeclaration; if (symbol.constEnumOnlyModule) result.constEnumOnlyModule = true; if (symbol.members) result.members = cloneMap(symbol.members); if (symbol.exports) result.exports = cloneMap(symbol.exports); this.recordMergedSymbol(result, symbol); return result; } public mergeSymbol(target: Symbol, source: Symbol) { const __conv_self__ = this; if (!(target.flags & this.getExcludedSymbolFlags(source.flags))) { 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 && (!target.valueDeclaration || (target.valueDeclaration.kind === SyntaxKind.ModuleDeclaration && source.valueDeclaration.kind !== SyntaxKind.ModuleDeclaration))) { // other kinds of value declarations take precedence over modules target.valueDeclaration = source.valueDeclaration; } addRange(target.declarations, source.declarations); if (source.members) { if (!target.members) target.members = createSymbolTable(); this.mergeSymbolTable(target.members, source.members); } if (source.exports) { if (!target.exports) target.exports = createSymbolTable(); this.mergeSymbolTable(target.exports, source.exports); } this.recordMergedSymbol(target, source); } else if (target.flags & SymbolFlags.NamespaceModule) { this.error(getNameOfDeclaration(source.declarations[0]), Diagnostics.Cannot_augment_module_0_with_value_exports_because_it_resolves_to_a_non_module_entity, this.symbolToString(target)); } else { const message = target.flags & SymbolFlags.BlockScopedVariable || source.flags & SymbolFlags.BlockScopedVariable ? Diagnostics.Cannot_redeclare_block_scoped_variable_0 : Diagnostics.Duplicate_identifier_0; forEach(source.declarations, node => { __conv_self__.error(getNameOfDeclaration(node) || node, message, __conv_self__.symbolToString(source)); }); forEach(target.declarations, node => { __conv_self__.error(getNameOfDeclaration(node) || node, message, __conv_self__.symbolToString(source)); }); } } public mergeSymbolTable(target: SymbolTable, source: SymbolTable) { const __conv_self__ = this; source.forEach((sourceSymbol, id) => { let targetSymbol = target.get(id); if (!targetSymbol) { target.set(id, sourceSymbol); } else { if (!(targetSymbol.flags & SymbolFlags.Transient)) { targetSymbol = __conv_self__.cloneSymbol(targetSymbol); target.set(id, targetSymbol); } __conv_self__.mergeSymbol(targetSymbol, sourceSymbol); } }); } public mergeModuleAugmentation(moduleName: LiteralExpression): void { const moduleAugmentation = moduleName.parent; 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)) { this.mergeSymbolTable(this.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 = !isInAmbientContext(moduleName.parent.parent) ? Diagnostics.Invalid_module_name_in_augmentation_module_0_cannot_be_found : undefined; let mainModule = this.resolveExternalModuleNameWorker(moduleName, moduleName, moduleNotFoundError, /*isForAugmentation*/ true); if (!mainModule) { return; } // obtain item referenced by 'export=' mainModule = this.resolveExternalModuleSymbol(mainModule); if (mainModule.flags & SymbolFlags.Namespace) { // if module symbol has already been merged - it is safe to use it. // otherwise clone it mainModule = mainModule.flags & SymbolFlags.Transient ? mainModule : this.cloneSymbol(mainModule); this.mergeSymbol(mainModule, moduleAugmentation.symbol); } else { this.error(moduleName, Diagnostics.Cannot_augment_module_0_because_it_resolves_to_a_non_module_entity, moduleName.text); } } } public addToSymbolTable(target: SymbolTable, source: SymbolTable, message: DiagnosticMessage) { const __conv_self__ = this; source.forEach((sourceSymbol, id) => { const targetSymbol = target.get(id); if (targetSymbol) { // Error on redeclarations forEach(targetSymbol.declarations, addDeclarationDiagnostic(unescapeLeadingUnderscores(id), message)); } else { target.set(id, sourceSymbol); } }); function addDeclarationDiagnostic(id: string, message: DiagnosticMessage) { return (declaration: Declaration) => __conv_self__.diagnostics.add(createDiagnosticForNode(declaration, message, id)); } } public getSymbolLinks(symbol: Symbol): SymbolLinks { if (symbol.flags & SymbolFlags.Transient) return symbol; const id = getSymbolId(symbol); return this.symbolLinks[id] || (this.symbolLinks[id] = {}); } public getNodeLinks(node: Node): NodeLinks { const nodeId = getNodeId(node); return this.nodeLinks[nodeId] || (this.nodeLinks[nodeId] = { flags: 0 }); } public getObjectFlags(type: Type): ObjectFlags { return type.flags & TypeFlags.Object ? (type).objectFlags : 0; } public isGlobalSourceFile(node: Node) { return node.kind === SyntaxKind.SourceFile && !isExternalOrCommonJsModule(node); } public getSymbol(symbols: SymbolTable, name: __String, meaning: SymbolFlags): Symbol { if (meaning) { const symbol = symbols.get(name); if (symbol) { Debug.assert((getCheckFlags(symbol) & CheckFlags.Instantiated) === 0, "Should never get an instantiated symbol here."); if (symbol.flags & meaning) { return symbol; } if (symbol.flags & SymbolFlags.Alias) { const target = this.resolveAlias(symbol); // Unknown symbol means an error occurred in alias resolution, treat it as positive answer to avoid cascading errors if (target === this.unknownSymbol || target.flags & 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 */ public getSymbolsOfParameterPropertyDeclaration(parameter: ParameterDeclaration, parameterName: __String): [Symbol, Symbol] { const constructorDeclaration = parameter.parent; const classDeclaration = parameter.parent.parent; const parameterSymbol = this.getSymbol(constructorDeclaration.locals, parameterName, SymbolFlags.Value); const propertySymbol = this.getSymbol(classDeclaration.symbol.members, parameterName, SymbolFlags.Value); if (parameterSymbol && propertySymbol) { return [parameterSymbol, propertySymbol]; } Debug.fail("There should exist two symbols, one as property declaration and one as parameter declaration"); } public isBlockScopedNameDeclaredBeforeUse(declaration: Declaration, usage: Node): boolean { const __conv_self__ = this; const declarationFile = getSourceFileOfNode(declaration); const useFile = getSourceFileOfNode(usage); if (declarationFile !== useFile) { if ((this.modulekind && (declarationFile.externalModuleIndicator || useFile.externalModuleIndicator)) || (!this.compilerOptions.outFile && !this.compilerOptions.out) || this.isInTypeQuery(usage) || isInAmbientContext(declaration)) { // 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 = this.host.getSourceFiles(); return indexOf(sourceFiles, declarationFile) <= indexOf(sourceFiles, useFile); } if (declaration.pos <= usage.pos) { // 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 this.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); } 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 // 4. inside a static property initializer, a reference to a static method in the same class // or if usage is in a type context: // 1. inside a type query (typeof in type position) if (usage.parent.kind === SyntaxKind.ExportSpecifier) { // export specifiers do not use the variable, they only make it available for use return true; } const container = getEnclosingBlockScopeContainer(declaration); return this.isInTypeQuery(usage) || isUsedInFunctionOrInstanceProperty(usage, declaration, container); function isImmediatelyUsedInInitializerOfBlockScopedVariable(declaration: VariableDeclaration, usage: Node): boolean { const container = getEnclosingBlockScopeContainer(declaration); 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 (__conv_self__.isSameScopeDescendentOf(usage, declaration, container)) { return true; } break; } // ForIn/ForOf case - use site should not be used in expression part return isForInOrOfStatement(declaration.parent.parent) && __conv_self__.isSameScopeDescendentOf(usage, declaration.parent.parent.expression, container); } function isUsedInFunctionOrInstanceProperty(usage: Node, declaration: Node, container?: Node): boolean { return !!findAncestor(usage, current => { if (current === container) { return "quit"; } if (isFunctionLike(current)) { return true; } const initializerOfProperty = current.parent && current.parent.kind === SyntaxKind.PropertyDeclaration && (current.parent).initializer === current; if (initializerOfProperty) { if (hasModifier(current.parent, ModifierFlags.Static)) { if (declaration.kind === SyntaxKind.MethodDeclaration) { return true; } } else { const isDeclarationInstanceProperty = declaration.kind === SyntaxKind.PropertyDeclaration && !hasModifier(declaration, ModifierFlags.Static); if (!isDeclarationInstanceProperty || getContainingClass(usage) !== getContainingClass(declaration)) { return true; } } } }); } } // Resolve a given name for a given meaning at a given location. An error is reported if the name was not found and // the nameNotFoundMessage argument is not undefined. Returns the resolved symbol, or undefined if no symbol with // the given name can be found. public resolveName(location: Node | undefined, name: __String, meaning: SymbolFlags, nameNotFoundMessage: DiagnosticMessage | undefined, nameArg: __String | Identifier, suggestedNameNotFoundMessage?: DiagnosticMessage): Symbol { return this.resolveNameHelper(location, name, meaning, nameNotFoundMessage, nameArg, this.getSymbol, suggestedNameNotFoundMessage); } public resolveNameHelper(location: Node | undefined, name: __String, meaning: SymbolFlags, nameNotFoundMessage: DiagnosticMessage, nameArg: __String | Identifier, lookup: (symbols: SymbolTable, name: __String, meaning: SymbolFlags) => Symbol, suggestedNameNotFoundMessage?: DiagnosticMessage): Symbol { const originalLocation = location; // needed for did-you-mean error reporting, which gathers candidates starting from the original location let result: Symbol; let lastLocation: Node; let propertyWithInvalidInitializer: Node; const errorLocation = location; let grandparent: Node; let isInExternalModule = false; loop: while (location) { // Locals of a source file are not in scope (because they get merged into the global symbol table) if (location.locals && !this.isGlobalSourceFile(location)) { if (result = lookup(location.locals, name, meaning)) { let useResult = true; if (isFunctionLike(location) && lastLocation && lastLocation !== (location).body) { // symbol lookup restrictions for function-like declarations // - Type parameters of a function are in scope in the entire function declaration, including the parameter // list and return type. However, local types are only in scope in the function body. // - parameters are only in the scope of function body // This restriction does not apply to JSDoc comment types because they are parented // at a higher level than type parameters would normally be if (meaning & result.flags & SymbolFlags.Type && lastLocation.kind !== SyntaxKind.JSDocComment) { useResult = result.flags & SymbolFlags.TypeParameter ? lastLocation === (location).type || lastLocation.kind === SyntaxKind.Parameter || lastLocation.kind === SyntaxKind.TypeParameter : false; } if (meaning & SymbolFlags.Value && result.flags & SymbolFlags.FunctionScopedVariable) { // parameters are visible only inside function body, parameter list and return type // technically for parameter list case here we might mix parameters and variables declared in function, // however it is detected separately when checking initializers of parameters // to make sure that they reference no variables declared after them. useResult = lastLocation.kind === SyntaxKind.Parameter || (lastLocation === (location).type && result.valueDeclaration.kind === SyntaxKind.Parameter); } } if (useResult) { break loop; } else { result = undefined; } } } switch (location.kind) { case SyntaxKind.SourceFile: if (!isExternalOrCommonJsModule(location)) break; isInExternalModule = true; // falls through case SyntaxKind.ModuleDeclaration: const moduleExports = this.getSymbolOfNode(location).exports; if (location.kind === SyntaxKind.SourceFile || isAmbientModule(location)) { // It's an external module. First see if the module has an export default and if the local // name of that export default matches. if (result = moduleExports.get("default" as __String)) { const localSymbol = getLocalSymbolForExportDefault(result); if (localSymbol && (result.flags & meaning) && localSymbol.escapedName === name) { break loop; } result = undefined; } // Because of module/namespace merging, a module's exports are in scope, // yet we never want to treat an export specifier as putting a member in scope. // Therefore, if the name we find is purely an export specifier, it is not actually considered in scope. // Two things to note about this: // 1. We have to check this without calling getSymbol. The problem with calling getSymbol // on an export specifier is that it might find the export specifier itself, and try to // resolve it as an alias. This will cause the checker to consider the export specifier // a circular alias reference when it might not be. // 2. We check === SymbolFlags.Alias in order to check that the symbol is *purely* // an alias. If we used &, we'd be throwing out symbols that have non alias aspects, // which is not the desired behavior. const moduleExport = moduleExports.get(name); if (moduleExport && moduleExport.flags === SymbolFlags.Alias && getDeclarationOfKind(moduleExport, SyntaxKind.ExportSpecifier)) { break; } } if (result = lookup(moduleExports, name, meaning & SymbolFlags.ModuleMember)) { break loop; } break; case SyntaxKind.EnumDeclaration: if (result = lookup(this.getSymbolOfNode(location).exports, name, meaning & SymbolFlags.EnumMember)) { break loop; } break; case SyntaxKind.PropertyDeclaration: case SyntaxKind.PropertySignature: // TypeScript 1.0 spec (April 2014): 8.4.1 // Initializer expressions for instance member variables are evaluated in the scope // of the class constructor body but are not permitted to reference parameters or // local variables of the constructor. This effectively means that entities from outer scopes // by the same name as a constructor parameter or local variable are inaccessible // in initializer expressions for instance member variables. if (isClassLike(location.parent) && !hasModifier(location, ModifierFlags.Static)) { const ctor = this.findConstructorDeclaration((location.parent)); if (ctor && ctor.locals) { if (lookup(ctor.locals, name, meaning & SymbolFlags.Value)) { // Remember the property node, it will be used later to report appropriate error propertyWithInvalidInitializer = location; } } } break; case SyntaxKind.ClassDeclaration: case SyntaxKind.ClassExpression: case SyntaxKind.InterfaceDeclaration: if (result = lookup(this.getSymbolOfNode(location).members, name, meaning & SymbolFlags.Type)) { if (!this.isTypeParameterSymbolDeclaredInContainer(result, location)) { // ignore type parameters not declared in this container result = undefined; break; } if (lastLocation && hasModifier(lastLocation, ModifierFlags.Static)) { // TypeScript 1.0 spec (April 2014): 3.4.1 // The scope of a type parameter extends over the entire declaration with which the type // parameter list is associated, with the exception of static member declarations in classes. this.error(errorLocation, Diagnostics.Static_members_cannot_reference_class_type_parameters); return undefined; } break loop; } if (location.kind === SyntaxKind.ClassExpression && meaning & SymbolFlags.Class) { const className = (location).name; if (className && name === className.escapedText) { result = location.symbol; break loop; } } break; // It is not legal to reference a class's own type parameters from a computed property name that // belongs to the class. For example: // // function foo() { return '' } // class C { // <-- Class's own type parameter T // [foo()]() { } // <-- Reference to T from class's own computed property // } // case SyntaxKind.ComputedPropertyName: grandparent = location.parent.parent; if (isClassLike(grandparent) || grandparent.kind === SyntaxKind.InterfaceDeclaration) { // A reference to this grandparent's type parameters would be an error if (result = lookup(this.getSymbolOfNode(grandparent).members, name, meaning & SymbolFlags.Type)) { this.error(errorLocation, Diagnostics.A_computed_property_name_cannot_reference_a_type_parameter_from_its_containing_type); return undefined; } } break; case SyntaxKind.MethodDeclaration: case SyntaxKind.MethodSignature: case SyntaxKind.Constructor: case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: case SyntaxKind.FunctionDeclaration: case SyntaxKind.ArrowFunction: if (meaning & SymbolFlags.Variable && name === "arguments") { result = this.argumentsSymbol; break loop; } break; case SyntaxKind.FunctionExpression: if (meaning & SymbolFlags.Variable && name === "arguments") { result = this.argumentsSymbol; break loop; } if (meaning & SymbolFlags.Function) { const functionName = (location).name; if (functionName && name === functionName.escapedText) { result = location.symbol; break loop; } } break; case SyntaxKind.Decorator: // Decorators are resolved at the class declaration. Resolving at the parameter // or member would result in looking up locals in the method. // // function y() {} // class C { // method(@y x, y) {} // <-- decorator y should be resolved at the class declaration, not the parameter. // } // if (location.parent && location.parent.kind === SyntaxKind.Parameter) { location = location.parent; } // // function y() {} // class C { // @y method(x, y) {} // <-- decorator y should be resolved at the class declaration, not the method. // } // if (location.parent && isClassElement(location.parent)) { location = location.parent; } break; } lastLocation = location; location = location.parent; } // We just climbed up parents looking for the name, meaning that we started in a descendant node of `lastLocation`. // If `result === lastLocation.symbol`, that means that we are somewhere inside `lastLocation` looking up a name, and resolving to `lastLocation` itself. // That means that this is a self-reference of `lastLocation`, and shouldn't count this when considering whether `lastLocation` is used. if (result && nameNotFoundMessage && this.noUnusedIdentifiers && result !== lastLocation.symbol) { result.isReferenced = true; } if (!result) { result = lookup(this.globals, name, meaning); } if (!result) { if (nameNotFoundMessage) { if (!errorLocation || !this.checkAndReportErrorForMissingPrefix(errorLocation, name, nameArg) && !this.checkAndReportErrorForExtendingInterface(errorLocation) && !this.checkAndReportErrorForUsingTypeAsNamespace(errorLocation, name, meaning) && !this.checkAndReportErrorForUsingTypeAsValue(errorLocation, name, meaning) && !this.checkAndReportErrorForUsingNamespaceModuleAsValue(errorLocation, name, meaning)) { let suggestion: __String | undefined; if (suggestedNameNotFoundMessage && this.suggestionCount < this.maximumSuggestionCount) { suggestion = this.getSuggestionForNonexistentSymbol(originalLocation, name, meaning); if (suggestion) { this.error(errorLocation, suggestedNameNotFoundMessage, this.diagnosticName(nameArg), unescapeLeadingUnderscores(suggestion)); } } if (!suggestion) { this.error(errorLocation, nameNotFoundMessage, this.diagnosticName(nameArg)); } this.suggestionCount++; } } return undefined; } // Perform extra checks only if error reporting was requested if (nameNotFoundMessage) { if (propertyWithInvalidInitializer) { // 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. const propertyName = (propertyWithInvalidInitializer).name; this.error(errorLocation, Diagnostics.Initializer_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor, declarationNameToString(propertyName), this.diagnosticName(nameArg)); return undefined; } // 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 = this.getExportSymbolOfValueSymbolIfExported(result); if (exportOrLocalSymbol.flags & SymbolFlags.BlockScopedVariable || exportOrLocalSymbol.flags & SymbolFlags.Class || exportOrLocalSymbol.flags & SymbolFlags.Enum) { this.checkResolvedBlockScopedVariable(exportOrLocalSymbol, errorLocation); } } // If we're in an external module, we can't reference value symbols created from UMD export declarations if (result && isInExternalModule && (meaning & SymbolFlags.Value) === SymbolFlags.Value) { const decls = result.declarations; if (decls && decls.length === 1 && decls[0].kind === SyntaxKind.NamespaceExportDeclaration) { this.error(errorLocation, Diagnostics._0_refers_to_a_UMD_global_but_the_current_file_is_a_module_Consider_adding_an_import_instead, unescapeLeadingUnderscores(name)); } } } return result; } public diagnosticName(nameArg: __String | Identifier) { return typeof nameArg === "string" ? unescapeLeadingUnderscores(nameArg as __String) : declarationNameToString(nameArg as Identifier); } public isTypeParameterSymbolDeclaredInContainer(symbol: Symbol, container: Node) { for (const decl of symbol.declarations) { if (decl.kind === SyntaxKind.TypeParameter && decl.parent === container) { return true; } } return false; } public checkAndReportErrorForMissingPrefix(errorLocation: Node, name: __String, nameArg: __String | Identifier): boolean { if ((errorLocation.kind === SyntaxKind.Identifier && (this.isTypeReferenceIdentifier((errorLocation))) || this.isInTypeQuery(errorLocation))) { return false; } const container = getThisContainer(errorLocation, /*includeArrowFunctions*/ true); let location = container; while (location) { if (isClassLike(location.parent)) { const classSymbol = this.getSymbolOfNode(location.parent); if (!classSymbol) { break; } // Check to see if a static member exists. const constructorType = this.getTypeOfSymbol(classSymbol); if (this.getPropertyOfType(constructorType, name)) { this.error(errorLocation, Diagnostics.Cannot_find_name_0_Did_you_mean_the_static_member_1_0, this.diagnosticName(nameArg), this.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 && !hasModifier(location, ModifierFlags.Static)) { const instanceType = (this.getDeclaredTypeOfSymbol(classSymbol)).thisType; if (this.getPropertyOfType(instanceType, name)) { this.error(errorLocation, Diagnostics.Cannot_find_name_0_Did_you_mean_the_instance_member_this_0, this.diagnosticName(nameArg)); return true; } } } location = location.parent; } return false; } public checkAndReportErrorForExtendingInterface(errorLocation: Node): boolean { const expression = this.getEntityNameForExtendingInterface(errorLocation); const isError = !!(expression && this.resolveEntityName(expression, SymbolFlags.Interface, /*ignoreErrors*/ true)); if (isError) { this.error(errorLocation, Diagnostics.Cannot_extend_an_interface_0_Did_you_mean_implements, getTextOfNode(expression)); } return isError; } /** * Climbs up parents to an ExpressionWithTypeArguments, and returns its expression, * but returns undefined if that expression is not an EntityNameExpression. */ public getEntityNameForExtendingInterface(node: Node): EntityNameExpression | undefined { switch (node.kind) { case SyntaxKind.Identifier: case SyntaxKind.PropertyAccessExpression: return node.parent ? this.getEntityNameForExtendingInterface(node.parent) : undefined; case SyntaxKind.ExpressionWithTypeArguments: Debug.assert(isEntityNameExpression((node).expression)); return (node).expression; default: return undefined; } } public checkAndReportErrorForUsingTypeAsNamespace(errorLocation: Node, name: __String, meaning: SymbolFlags): boolean { if (meaning === SymbolFlags.Namespace) { const symbol = this.resolveSymbol(this.resolveName(errorLocation, name, SymbolFlags.Type & ~SymbolFlags.Value, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined)); 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 = this.getPropertyOfType(this.getDeclaredTypeOfSymbol(symbol), propName); if (propType) { this.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; } } this.error(errorLocation, Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_namespace_here, unescapeLeadingUnderscores(name)); return true; } } return false; } public checkAndReportErrorForUsingTypeAsValue(errorLocation: Node, name: __String, meaning: SymbolFlags): boolean { if (meaning & (SymbolFlags.Value & ~SymbolFlags.NamespaceModule)) { if (name === "any" || name === "string" || name === "number" || name === "boolean" || name === "never") { this.error(errorLocation, Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_value_here, unescapeLeadingUnderscores(name)); return true; } const symbol = this.resolveSymbol(this.resolveName(errorLocation, name, SymbolFlags.Type & ~SymbolFlags.Value, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined)); if (symbol && !(symbol.flags & SymbolFlags.NamespaceModule)) { this.error(errorLocation, Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_value_here, unescapeLeadingUnderscores(name)); return true; } } return false; } public checkAndReportErrorForUsingNamespaceModuleAsValue(errorLocation: Node, name: __String, meaning: SymbolFlags): boolean { if (meaning & (SymbolFlags.Value & ~SymbolFlags.NamespaceModule & ~SymbolFlags.Type)) { const symbol = this.resolveSymbol(this.resolveName(errorLocation, name, SymbolFlags.NamespaceModule & ~SymbolFlags.Value, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined)); if (symbol) { this.error(errorLocation, Diagnostics.Cannot_use_namespace_0_as_a_value, unescapeLeadingUnderscores(name)); return true; } } else if (meaning & (SymbolFlags.Type & ~SymbolFlags.NamespaceModule & ~SymbolFlags.Value)) { const symbol = this.resolveSymbol(this.resolveName(errorLocation, name, SymbolFlags.NamespaceModule & ~SymbolFlags.Type, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined)); if (symbol) { this.error(errorLocation, Diagnostics.Cannot_use_namespace_0_as_a_type, unescapeLeadingUnderscores(name)); return true; } } return false; } public checkResolvedBlockScopedVariable(result: Symbol, errorLocation: Node): void { Debug.assert(!!(result.flags & SymbolFlags.BlockScopedVariable || result.flags & SymbolFlags.Class || result.flags & SymbolFlags.Enum)); // Block-scoped variables cannot be used before their definition const declaration = forEach(result.declarations, d => isBlockOrCatchScoped(d) || isClassLike(d) || (d.kind === SyntaxKind.EnumDeclaration) ? d : undefined); Debug.assert(declaration !== undefined, "Declaration to checkResolvedBlockScopedVariable is undefined"); if (!isInAmbientContext(declaration) && !this.isBlockScopedNameDeclaredBeforeUse(declaration, errorLocation)) { if (result.flags & SymbolFlags.BlockScopedVariable) { this.error(errorLocation, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, declarationNameToString(getNameOfDeclaration(declaration))); } else if (result.flags & SymbolFlags.Class) { this.error(errorLocation, Diagnostics.Class_0_used_before_its_declaration, declarationNameToString(getNameOfDeclaration(declaration))); } else if (result.flags & SymbolFlags.RegularEnum) { this.error(errorLocation, Diagnostics.Enum_0_used_before_its_declaration, declarationNameToString(getNameOfDeclaration(declaration))); } } } /* 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. * Return false if 'stopAt' node is reached or isFunctionLike(current) === true. */ public isSameScopeDescendentOf(initial: Node, parent: Node, stopAt: Node): boolean { return parent && !!findAncestor(initial, n => n === stopAt || isFunctionLike(n) ? "quit" : n === parent); } public getAnyImportSyntax(node: Node): AnyImportSyntax { if (isAliasSymbolDeclaration(node)) { if (node.kind === SyntaxKind.ImportEqualsDeclaration) { return node; } return findAncestor(node, isImportDeclaration); } } public getDeclarationOfAliasSymbol(symbol: Symbol): Declaration | undefined { return find(symbol.declarations, isAliasSymbolDeclaration); } public getTargetOfImportEqualsDeclaration(node: ImportEqualsDeclaration, dontResolveAlias: boolean): Symbol { if (node.moduleReference.kind === SyntaxKind.ExternalModuleReference) { return this.resolveExternalModuleSymbol(this.resolveExternalModuleName(node, getExternalModuleImportEqualsDeclarationExpression(node))); } return this.getSymbolOfPartOfRightHandSideOfImportEquals((node.moduleReference), dontResolveAlias); } public getTargetOfImportClause(node: ImportClause, dontResolveAlias: boolean): Symbol { const moduleSymbol = this.resolveExternalModuleName(node, (node.parent).moduleSpecifier); if (moduleSymbol) { let exportDefaultSymbol: Symbol; if (isShorthandAmbientModuleSymbol(moduleSymbol)) { exportDefaultSymbol = moduleSymbol; } else { const exportValue = moduleSymbol.exports.get("export=" as __String); exportDefaultSymbol = exportValue ? this.getPropertyOfType(this.getTypeOfSymbol(exportValue), ("default" as __String)) : this.resolveSymbol(moduleSymbol.exports.get("default" as __String), dontResolveAlias); } if (!exportDefaultSymbol && !this.allowSyntheticDefaultImports) { this.error(node.name, Diagnostics.Module_0_has_no_default_export, this.symbolToString(moduleSymbol)); } else if (!exportDefaultSymbol && this.allowSyntheticDefaultImports) { return this.resolveExternalModuleSymbol(moduleSymbol, dontResolveAlias) || this.resolveSymbol(moduleSymbol, dontResolveAlias); } return exportDefaultSymbol; } } public getTargetOfNamespaceImport(node: NamespaceImport, dontResolveAlias: boolean): Symbol { const moduleSpecifier = (node.parent.parent).moduleSpecifier; return this.resolveESModuleSymbol(this.resolveExternalModuleName(node, moduleSpecifier), moduleSpecifier, dontResolveAlias); } // 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'. public combineValueAndTypeSymbols(valueSymbol: Symbol, typeSymbol: Symbol): Symbol { if (valueSymbol === this.unknownSymbol && typeSymbol === this.unknownSymbol) { return this.unknownSymbol; } if (valueSymbol.flags & (SymbolFlags.Type | SymbolFlags.Namespace)) { return valueSymbol; } const result = this.createSymbol(valueSymbol.flags | typeSymbol.flags, valueSymbol.escapedName); result.declarations = concatenate(valueSymbol.declarations, typeSymbol.declarations); result.parent = valueSymbol.parent || typeSymbol.parent; if (valueSymbol.valueDeclaration) result.valueDeclaration = valueSymbol.valueDeclaration; if (typeSymbol.members) result.members = typeSymbol.members; if (valueSymbol.exports) result.exports = valueSymbol.exports; return result; } public getExportOfModule(symbol: Symbol, name: __String, dontResolveAlias: boolean): Symbol { if (symbol.flags & SymbolFlags.Module) { return this.resolveSymbol(this.getExportsOfSymbol(symbol).get(name), dontResolveAlias); } } public getPropertyOfVariable(symbol: Symbol, name: __String): Symbol { if (symbol.flags & SymbolFlags.Variable) { const typeAnnotation = (symbol.valueDeclaration).type; if (typeAnnotation) { return this.resolveSymbol(this.getPropertyOfType(this.getTypeFromTypeNode(typeAnnotation), name)); } } } public getExternalModuleMember(node: ImportDeclaration | ExportDeclaration, specifier: ImportOrExportSpecifier, dontResolveAlias?: boolean): Symbol { const moduleSymbol = this.resolveExternalModuleName(node, node.moduleSpecifier); const targetSymbol = this.resolveESModuleSymbol(moduleSymbol, node.moduleSpecifier, dontResolveAlias); if (targetSymbol) { const name = specifier.propertyName || specifier.name; if (name.escapedText) { if (isShorthandAmbientModuleSymbol(moduleSymbol)) { return moduleSymbol; } let symbolFromVariable: Symbol; // First check if module was specified with "export=". If so, get the member from the resolved type if (moduleSymbol && moduleSymbol.exports && moduleSymbol.exports.get("export=" as __String)) { symbolFromVariable = this.getPropertyOfType(this.getTypeOfSymbol(targetSymbol), name.escapedText); } else { symbolFromVariable = this.getPropertyOfVariable(targetSymbol, name.escapedText); } // if symbolFromVariable is export - get its final target symbolFromVariable = this.resolveSymbol(symbolFromVariable, dontResolveAlias); let symbolFromModule = this.getExportOfModule(targetSymbol, name.escapedText, dontResolveAlias); // If the export member we're looking for is default, and there is no real default but allowSyntheticDefaultImports is on, return the entire module as the default if (!symbolFromModule && this.allowSyntheticDefaultImports && name.escapedText === "default") { symbolFromModule = this.resolveExternalModuleSymbol(moduleSymbol, dontResolveAlias) || this.resolveSymbol(moduleSymbol, dontResolveAlias); } const symbol = symbolFromModule && symbolFromVariable ? this.combineValueAndTypeSymbols(symbolFromVariable, symbolFromModule) : symbolFromModule || symbolFromVariable; if (!symbol) { this.error(name, Diagnostics.Module_0_has_no_exported_member_1, this.getFullyQualifiedName(moduleSymbol), declarationNameToString(name)); } return symbol; } } } public getTargetOfImportSpecifier(node: ImportSpecifier, dontResolveAlias: boolean): Symbol { return this.getExternalModuleMember((node.parent.parent.parent), node, dontResolveAlias); } public getTargetOfNamespaceExportDeclaration(node: NamespaceExportDeclaration, dontResolveAlias: boolean): Symbol { return this.resolveExternalModuleSymbol(node.parent.symbol, dontResolveAlias); } public getTargetOfExportSpecifier(node: ExportSpecifier, meaning: SymbolFlags, dontResolveAlias?: boolean) { return node.parent.parent.moduleSpecifier ? this.getExternalModuleMember(node.parent.parent, node, dontResolveAlias) : this.resolveEntityName(node.propertyName || node.name, meaning, /*ignoreErrors*/ false, dontResolveAlias); } public getTargetOfExportAssignment(node: ExportAssignment, dontResolveAlias: boolean): Symbol { return this.resolveEntityName((node.expression), SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace, /*ignoreErrors*/ false, dontResolveAlias); } public getTargetOfAliasDeclaration(node: Declaration, dontRecursivelyResolve?: boolean): Symbol { switch (node.kind) { case SyntaxKind.ImportEqualsDeclaration: return this.getTargetOfImportEqualsDeclaration((node), dontRecursivelyResolve); case SyntaxKind.ImportClause: return this.getTargetOfImportClause((node), dontRecursivelyResolve); case SyntaxKind.NamespaceImport: return this.getTargetOfNamespaceImport((node), dontRecursivelyResolve); case SyntaxKind.ImportSpecifier: return this.getTargetOfImportSpecifier((node), dontRecursivelyResolve); case SyntaxKind.ExportSpecifier: return this.getTargetOfExportSpecifier((node), SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace, dontRecursivelyResolve); case SyntaxKind.ExportAssignment: return this.getTargetOfExportAssignment((node), dontRecursivelyResolve); case SyntaxKind.NamespaceExportDeclaration: return this.getTargetOfNamespaceExportDeclaration((node), dontRecursivelyResolve); } } /** * Indicates that a symbol is an alias that does not merge with a local declaration. */ public isNonLocalAlias(symbol: Symbol, excludes = SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace) { return symbol && (symbol.flags & (SymbolFlags.Alias | excludes)) === SymbolFlags.Alias; } public resolveSymbol(symbol: Symbol, dontResolveAlias?: boolean): Symbol { const shouldResolve = !dontResolveAlias && this.isNonLocalAlias(symbol); return shouldResolve ? this.resolveAlias(symbol) : symbol; } public resolveAlias(symbol: Symbol): Symbol { Debug.assert((symbol.flags & SymbolFlags.Alias) !== 0, "Should only get Alias here."); const links = this.getSymbolLinks(symbol); if (!links.target) { links.target = this.resolvingSymbol; const node = this.getDeclarationOfAliasSymbol(symbol); Debug.assert(!!node); const target = this.getTargetOfAliasDeclaration(node); if (links.target === this.resolvingSymbol) { links.target = target || this.unknownSymbol; } else { this.error(node, Diagnostics.Circular_definition_of_import_alias_0, this.symbolToString(symbol)); } } else if (links.target === this.resolvingSymbol) { links.target = this.unknownSymbol; } return links.target; } public markExportAsReferenced(node: ImportEqualsDeclaration | ExportAssignment | ExportSpecifier) { const symbol = this.getSymbolOfNode(node); const target = this.resolveAlias(symbol); if (target) { const markAlias = target === this.unknownSymbol || ((target.flags & SymbolFlags.Value) && !this.isConstEnumOrConstEnumOnlyModule(target)); if (markAlias) { this.markAliasSymbolAsReferenced(symbol); } } } // When an alias symbol is referenced, we need to mark the entity it references as referenced and in turn repeat that until // we reach a non-alias or an exported entity (which is always considered referenced). We do this by checking the target of // the alias as an expression (which recursively takes us back here if the target references another alias). public markAliasSymbolAsReferenced(symbol: Symbol) { const links = this.getSymbolLinks(symbol); if (!links.referenced) { links.referenced = true; const node = this.getDeclarationOfAliasSymbol(symbol); Debug.assert(!!node); if (node.kind === SyntaxKind.ExportAssignment) { // export default this.checkExpressionCached((node).expression); } else if (node.kind === SyntaxKind.ExportSpecifier) { // export { } or export { as foo } this.checkExpressionCached((node).propertyName || (node).name); } else if (isInternalModuleImportEqualsDeclaration(node)) { // import foo = this.checkExpressionCached(((node).moduleReference)); } } } // This function is only for imports with entity names public getSymbolOfPartOfRightHandSideOfImportEquals(entityName: EntityName, dontResolveAlias?: boolean): Symbol { // 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; } // Check for case 1 and 3 in the above example if (entityName.kind === SyntaxKind.Identifier || entityName.parent.kind === SyntaxKind.QualifiedName) { return this.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 this.resolveEntityName(entityName, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace, /*ignoreErrors*/ false, dontResolveAlias); } } public getFullyQualifiedName(symbol: Symbol): string { return symbol.parent ? this.getFullyQualifiedName(symbol.parent) + "." + this.symbolToString(symbol) : this.symbolToString(symbol); } /** * Resolves a qualified name and any involved aliases. */ public resolveEntityName(name: EntityNameOrEntityNameExpression, meaning: SymbolFlags, ignoreErrors?: boolean, dontResolveAlias?: boolean, location?: Node): Symbol | undefined { if (nodeIsMissing(name)) { return undefined; } let symbol: Symbol; if (name.kind === SyntaxKind.Identifier) { const message = meaning === SymbolFlags.Namespace ? Diagnostics.Cannot_find_namespace_0 : Diagnostics.Cannot_find_name_0; symbol = this.resolveName(location || name, name.escapedText, meaning, ignoreErrors ? undefined : message, name); if (!symbol) { return undefined; } } else if (name.kind === SyntaxKind.QualifiedName || name.kind === SyntaxKind.PropertyAccessExpression) { let left: EntityNameOrEntityNameExpression; if (name.kind === SyntaxKind.QualifiedName) { left = (name).left; } else if (name.kind === SyntaxKind.PropertyAccessExpression && (name.expression.kind === SyntaxKind.ParenthesizedExpression || isEntityNameExpression(name.expression))) { left = name.expression; } else { // If the expression in property-access expression is not entity-name or parenthsizedExpression (e.g. it is a call expression), it won't be able to successfully resolve the name. // This is the case when we are trying to do any language service operation in heritage clauses. By return undefined, the getSymbolOfEntityNameOrPropertyAccessExpression // will attempt to checkPropertyAccessExpression to resolve symbol. // i.e class C extends foo()./*do language service operation here*/B {} return undefined; } const right = name.kind === SyntaxKind.QualifiedName ? name.right : name.name; const namespace = this.resolveEntityName(left, SymbolFlags.Namespace, ignoreErrors, /*dontResolveAlias*/ false, location); if (!namespace || nodeIsMissing(right)) { return undefined; } else if (namespace === this.unknownSymbol) { return namespace; } symbol = this.getSymbol(this.getExportsOfSymbol(namespace), right.escapedText, meaning); if (!symbol) { if (!ignoreErrors) { this.error(right, Diagnostics.Namespace_0_has_no_exported_member_1, this.getFullyQualifiedName(namespace), declarationNameToString(right)); } return undefined; } } else if (name.kind === SyntaxKind.ParenthesizedExpression) { // If the expression in parenthesizedExpression is not an entity-name (e.g. it is a call expression), it won't be able to successfully resolve the name. // This is the case when we are trying to do any language service operation in heritage clauses. // By return undefined, the getSymbolOfEntityNameOrPropertyAccessExpression will attempt to checkPropertyAccessExpression to resolve symbol. // i.e class C extends foo()./*do language service operation here*/B {} return isEntityNameExpression(name.expression) ? this.resolveEntityName((name.expression as EntityNameOrEntityNameExpression), meaning, ignoreErrors, dontResolveAlias, location) : undefined; } else { Debug.fail("Unknown entity name kind."); } Debug.assert((getCheckFlags(symbol) & CheckFlags.Instantiated) === 0, "Should never get an instantiated symbol here."); return (symbol.flags & meaning) || dontResolveAlias ? symbol : this.resolveAlias(symbol); } public resolveExternalModuleName(location: Node, moduleReferenceExpression: Expression): Symbol { return this.resolveExternalModuleNameWorker(location, moduleReferenceExpression, Diagnostics.Cannot_find_module_0); } public resolveExternalModuleNameWorker(location: Node, moduleReferenceExpression: Expression, moduleNotFoundError: DiagnosticMessage, isForAugmentation = false): Symbol { if (moduleReferenceExpression.kind !== SyntaxKind.StringLiteral && moduleReferenceExpression.kind !== SyntaxKind.NoSubstitutionTemplateLiteral) { return; } const moduleReferenceLiteral = moduleReferenceExpression; return this.resolveExternalModule(location, moduleReferenceLiteral.text, moduleNotFoundError, moduleReferenceLiteral, isForAugmentation); } public resolveExternalModule(location: Node, moduleReference: string, moduleNotFoundError: DiagnosticMessage, errorNode: Node, isForAugmentation = false): Symbol { if (moduleReference === undefined) { return; } if (startsWith(moduleReference, "@types/")) { const diag = Diagnostics.Cannot_import_type_declaration_files_Consider_importing_0_instead_of_1; const withoutAtTypePrefix = removePrefix(moduleReference, "@types/"); this.error(errorNode, diag, withoutAtTypePrefix, moduleReference); } const ambientModule = this.tryFindAmbientModule(moduleReference, /*withAugmentations*/ true); if (ambientModule) { return ambientModule; } const isRelative = isExternalModuleNameRelative(moduleReference); const resolvedModule = getResolvedModule(getSourceFileOfNode(location), moduleReference); const resolutionDiagnostic = resolvedModule && getResolutionDiagnostic(this.compilerOptions, resolvedModule); const sourceFile = resolvedModule && !resolutionDiagnostic && this.host.getSourceFile(resolvedModule.resolvedFileName); if (sourceFile) { if (sourceFile.symbol) { // merged symbol is module declaration symbol combined with all augmentations return this.getMergedSymbol(sourceFile.symbol); } if (moduleNotFoundError) { // report errors only if it was requested this.error(errorNode, Diagnostics.File_0_is_not_a_module, sourceFile.fileName); } return undefined; } if (this.patternAmbientModules) { const pattern = findBestPatternMatch(this.patternAmbientModules, _ => _.pattern, moduleReference); if (pattern) { return this.getMergedSymbol(pattern.symbol); } } // May be an untyped module. If so, ignore resolutionDiagnostic. if (!isRelative && resolvedModule && !extensionIsTypeScript(resolvedModule.extension)) { if (isForAugmentation) { const diag = Diagnostics.Invalid_module_name_in_augmentation_Module_0_resolves_to_an_untyped_module_at_1_which_cannot_be_augmented; this.error(errorNode, diag, moduleReference, resolvedModule.resolvedFileName); } else if (this.noImplicitAny && moduleNotFoundError) { let errorInfo = chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Try_npm_install_types_Slash_0_if_it_exists_or_add_a_new_declaration_d_ts_file_containing_declare_module_0, moduleReference); errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Could_not_find_a_declaration_file_for_module_0_1_implicitly_has_an_any_type, moduleReference, resolvedModule.resolvedFileName); this.diagnostics.add(createDiagnosticForNodeFromMessageChain(errorNode, errorInfo)); } // 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) { // report errors only if it was requested if (resolutionDiagnostic) { this.error(errorNode, resolutionDiagnostic, moduleReference, resolvedModule.resolvedFileName); } else { const tsExtension = tryExtractTypeScriptExtension(moduleReference); if (tsExtension) { const diag = Diagnostics.An_import_path_cannot_end_with_a_0_extension_Consider_importing_1_instead; this.error(errorNode, diag, tsExtension, removeExtension(moduleReference, tsExtension)); } else { this.error(errorNode, moduleNotFoundError, moduleReference); } } } return undefined; } // An external module with an 'export =' declaration resolves to the target of the 'export =' declaration, // and an external module with no 'export =' declaration resolves to the module itself. public resolveExternalModuleSymbol(moduleSymbol: Symbol, dontResolveAlias?: boolean): Symbol { return moduleSymbol && this.getMergedSymbol(this.resolveSymbol(moduleSymbol.exports.get(InternalSymbolName.ExportEquals), dontResolveAlias)) || moduleSymbol; } // 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). public resolveESModuleSymbol(moduleSymbol: Symbol, moduleReferenceExpression: Expression, dontResolveAlias: boolean): Symbol { const symbol = this.resolveExternalModuleSymbol(moduleSymbol, dontResolveAlias); if (!dontResolveAlias && symbol && !(symbol.flags & (SymbolFlags.Module | SymbolFlags.Variable))) { this.error(moduleReferenceExpression, Diagnostics.Module_0_resolves_to_a_non_module_entity_and_cannot_be_imported_using_this_construct, this.symbolToString(moduleSymbol)); } return symbol; } public hasExportAssignmentSymbol(moduleSymbol: Symbol): boolean { return moduleSymbol.exports.get(InternalSymbolName.ExportEquals) !== undefined; } public getExportsOfModuleAsArray(moduleSymbol: Symbol): Symbol[] { return this.symbolsToArray(this.getExportsOfModule(moduleSymbol)); } public getExportsAndPropertiesOfModule(moduleSymbol: Symbol): Symbol[] { const exports = this.getExportsOfModuleAsArray(moduleSymbol); const exportEquals = this.resolveExternalModuleSymbol(moduleSymbol); if (exportEquals !== moduleSymbol) { addRange(exports, this.getPropertiesOfType(this.getTypeOfSymbol(exportEquals))); } return exports; } public tryGetMemberInModuleExports(memberName: __String, moduleSymbol: Symbol): Symbol | undefined { const symbolTable = this.getExportsOfModule(moduleSymbol); if (symbolTable) { return symbolTable.get(memberName); } } public tryGetMemberInModuleExportsAndProperties(memberName: __String, moduleSymbol: Symbol): Symbol | undefined { const symbol = this.tryGetMemberInModuleExports(memberName, moduleSymbol); if (!symbol) { const exportEquals = this.resolveExternalModuleSymbol(moduleSymbol); if (exportEquals !== moduleSymbol) { return this.getPropertyOfType(this.getTypeOfSymbol(exportEquals), memberName); } } return symbol; } public getExportsOfSymbol(symbol: Symbol): SymbolTable { return symbol.flags & SymbolFlags.Module ? this.getExportsOfModule(symbol) : symbol.exports || this.emptySymbols; } public getExportsOfModule(moduleSymbol: Symbol): SymbolTable { const links = this.getSymbolLinks(moduleSymbol); return links.resolvedExports || (links.resolvedExports = this.getExportsOfModuleWorker(moduleSymbol)); } /** * 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 */ public extendExportSymbols(target: SymbolTable, source: SymbolTable, lookupTable?: ExportCollisionTrackerTable, exportNode?: ExportDeclaration) { const __conv_self__ = this; source && source.forEach((sourceSymbol, id) => { if (id === "default") return; const targetSymbol = target.get(id); if (!targetSymbol) { target.set(id, sourceSymbol); if (lookupTable && exportNode) { lookupTable.set(id, { specifierText: getTextOfNode(exportNode.moduleSpecifier) } as ExportCollisionTracker); } } else if (lookupTable && exportNode && targetSymbol && __conv_self__.resolveSymbol(targetSymbol) !== __conv_self__.resolveSymbol(sourceSymbol)) { const collisionTracker = lookupTable.get(id); if (!collisionTracker.exportsWithDuplicate) { collisionTracker.exportsWithDuplicate = [exportNode]; } else { collisionTracker.exportsWithDuplicate.push(exportNode); } } }); } public getExportsOfModuleWorker(moduleSymbol: Symbol): SymbolTable { const __conv_self__ = this; const visitedSymbols: Symbol[] = []; // A module defined by an 'export=' consists on one export that needs to be resolved moduleSymbol = this.resolveExternalModuleSymbol(moduleSymbol); return visit(moduleSymbol) || this.emptySymbols; // 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): SymbolTable { if (!(symbol && symbol.flags & SymbolFlags.HasExports && !contains(visitedSymbols, symbol))) { return; } visitedSymbols.push(symbol); const symbols = cloneMap(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 = createMap() as ExportCollisionTrackerTable; for (const node of exportStars.declarations) { const resolvedModule = __conv_self__.resolveExternalModuleName(node, (node as ExportDeclaration).moduleSpecifier); const exportedSymbols = visit(resolvedModule); __conv_self__.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) { __conv_self__.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))); } }); __conv_self__.extendExportSymbols(symbols, nestedSymbols); } return symbols; } } public getMergedSymbol(symbol: Symbol): Symbol { let merged: Symbol; return symbol && symbol.mergeId && (merged = this.mergedSymbols[symbol.mergeId]) ? merged : symbol; } public getSymbolOfNode(node: Node): Symbol { return this.getMergedSymbol(node.symbol); } public getParentOfSymbol(symbol: Symbol): Symbol { return this.getMergedSymbol(symbol.parent); } public getExportSymbolOfValueSymbolIfExported(symbol: Symbol): Symbol { return symbol && (symbol.flags & SymbolFlags.ExportValue) !== 0 ? this.getMergedSymbol(symbol.exportSymbol) : symbol; } public symbolIsValue(symbol: Symbol): boolean { return !!(symbol.flags & SymbolFlags.Value || symbol.flags & SymbolFlags.Alias && this.resolveAlias(symbol).flags & SymbolFlags.Value); } public findConstructorDeclaration(node: ClassLikeDeclaration): ConstructorDeclaration { const members = node.members; for (const member of members) { if (member.kind === SyntaxKind.Constructor && nodeIsPresent((member).body)) { return member; } } } public createType(flags: TypeFlags): Type { const result = new this.Type(this.checker, flags); this.typeCount++; result.id = this.typeCount; return result; } public createIntrinsicType(kind: TypeFlags, intrinsicName: string): IntrinsicType { const type = (this.createType(kind)); type.intrinsicName = intrinsicName; return type; } public createBooleanType(trueFalseTypes: Type[]): IntrinsicType & UnionType { const type = (this.getUnionType(trueFalseTypes)); type.flags |= TypeFlags.Boolean; type.intrinsicName = "boolean"; return type; } public createObjectType(objectFlags: ObjectFlags, symbol?: Symbol): ObjectType { const type = (this.createType(TypeFlags.Object)); type.objectFlags = objectFlags; type.symbol = symbol; return type; } public createTypeofType() { return this.getUnionType(arrayFrom(this.typeofEQFacts.keys(), this.getLiteralType)); } // A reserved member name starts with two underscores, but the third character cannot be an underscore // or the @ symbol. A third underscore indicates an escaped form of an identifer that started // with at least two underscores. The @ character indicates that the name is denoted by a well known ES // Symbol instance. public 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; } public getNamedMembers(members: SymbolTable): Symbol[] { const __conv_self__ = this; let result: Symbol[]; members.forEach((symbol, id) => { if (!__conv_self__.isReservedMemberName(id)) { if (!result) result = []; if (__conv_self__.symbolIsValue(symbol)) { result.push(symbol); } } }); return result || emptyArray; } public setStructuredTypeMembers(type: StructuredType, members: SymbolTable, callSignatures: Signature[], constructSignatures: Signature[], stringIndexInfo: IndexInfo, numberIndexInfo: IndexInfo): ResolvedType { (type).members = members; (type).properties = this.getNamedMembers(members); (type).callSignatures = callSignatures; (type).constructSignatures = constructSignatures; if (stringIndexInfo) (type).stringIndexInfo = stringIndexInfo; if (numberIndexInfo) (type).numberIndexInfo = numberIndexInfo; return type; } public createAnonymousType(symbol: Symbol, members: SymbolTable, callSignatures: Signature[], constructSignatures: Signature[], stringIndexInfo: IndexInfo, numberIndexInfo: IndexInfo): ResolvedType { return this.setStructuredTypeMembers(this.createObjectType(ObjectFlags.Anonymous, symbol), members, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo); } public forEachSymbolTableInScope(enclosingDeclaration: Node, callback: (symbolTable: SymbolTable) => 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 (location.locals && !this.isGlobalSourceFile(location)) { if (result = callback(location.locals)) { return result; } } switch (location.kind) { case SyntaxKind.SourceFile: if (!isExternalOrCommonJsModule(location)) { break; } // falls through case SyntaxKind.ModuleDeclaration: if (result = callback(this.getSymbolOfNode(location).exports)) { return result; } break; } } return callback(this.globals); } public 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; } public getAccessibleSymbolChain(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags, useOnlyExternalAliasing: boolean): Symbol[] { const __conv_self__ = this; function getAccessibleSymbolChainFromSymbolTable(symbols: SymbolTable) { return getAccessibleSymbolChainFromSymbolTableWorker(symbols, []); } function getAccessibleSymbolChainFromSymbolTableWorker(symbols: SymbolTable, visitedSymbolTables: SymbolTable[]): Symbol[] { if (contains(visitedSymbolTables, symbols)) { return undefined; } visitedSymbolTables.push(symbols); const result = trySymbolTable(symbols); 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 if (!__conv_self__.needsQualification(symbolFromSymbolTable, enclosingDeclaration, meaning)) { return true; } // If symbol needs qualification, make sure that parent is accessible, if it is then this symbol is accessible too const accessibleParent = __conv_self__.getAccessibleSymbolChain(symbolFromSymbolTable.parent, enclosingDeclaration, __conv_self__.getQualifiedLeftMeaning(meaning), useOnlyExternalAliasing); return !!accessibleParent; } function isAccessible(symbolFromSymbolTable: Symbol, resolvedAliasSymbol?: Symbol) { if (symbol === (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 return !forEach(symbolFromSymbolTable.declarations, __conv_self__.hasExternalModuleSymbol) && canQualifySymbol(symbolFromSymbolTable, meaning); } } function trySymbolTable(symbols: SymbolTable) { // If symbol is directly available by its name in the symbol table if (isAccessible(symbols.get(symbol.escapedName))) { return [symbol]; } // Check if symbol is any of the alias return forEachEntry(symbols, symbolFromSymbolTable => { if (symbolFromSymbolTable.flags & SymbolFlags.Alias && symbolFromSymbolTable.escapedName !== "export=" && !getDeclarationOfKind(symbolFromSymbolTable, SyntaxKind.ExportSpecifier)) { if (!useOnlyExternalAliasing || // Is this external alias, then use it to name ts.forEach(symbolFromSymbolTable.declarations, isExternalModuleImportEqualsDeclaration)) { const resolvedImportedSymbol = __conv_self__.resolveAlias(symbolFromSymbolTable); if (isAccessible(symbolFromSymbolTable, resolvedImportedSymbol)) { 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 accessibleSymbolsFromExports = resolvedImportedSymbol.exports ? getAccessibleSymbolChainFromSymbolTableWorker(resolvedImportedSymbol.exports, visitedSymbolTables) : undefined; if (accessibleSymbolsFromExports && canQualifySymbol(symbolFromSymbolTable, __conv_self__.getQualifiedLeftMeaning(meaning))) { return [symbolFromSymbolTable].concat(accessibleSymbolsFromExports); } } } }); } } if (symbol && !this.isPropertyOrMethodDeclarationSymbol(symbol)) { return this.forEachSymbolTableInScope(enclosingDeclaration, getAccessibleSymbolChainFromSymbolTable); } } public needsQualification(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags) { const __conv_self__ = this; let qualify = false; this.forEachSymbolTableInScope(enclosingDeclaration, symbolTable => { // If symbol of this name is not available in the symbol table we are ok let symbolFromSymbolTable = 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 symbolFromSymbolTable = (symbolFromSymbolTable.flags & SymbolFlags.Alias && !getDeclarationOfKind(symbolFromSymbolTable, SyntaxKind.ExportSpecifier)) ? __conv_self__.resolveAlias(symbolFromSymbolTable) : symbolFromSymbolTable; if (symbolFromSymbolTable.flags & meaning) { qualify = true; return true; } // Continue to the next symbol table return false; }); return qualify; } public 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; } /** * 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 */ public isSymbolAccessible(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags, shouldComputeAliasesToMakeVisible: boolean): SymbolAccessibilityResult { const __conv_self__ = this; if (symbol && enclosingDeclaration && !(symbol.flags & SymbolFlags.TypeParameter)) { const initialSymbol = symbol; let meaningToLook = meaning; while (symbol) { // Symbol is accessible if it by itself is accessible const accessibleSymbolChain = this.getAccessibleSymbolChain(symbol, enclosingDeclaration, meaningToLook, /*useOnlyExternalAliasing*/ false); if (accessibleSymbolChain) { const hasAccessibleDeclarations = this.hasVisibleDeclarations(accessibleSymbolChain[0], shouldComputeAliasesToMakeVisible); if (!hasAccessibleDeclarations) { return { accessibility: SymbolAccessibility.NotAccessible, errorSymbolName: this.symbolToString(initialSymbol, enclosingDeclaration, meaning), errorModuleName: symbol !== initialSymbol ? this.symbolToString(symbol, enclosingDeclaration, SymbolFlags.Namespace) : undefined, }; } return hasAccessibleDeclarations; } // 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 meaningToLook = this.getQualifiedLeftMeaning(meaning); symbol = this.getParentOfSymbol(symbol); } // 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(initialSymbol.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: this.symbolToString(initialSymbol, enclosingDeclaration, meaning), errorModuleName: this.symbolToString(symbolExternalModule) }; } } // Just a local name that is not accessible return { accessibility: SymbolAccessibility.NotAccessible, errorSymbolName: this.symbolToString(initialSymbol, enclosingDeclaration, meaning), }; } return { accessibility: SymbolAccessibility.Accessible }; function getExternalModuleContainer(declaration: Node) { const node = findAncestor(declaration, __conv_self__.hasExternalModuleSymbol); return node && __conv_self__.getSymbolOfNode(node); } } public hasExternalModuleSymbol(declaration: Node) { return isAmbientModule(declaration) || (declaration.kind === SyntaxKind.SourceFile && isExternalOrCommonJsModule(declaration)); } public hasVisibleDeclarations(symbol: Symbol, shouldComputeAliasToMakeVisible: boolean): SymbolVisibilityResult { const __conv_self__ = this; let aliasesToMakeVisible: AnyImportSyntax[]; if (forEach(symbol.declarations, declaration => !getIsDeclarationVisible(declaration))) { return undefined; } return { accessibility: SymbolAccessibility.Accessible, aliasesToMakeVisible }; function getIsDeclarationVisible(declaration: Declaration) { if (!__conv_self__.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 = __conv_self__.getAnyImportSyntax(declaration); if (anyImportSyntax && !hasModifier(anyImportSyntax, ModifierFlags.Export) && __conv_self__.isDeclarationVisible((anyImportSyntax.parent))) { // 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) { __conv_self__.getNodeLinks(declaration).isVisible = true; if (aliasesToMakeVisible) { if (!contains(aliasesToMakeVisible, anyImportSyntax)) { aliasesToMakeVisible.push(anyImportSyntax); } } else { aliasesToMakeVisible = [anyImportSyntax]; } } return true; } // Declaration is not visible return false; } return true; } } public isEntityNameVisible(entityName: EntityNameOrEntityNameExpression, enclosingDeclaration: Node): SymbolVisibilityResult { // get symbol of the first identifier of the entityName let meaning: SymbolFlags; if (entityName.parent.kind === SyntaxKind.TypeQuery || isExpressionWithTypeArgumentsInClassExtendsClause(entityName.parent)) { // Typeof value meaning = SymbolFlags.Value | SymbolFlags.ExportValue; } else if (entityName.kind === SyntaxKind.QualifiedName || entityName.kind === SyntaxKind.PropertyAccessExpression || entityName.parent.kind === SyntaxKind.ImportEqualsDeclaration) { // 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; } const firstIdentifier = this.getFirstIdentifier(entityName); const symbol = this.resolveName(enclosingDeclaration, firstIdentifier.escapedText, meaning, /*nodeNotFoundErrorMessage*/ undefined, /*nameArg*/ undefined); // Verify if the symbol is accessible return (symbol && this.hasVisibleDeclarations(symbol, /*shouldComputeAliasToMakeVisible*/ true)) || { accessibility: SymbolAccessibility.NotAccessible, errorSymbolName: getTextOfNode(firstIdentifier), errorNode: firstIdentifier }; } public writeKeyword(writer: SymbolWriter, kind: SyntaxKind) { writer.writeKeyword(tokenToString(kind)); } public writePunctuation(writer: SymbolWriter, kind: SyntaxKind) { writer.writePunctuation(tokenToString(kind)); } public writeSpace(writer: SymbolWriter) { writer.writeSpace(" "); } public symbolToString(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): string { const __conv_self__ = this; return usingSingleLineStringWriter(writer => { __conv_self__.getSymbolDisplayBuilder().buildSymbolDisplay(symbol, writer, enclosingDeclaration, meaning); }); } public signatureToString(signature: Signature, enclosingDeclaration?: Node, flags?: TypeFormatFlags, kind?: SignatureKind): string { const __conv_self__ = this; return usingSingleLineStringWriter(writer => { __conv_self__.getSymbolDisplayBuilder().buildSignatureDisplay(signature, writer, enclosingDeclaration, flags, kind); }); } public typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string { const typeNode = this.nodeBuilder.typeToTypeNode(type, enclosingDeclaration, toNodeBuilderFlags(flags) | NodeBuilderFlags.IgnoreErrors | NodeBuilderFlags.WriteTypeParametersInQualifiedName); Debug.assert(typeNode !== undefined, "should always get typenode"); const options = { removeComments: true }; const writer = createTextWriter(""); const printer = createPrinter(options); const sourceFile = enclosingDeclaration && getSourceFileOfNode(enclosingDeclaration); printer.writeNode(EmitHint.Unspecified, typeNode, /*sourceFile*/ sourceFile, writer); const result = writer.getText(); const maxLength = this.compilerOptions.noErrorTruncation || flags & TypeFormatFlags.NoTruncation ? undefined : 100; if (maxLength && result.length >= maxLength) { return result.substr(0, maxLength - "...".length) + "..."; } return result; function toNodeBuilderFlags(flags?: TypeFormatFlags): NodeBuilderFlags { let result = NodeBuilderFlags.None; if (!flags) { return result; } if (flags & TypeFormatFlags.NoTruncation) { result |= NodeBuilderFlags.NoTruncation; } if (flags & TypeFormatFlags.UseFullyQualifiedType) { result |= NodeBuilderFlags.UseFullyQualifiedType; } if (flags & TypeFormatFlags.SuppressAnyReturnType) { result |= NodeBuilderFlags.SuppressAnyReturnType; } if (flags & TypeFormatFlags.WriteArrayAsGenericType) { result |= NodeBuilderFlags.WriteArrayAsGenericType; } if (flags & TypeFormatFlags.WriteTypeArgumentsOfSignature) { result |= NodeBuilderFlags.WriteTypeArgumentsOfSignature; } return result; } } public createNodeBuilder() { const __conv_self__ = this; return { typeToTypeNode: (type: Type, enclosingDeclaration?: Node, flags?: NodeBuilderFlags) => { const context = createNodeBuilderContext(enclosingDeclaration, flags); const resultingNode = typeToTypeNodeHelper(type, context); const result = context.encounteredError ? undefined : resultingNode; return result; }, indexInfoToIndexSignatureDeclaration: (indexInfo: IndexInfo, kind: IndexKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags) => { const context = createNodeBuilderContext(enclosingDeclaration, flags); const resultingNode = indexInfoToIndexSignatureDeclarationHelper(indexInfo, kind, context); const result = context.encounteredError ? undefined : resultingNode; return result; }, signatureToSignatureDeclaration: (signature: Signature, kind: SyntaxKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags) => { const context = createNodeBuilderContext(enclosingDeclaration, flags); const resultingNode = signatureToSignatureDeclarationHelper(signature, kind, context); const result = context.encounteredError ? undefined : resultingNode; return result; } }; interface NodeBuilderContext { enclosingDeclaration: Node | undefined; flags: NodeBuilderFlags | undefined; // State encounteredError: boolean; symbolStack: Symbol[] | undefined; } function createNodeBuilderContext(enclosingDeclaration: Node | undefined, flags: NodeBuilderFlags | undefined): NodeBuilderContext { return { enclosingDeclaration, flags, encounteredError: false, symbolStack: undefined }; } function typeToTypeNodeHelper(type: Type, context: NodeBuilderContext): TypeNode { const inTypeAlias = context.flags & NodeBuilderFlags.InTypeAlias; context.flags &= ~NodeBuilderFlags.InTypeAlias; if (!type) { context.encounteredError = true; return undefined; } if (type.flags & TypeFlags.Any) { return createKeywordTypeNode(SyntaxKind.AnyKeyword); } if (type.flags & TypeFlags.String) { return createKeywordTypeNode(SyntaxKind.StringKeyword); } if (type.flags & TypeFlags.Number) { return createKeywordTypeNode(SyntaxKind.NumberKeyword); } if (type.flags & TypeFlags.Boolean) { return createKeywordTypeNode(SyntaxKind.BooleanKeyword); } if (type.flags & TypeFlags.EnumLiteral && !(type.flags & TypeFlags.Union)) { const parentSymbol = __conv_self__.getParentOfSymbol(type.symbol); const parentName = symbolToName(parentSymbol, context, SymbolFlags.Type, /*expectsIdentifier*/ false); const enumLiteralName = __conv_self__.getDeclaredTypeOfSymbol(parentSymbol) === type ? parentName : createQualifiedName(parentName, getNameOfSymbol(type.symbol, context)); return createTypeReferenceNode(enumLiteralName, /*typeArguments*/ undefined); } if (type.flags & TypeFlags.EnumLike) { const name = symbolToName(type.symbol, context, SymbolFlags.Type, /*expectsIdentifier*/ false); return createTypeReferenceNode(name, /*typeArguments*/ undefined); } if (type.flags & (TypeFlags.StringLiteral)) { return createLiteralTypeNode(setEmitFlags(createLiteral((type).value), EmitFlags.NoAsciiEscaping)); } if (type.flags & (TypeFlags.NumberLiteral)) { return createLiteralTypeNode((createLiteral((type).value))); } if (type.flags & TypeFlags.BooleanLiteral) { return (type).intrinsicName === "true" ? createTrue() : createFalse(); } if (type.flags & TypeFlags.Void) { return createKeywordTypeNode(SyntaxKind.VoidKeyword); } if (type.flags & TypeFlags.Undefined) { return createKeywordTypeNode(SyntaxKind.UndefinedKeyword); } if (type.flags & TypeFlags.Null) { return createKeywordTypeNode(SyntaxKind.NullKeyword); } if (type.flags & TypeFlags.Never) { return createKeywordTypeNode(SyntaxKind.NeverKeyword); } if (type.flags & TypeFlags.ESSymbol) { return createKeywordTypeNode(SyntaxKind.SymbolKeyword); } if (type.flags & TypeFlags.NonPrimitive) { return createKeywordTypeNode(SyntaxKind.ObjectKeyword); } if (type.flags & TypeFlags.TypeParameter && (type as TypeParameter).isThisType) { if (context.flags & NodeBuilderFlags.InObjectTypeLiteral) { if (!context.encounteredError && !(context.flags & NodeBuilderFlags.AllowThisInObjectLiteral)) { context.encounteredError = true; } } return createThis(); } const objectFlags = __conv_self__.getObjectFlags(type); if (objectFlags & ObjectFlags.Reference) { Debug.assert(!!(type.flags & TypeFlags.Object)); return typeReferenceToTypeNode(type); } if (type.flags & TypeFlags.TypeParameter || objectFlags & ObjectFlags.ClassOrInterface) { const name = symbolToName(type.symbol, context, SymbolFlags.Type, /*expectsIdentifier*/ false); // Ignore constraint/default when creating a usage (as opposed to declaration) of a type parameter. return createTypeReferenceNode(name, /*typeArguments*/ undefined); } if (!inTypeAlias && type.aliasSymbol && __conv_self__.isSymbolAccessible(type.aliasSymbol, context.enclosingDeclaration, SymbolFlags.Type, /*shouldComputeAliasesToMakeVisible*/ false).accessibility === SymbolAccessibility.Accessible) { const name = symbolToTypeReferenceName(type.aliasSymbol); const typeArgumentNodes = mapToTypeNodes(type.aliasTypeArguments, context); return createTypeReferenceNode(name, typeArgumentNodes); } if (type.flags & (TypeFlags.Union | TypeFlags.Intersection)) { const types = type.flags & TypeFlags.Union ? __conv_self__.formatUnionTypes((type).types) : (type).types; const typeNodes = mapToTypeNodes(types, context); if (typeNodes && typeNodes.length > 0) { const unionOrIntersectionTypeNode = createUnionOrIntersectionTypeNode(type.flags & TypeFlags.Union ? SyntaxKind.UnionType : SyntaxKind.IntersectionType, typeNodes); return unionOrIntersectionTypeNode; } else { if (!context.encounteredError && !(context.flags & NodeBuilderFlags.AllowEmptyUnionOrIntersection)) { context.encounteredError = true; } return undefined; } } if (objectFlags & (ObjectFlags.Anonymous | ObjectFlags.Mapped)) { Debug.assert(!!(type.flags & TypeFlags.Object)); // The type is an object literal type. return createAnonymousTypeNode(type); } if (type.flags & TypeFlags.Index) { const indexedType = (type).type; const indexTypeNode = typeToTypeNodeHelper(indexedType, context); return createTypeOperatorNode(indexTypeNode); } if (type.flags & TypeFlags.IndexedAccess) { const objectTypeNode = typeToTypeNodeHelper((type).objectType, context); const indexTypeNode = typeToTypeNodeHelper((type).indexType, context); return createIndexedAccessTypeNode(objectTypeNode, indexTypeNode); } Debug.fail("Should be unreachable."); function createMappedTypeNodeFromType(type: MappedType) { Debug.assert(!!(type.flags & TypeFlags.Object)); const readonlyToken = type.declaration && type.declaration.readonlyToken ? createToken(SyntaxKind.ReadonlyKeyword) : undefined; const questionToken = type.declaration && type.declaration.questionToken ? createToken(SyntaxKind.QuestionToken) : undefined; const typeParameterNode = typeParameterToDeclaration(__conv_self__.getTypeParameterFromMappedType(type), context); const templateTypeNode = typeToTypeNodeHelper(__conv_self__.getTemplateTypeFromMappedType(type), context); const mappedTypeNode = createMappedTypeNode(readonlyToken, typeParameterNode, questionToken, templateTypeNode); return setEmitFlags(mappedTypeNode, EmitFlags.SingleLine); } function createAnonymousTypeNode(type: ObjectType): TypeNode { const symbol = type.symbol; if (symbol) { // Always use 'typeof T' for type of class, enum, and module objects if (symbol.flags & SymbolFlags.Class && !__conv_self__.getBaseTypeVariableOfClass(symbol) || symbol.flags & (SymbolFlags.Enum | SymbolFlags.ValueModule) || shouldWriteTypeOfFunctionSymbol()) { return createTypeQueryNodeFromSymbol(symbol, SymbolFlags.Value); } else if (contains(context.symbolStack, symbol)) { // If type is an anonymous type literal in a type alias declaration, use type alias name const typeAlias = __conv_self__.getTypeAliasForTypeLiteral(type); if (typeAlias) { // The specified symbol flags need to be reinterpreted as type flags const entityName = symbolToName(typeAlias, context, SymbolFlags.Type, /*expectsIdentifier*/ false); return createTypeReferenceNode(entityName, /*typeArguments*/ undefined); } else { return createKeywordTypeNode(SyntaxKind.AnyKeyword); } } else { // 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.symbolStack) { context.symbolStack = []; } context.symbolStack.push(symbol); const result = createTypeNodeFromObjectType(type); context.symbolStack.pop(); return result; } } else { // Anonymous types without a symbol are never circular. return createTypeNodeFromObjectType(type); } function shouldWriteTypeOfFunctionSymbol() { const isStaticMethodSymbol = !!(symbol.flags & SymbolFlags.Method) && some(symbol.declarations, declaration => hasModifier(declaration, ModifierFlags.Static)); const isNonLocalFunctionSymbol = !!(symbol.flags & SymbolFlags.Function) && (symbol.parent || 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 contains(context.symbolStack, symbol); // it is type of the symbol uses itself recursively } } } function createTypeNodeFromObjectType(type: ObjectType): TypeNode { if (__conv_self__.isGenericMappedType(type)) { return createMappedTypeNodeFromType(type); } const resolved = __conv_self__.resolveStructuredTypeMembers(type); if (!resolved.properties.length && !resolved.stringIndexInfo && !resolved.numberIndexInfo) { if (!resolved.callSignatures.length && !resolved.constructSignatures.length) { return setEmitFlags(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); return signatureNode; } if (resolved.constructSignatures.length === 1 && !resolved.callSignatures.length) { const signature = resolved.constructSignatures[0]; const signatureNode = signatureToSignatureDeclarationHelper(signature, SyntaxKind.ConstructorType, context); return signatureNode; } } const savedFlags = context.flags; context.flags |= NodeBuilderFlags.InObjectTypeLiteral; const members = createTypeNodesFromResolvedType(resolved); context.flags = savedFlags; const typeLiteralNode = createTypeLiteralNode(members); return setEmitFlags(typeLiteralNode, EmitFlags.SingleLine); } function createTypeQueryNodeFromSymbol(symbol: Symbol, symbolFlags: SymbolFlags) { const entityName = symbolToName(symbol, context, symbolFlags, /*expectsIdentifier*/ false); return createTypeQueryNode(entityName); } function symbolToTypeReferenceName(symbol: Symbol) { // Unnamed function expressions and arrow functions have reserved names that we don't want to display const entityName = symbol.flags & SymbolFlags.Class || !__conv_self__.isReservedMemberName(symbol.escapedName) ? symbolToName(symbol, context, SymbolFlags.Type, /*expectsIdentifier*/ false) : createIdentifier(""); return entityName; } function typeReferenceToTypeNode(type: TypeReference) { const typeArguments: Type[] = type.typeArguments || emptyArray; if (type.target === __conv_self__.globalArrayType) { if (context.flags & NodeBuilderFlags.WriteArrayAsGenericType) { const typeArgumentNode = typeToTypeNodeHelper(typeArguments[0], context); return createTypeReferenceNode("Array", [typeArgumentNode]); } const elementType = typeToTypeNodeHelper(typeArguments[0], context); return createArrayTypeNode(elementType); } else if (type.target.objectFlags & ObjectFlags.Tuple) { if (typeArguments.length > 0) { const tupleConstituentNodes = mapToTypeNodes(typeArguments.slice(0, __conv_self__.getTypeReferenceArity(type)), context); if (tupleConstituentNodes && tupleConstituentNodes.length > 0) { return createTupleTypeNode(tupleConstituentNodes); } } if (context.encounteredError || (context.flags & NodeBuilderFlags.AllowEmptyTuple)) { return createTupleTypeNode([]); } context.encounteredError = true; return undefined; } else { const outerTypeParameters = type.target.outerTypeParameters; let i = 0; let qualifiedName: QualifiedName | 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 = __conv_self__.getParentSymbolOfTypeParameter(outerTypeParameters[i]); do { i++; } while (i < length && __conv_self__.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 typeArgumentNodes = typeArgumentSlice && createNodeArray(typeArgumentSlice); const namePart = symbolToTypeReferenceName(parent); (namePart.kind === SyntaxKind.Identifier ? namePart : namePart.right).typeArguments = typeArgumentNodes; if (qualifiedName) { Debug.assert(!qualifiedName.right); qualifiedName = addToQualifiedNameMissingRightIdentifier(qualifiedName, namePart); qualifiedName = createQualifiedName(qualifiedName, /*right*/ undefined); } else { qualifiedName = createQualifiedName(namePart, /*right*/ undefined); } } } } let entityName: EntityName = undefined; const nameIdentifier = symbolToTypeReferenceName(type.symbol); if (qualifiedName) { Debug.assert(!qualifiedName.right); qualifiedName = addToQualifiedNameMissingRightIdentifier(qualifiedName, nameIdentifier); entityName = qualifiedName; } else { entityName = nameIdentifier; } let typeArgumentNodes: ReadonlyArray | undefined; if (typeArguments.length > 0) { const typeParameterCount = (type.target.typeParameters || emptyArray).length; typeArgumentNodes = mapToTypeNodes(typeArguments.slice(i, typeParameterCount), context); } if (typeArgumentNodes) { const lastIdentifier = entityName.kind === SyntaxKind.Identifier ? entityName : entityName.right; lastIdentifier.typeArguments = undefined; } return createTypeReferenceNode(entityName, typeArgumentNodes); } } function addToQualifiedNameMissingRightIdentifier(left: QualifiedName, right: Identifier | QualifiedName) { Debug.assert(left.right === undefined); if (right.kind === SyntaxKind.Identifier) { left.right = right; return left; } let rightPart = right; while (rightPart.left.kind !== SyntaxKind.Identifier) { rightPart = rightPart.left; } left.right = rightPart.left; rightPart.left = left; return right; } function createTypeNodesFromResolvedType(resolvedType: ResolvedType): TypeElement[] { const typeElements: TypeElement[] = []; for (const signature of resolvedType.callSignatures) { typeElements.push(signatureToSignatureDeclarationHelper(signature, SyntaxKind.CallSignature, context)); } for (const signature of resolvedType.constructSignatures) { typeElements.push(signatureToSignatureDeclarationHelper(signature, SyntaxKind.ConstructSignature, context)); } if (resolvedType.stringIndexInfo) { typeElements.push(indexInfoToIndexSignatureDeclarationHelper(resolvedType.stringIndexInfo, IndexKind.String, context)); } if (resolvedType.numberIndexInfo) { typeElements.push(indexInfoToIndexSignatureDeclarationHelper(resolvedType.numberIndexInfo, IndexKind.Number, context)); } const properties = resolvedType.properties; if (!properties) { return typeElements; } for (const propertySymbol of properties) { const propertyType = __conv_self__.getTypeOfSymbol(propertySymbol); const saveEnclosingDeclaration = context.enclosingDeclaration; context.enclosingDeclaration = undefined; const propertyName = symbolToName(propertySymbol, context, SymbolFlags.Value, /*expectsIdentifier*/ true); context.enclosingDeclaration = saveEnclosingDeclaration; const optionalToken = propertySymbol.flags & SymbolFlags.Optional ? createToken(SyntaxKind.QuestionToken) : undefined; if (propertySymbol.flags & (SymbolFlags.Function | SymbolFlags.Method) && !__conv_self__.getPropertiesOfObjectType(propertyType).length) { const signatures = __conv_self__.getSignaturesOfType(propertyType, SignatureKind.Call); for (const signature of signatures) { const methodDeclaration = signatureToSignatureDeclarationHelper(signature, SyntaxKind.MethodSignature, context); methodDeclaration.name = propertyName; methodDeclaration.questionToken = optionalToken; typeElements.push(methodDeclaration); } } else { const propertyTypeNode = propertyType ? typeToTypeNodeHelper(propertyType, context) : createKeywordTypeNode(SyntaxKind.AnyKeyword); const modifiers = __conv_self__.isReadonlySymbol(propertySymbol) ? [createToken(SyntaxKind.ReadonlyKeyword)] : undefined; const propertySignature = createPropertySignature(modifiers, propertyName, optionalToken, propertyTypeNode, /*initializer*/ undefined); typeElements.push(propertySignature); } } return typeElements.length ? typeElements : undefined; } } function mapToTypeNodes(types: Type[], context: NodeBuilderContext): TypeNode[] { if (some(types)) { const result = []; for (let i = 0; i < types.length; ++i) { const type = types[i]; const typeNode = typeToTypeNodeHelper(type, context); if (typeNode) { result.push(typeNode); } } return result; } } function indexInfoToIndexSignatureDeclarationHelper(indexInfo: IndexInfo, kind: IndexKind, context: NodeBuilderContext): IndexSignatureDeclaration { const name = getNameFromIndexInfo(indexInfo) || "x"; const indexerTypeNode = createKeywordTypeNode(kind === IndexKind.String ? SyntaxKind.StringKeyword : SyntaxKind.NumberKeyword); const indexingParameter = createParameter( /*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, name, /*questionToken*/ undefined, indexerTypeNode, /*initializer*/ undefined); const typeNode = typeToTypeNodeHelper(indexInfo.type, context); return createIndexSignature( /*decorators*/ undefined, indexInfo.isReadonly ? [createToken(SyntaxKind.ReadonlyKeyword)] : undefined, [indexingParameter], typeNode); } function signatureToSignatureDeclarationHelper(signature: Signature, kind: SyntaxKind, context: NodeBuilderContext): SignatureDeclaration { const typeParameters = signature.typeParameters && signature.typeParameters.map(parameter => typeParameterToDeclaration(parameter, context)); const parameters = signature.parameters.map(parameter => symbolToParameterDeclaration(parameter, context)); if (signature.thisParameter) { const thisParameter = symbolToParameterDeclaration(signature.thisParameter, context); parameters.unshift(thisParameter); } let returnTypeNode: TypeNode; if (signature.typePredicate) { const typePredicate = signature.typePredicate; const parameterName = typePredicate.kind === TypePredicateKind.Identifier ? setEmitFlags(createIdentifier((typePredicate).parameterName), EmitFlags.NoAsciiEscaping) : createThisTypeNode(); const typeNode = typeToTypeNodeHelper(typePredicate.type, context); returnTypeNode = createTypePredicateNode(parameterName, typeNode); } else { const returnType = __conv_self__.getReturnTypeOfSignature(signature); returnTypeNode = returnType && typeToTypeNodeHelper(returnType, context); } if (context.flags & NodeBuilderFlags.SuppressAnyReturnType) { if (returnTypeNode && returnTypeNode.kind === SyntaxKind.AnyKeyword) { returnTypeNode = undefined; } } else if (!returnTypeNode) { returnTypeNode = createKeywordTypeNode(SyntaxKind.AnyKeyword); } return createSignatureDeclaration(kind, typeParameters, parameters, returnTypeNode); } function typeParameterToDeclaration(type: TypeParameter, context: NodeBuilderContext): TypeParameterDeclaration { const name = symbolToName(type.symbol, context, SymbolFlags.Type, /*expectsIdentifier*/ true); const constraint = __conv_self__.getConstraintFromTypeParameter(type); const constraintNode = constraint && typeToTypeNodeHelper(constraint, context); const defaultParameter = __conv_self__.getDefaultFromTypeParameter(type); const defaultParameterNode = defaultParameter && typeToTypeNodeHelper(defaultParameter, context); return createTypeParameterDeclaration(name, constraintNode, defaultParameterNode); } function symbolToParameterDeclaration(parameterSymbol: Symbol, context: NodeBuilderContext): ParameterDeclaration { const parameterDeclaration = getDeclarationOfKind(parameterSymbol, SyntaxKind.Parameter); if (__conv_self__.isTransientSymbol(parameterSymbol) && parameterSymbol.isRestParameter) { // special-case synthetic rest parameters in JS files return createParameter( /*decorators*/ undefined, /*modifiers*/ undefined, parameterSymbol.isRestParameter ? createToken(SyntaxKind.DotDotDotToken) : undefined, "args", /*questionToken*/ undefined, typeToTypeNodeHelper(__conv_self__.anyArrayType, context), /*initializer*/ undefined); } const modifiers = parameterDeclaration.modifiers && parameterDeclaration.modifiers.map(getSynthesizedClone); const dotDotDotToken = isRestParameter(parameterDeclaration) ? createToken(SyntaxKind.DotDotDotToken) : undefined; const name = parameterDeclaration.name ? parameterDeclaration.name.kind === SyntaxKind.Identifier ? setEmitFlags(getSynthesizedClone(parameterDeclaration.name), EmitFlags.NoAsciiEscaping) : cloneBindingName(parameterDeclaration.name) : unescapeLeadingUnderscores(parameterSymbol.escapedName); const questionToken = __conv_self__.isOptionalParameter(parameterDeclaration) ? createToken(SyntaxKind.QuestionToken) : undefined; let parameterType = __conv_self__.getTypeOfSymbol(parameterSymbol); if (__conv_self__.isRequiredInitializedParameter(parameterDeclaration)) { parameterType = __conv_self__.getNullableType(parameterType, TypeFlags.Undefined); } const parameterTypeNode = typeToTypeNodeHelper(parameterType, context); const parameterNode = createParameter( /*decorators*/ undefined, modifiers, dotDotDotToken, name, questionToken, parameterTypeNode, /*initializer*/ undefined); return parameterNode; function cloneBindingName(node: BindingName): BindingName { return elideInitializerAndSetEmitFlags(node); function elideInitializerAndSetEmitFlags(node: Node): Node { const visited = visitEachChild(node, elideInitializerAndSetEmitFlags, nullTransformationContext, /*nodesVisitor*/ undefined, elideInitializerAndSetEmitFlags); const clone = nodeIsSynthesized(visited) ? visited : getSynthesizedClone(visited); if (clone.kind === SyntaxKind.BindingElement) { (clone).initializer = undefined; } return setEmitFlags(clone, EmitFlags.SingleLine | EmitFlags.NoAsciiEscaping); } } } 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 { // 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)) { chain = getSymbolChain(symbol, meaning, /*endOfChain*/ true); Debug.assert(chain && chain.length > 0); } else { chain = [symbol]; } if (expectsIdentifier && chain.length !== 1 && !context.encounteredError && !(context.flags & NodeBuilderFlags.AllowQualifedNameInPlaceOfIdentifier)) { context.encounteredError = true; } return createEntityNameFromSymbolChain(chain, chain.length - 1); function createEntityNameFromSymbolChain(chain: Symbol[], index: number): EntityName { Debug.assert(chain && 0 <= index && index < chain.length); const symbol = chain[index]; let typeParameterNodes: ReadonlyArray | undefined; if (context.flags & NodeBuilderFlags.WriteTypeParametersInQualifiedName && index > 0) { const parentSymbol = chain[index - 1]; let typeParameters: TypeParameter[]; if (getCheckFlags(symbol) & CheckFlags.Instantiated) { typeParameters = __conv_self__.getTypeParametersOfClassOrInterface(parentSymbol); } else { const targetSymbol = __conv_self__.getTargetSymbol(parentSymbol); if (targetSymbol.flags & (SymbolFlags.Class | SymbolFlags.Interface | SymbolFlags.TypeAlias)) { typeParameters = __conv_self__.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol); } } typeParameterNodes = mapToTypeNodes(typeParameters, context); } const symbolName = getNameOfSymbol(symbol, context); const identifier = setEmitFlags(createIdentifier(symbolName, typeParameterNodes), EmitFlags.NoAsciiEscaping); return index > 0 ? createQualifiedName(createEntityNameFromSymbolChain(chain, index - 1), identifier) : identifier; } /** @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 = __conv_self__.getAccessibleSymbolChain(symbol, context.enclosingDeclaration, meaning, /*useOnlyExternalAliasing*/ false); let parentSymbol: Symbol; if (!accessibleSymbolChain || __conv_self__.needsQualification(accessibleSymbolChain[0], context.enclosingDeclaration, accessibleSymbolChain.length === 1 ? meaning : __conv_self__.getQualifiedLeftMeaning(meaning))) { // Go up and add our parent. const parent = __conv_self__.getParentOfSymbol(accessibleSymbolChain ? accessibleSymbolChain[0] : symbol); if (parent) { const parentChain = getSymbolChain(parent, __conv_self__.getQualifiedLeftMeaning(meaning), /*endOfChain*/ false); if (parentChain) { parentSymbol = parent; accessibleSymbolChain = parentChain.concat(accessibleSymbolChain || [symbol]); } } } 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 external module, don't write it. (We prefer just `x` vs `"foo/bar".x`.) !(!parentSymbol && ts.forEach(symbol.declarations, __conv_self__.hasExternalModuleSymbol)) && // If a parent symbol is an anonymous type, don't write it. !(symbol.flags & (SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral))) { return [symbol]; } } } function getNameOfSymbol(symbol: Symbol, context: NodeBuilderContext): string { const declaration = firstOrUndefined(symbol.declarations); if (declaration) { const name = getNameOfDeclaration(declaration); if (name) { return declarationNameToString(name); } if (declaration.parent && declaration.parent.kind === SyntaxKind.VariableDeclaration) { return declarationNameToString((declaration.parent).name); } if (!context.encounteredError && !(context.flags & NodeBuilderFlags.AllowAnonymousIdentifier)) { context.encounteredError = true; } switch (declaration.kind) { case SyntaxKind.ClassExpression: return "(Anonymous class)"; case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: return "(Anonymous function)"; } } return unescapeLeadingUnderscores(symbol.escapedName); } } public typePredicateToString(typePredicate: TypePredicate, enclosingDeclaration?: Declaration, flags?: TypeFormatFlags): string { const __conv_self__ = this; return usingSingleLineStringWriter(writer => { __conv_self__.getSymbolDisplayBuilder().buildTypePredicateDisplay(typePredicate, writer, enclosingDeclaration, flags); }); } public formatUnionTypes(types: Type[]): Type[] { const result: Type[] = []; let flags: TypeFlags = 0; 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.EnumLiteral)) { const baseType = t.flags & TypeFlags.BooleanLiteral ? this.booleanType : this.getBaseTypeOfEnumLiteralType((t)); if (baseType.flags & TypeFlags.Union) { const count = (baseType).types.length; if (i + count <= types.length && types[i + count - 1] === (baseType).types[count - 1]) { result.push(baseType); i += count - 1; continue; } } } result.push(t); } } if (flags & TypeFlags.Null) result.push(this.nullType); if (flags & TypeFlags.Undefined) result.push(this.undefinedType); return result || types; } public visibilityToString(flags: ModifierFlags): string | undefined { if (flags === ModifierFlags.Private) { return "private"; } if (flags === ModifierFlags.Protected) { return "protected"; } return "public"; } public getTypeAliasForTypeLiteral(type: Type): Symbol { if (type.symbol && type.symbol.flags & SymbolFlags.TypeLiteral) { const node = findAncestor(type.symbol.declarations[0].parent, n => n.kind !== SyntaxKind.ParenthesizedType); if (node.kind === SyntaxKind.TypeAliasDeclaration) { return this.getSymbolOfNode(node); } } return undefined; } public isTopLevelInExternalModuleAugmentation(node: Node): boolean { return node && node.parent && node.parent.kind === SyntaxKind.ModuleBlock && isExternalModuleAugmentation(node.parent.parent); } public literalTypeToString(type: LiteralType) { return type.flags & TypeFlags.StringLiteral ? `"${escapeString((type).value)}"` : "" + (type).value; } public getNameOfSymbol(symbol: Symbol): string { if (symbol.declarations && symbol.declarations.length) { const declaration = symbol.declarations[0]; const name = getNameOfDeclaration(declaration); if (name) { return declarationNameToString(name); } if (declaration.parent && declaration.parent.kind === SyntaxKind.VariableDeclaration) { return declarationNameToString((declaration.parent).name); } switch (declaration.kind) { case SyntaxKind.ClassExpression: return "(Anonymous class)"; case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: return "(Anonymous function)"; } } return unescapeLeadingUnderscores(symbol.escapedName); } public getSymbolDisplayBuilder(): SymbolDisplayBuilder { const __conv_self__ = this; /** * Writes only the name of the symbol out to the writer. Uses the original source text * for the name of the symbol if it is available to match how the user wrote the name. */ function appendSymbolNameOnly(symbol: Symbol, writer: SymbolWriter): void { writer.writeSymbol(__conv_self__.getNameOfSymbol(symbol), symbol); } /** * Writes a property access or element access with the name of the symbol out to the writer. * Uses the original source text for the name of the symbol if it is available to match how the user wrote the name, * ensuring that any names written with literals use element accesses. */ function appendPropertyOrElementAccessForSymbol(symbol: Symbol, writer: SymbolWriter): void { const symbolName = __conv_self__.getNameOfSymbol(symbol); const firstChar = symbolName.charCodeAt(0); const needsElementAccess = !isIdentifierStart(firstChar, __conv_self__.languageVersion); if (needsElementAccess) { __conv_self__.writePunctuation(writer, SyntaxKind.OpenBracketToken); if (isSingleOrDoubleQuote(firstChar)) { writer.writeStringLiteral(symbolName); } else { writer.writeSymbol(symbolName, symbol); } __conv_self__.writePunctuation(writer, SyntaxKind.CloseBracketToken); } else { __conv_self__.writePunctuation(writer, SyntaxKind.DotToken); writer.writeSymbol(symbolName, symbol); } } /** * Enclosing declaration is optional when we don't want to get qualified name in the enclosing declaration scope * Meaning needs to be specified if the enclosing declaration is given */ function buildSymbolDisplay(symbol: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, meaning?: SymbolFlags, flags?: SymbolFormatFlags, typeFlags?: TypeFormatFlags): void { let parentSymbol: Symbol; function appendParentTypeArgumentsAndSymbolName(symbol: Symbol): void { if (parentSymbol) { // Write type arguments of instantiated class/interface here if (flags & SymbolFormatFlags.WriteTypeParametersOrArguments) { if (getCheckFlags(symbol) & CheckFlags.Instantiated) { const params = __conv_self__.getTypeParametersOfClassOrInterface(parentSymbol.flags & SymbolFlags.Alias ? __conv_self__.resolveAlias(parentSymbol) : parentSymbol); buildDisplayForTypeArgumentsAndDelimiters(params, (symbol).mapper, writer, enclosingDeclaration); } else { buildTypeParameterDisplayFromSymbol(parentSymbol, writer, enclosingDeclaration); } } appendPropertyOrElementAccessForSymbol(symbol, writer); } else { appendSymbolNameOnly(symbol, writer); } parentSymbol = symbol; } // Let the writer know we just wrote out a symbol. The declaration emitter writer uses // this to determine if an import it has previously seen (and not written out) needs // to be written to the file once the walk of the tree is complete. // // NOTE(cyrusn): This approach feels somewhat unfortunate. A simple pass over the tree // up front (for example, during checking) could determine if we need to emit the imports // and we could then access that data during declaration emit. writer.trackSymbol(symbol, enclosingDeclaration, meaning); /** @param endOfChain Set to false for recursive calls; non-recursive calls should always output something. */ function walkSymbol(symbol: Symbol, meaning: SymbolFlags, endOfChain: boolean): void { const accessibleSymbolChain = __conv_self__.getAccessibleSymbolChain(symbol, enclosingDeclaration, meaning, !!(flags & SymbolFormatFlags.UseOnlyExternalAliasing)); if (!accessibleSymbolChain || __conv_self__.needsQualification(accessibleSymbolChain[0], enclosingDeclaration, accessibleSymbolChain.length === 1 ? meaning : __conv_self__.getQualifiedLeftMeaning(meaning))) { // Go up and add our parent. const parent = __conv_self__.getParentOfSymbol(accessibleSymbolChain ? accessibleSymbolChain[0] : symbol); if (parent) { walkSymbol(parent, __conv_self__.getQualifiedLeftMeaning(meaning), /*endOfChain*/ false); } } if (accessibleSymbolChain) { for (const accessibleSymbol of accessibleSymbolChain) { appendParentTypeArgumentsAndSymbolName(accessibleSymbol); } } else 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 external module, don't write it. (We prefer just `x` vs `"foo/bar".x`.) !(!parentSymbol && ts.forEach(symbol.declarations, __conv_self__.hasExternalModuleSymbol)) && // If a parent symbol is an anonymous type, don't write it. !(symbol.flags & (SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral))) { appendParentTypeArgumentsAndSymbolName(symbol); } } // Get qualified name if the symbol is not a type parameter // and there is an enclosing declaration or we specifically // asked for it const isTypeParameter = symbol.flags & SymbolFlags.TypeParameter; const typeFormatFlag = TypeFormatFlags.UseFullyQualifiedType & typeFlags; if (!isTypeParameter && (enclosingDeclaration || typeFormatFlag)) { walkSymbol(symbol, meaning, /*endOfChain*/ true); } else { appendParentTypeArgumentsAndSymbolName(symbol); } } function buildTypeDisplay(type: Type, writer: SymbolWriter, enclosingDeclaration?: Node, globalFlags?: TypeFormatFlags, symbolStack?: Symbol[]) { const globalFlagsToPass = globalFlags & (TypeFormatFlags.WriteOwnNameForAnyLike | TypeFormatFlags.WriteClassExpressionAsTypeLiteral); let inObjectTypeLiteral = false; return writeType(type, globalFlags); function writeType(type: Type, flags: TypeFormatFlags) { const nextFlags = flags & ~TypeFormatFlags.InTypeAlias; // Write undefined/null type as any if (type.flags & TypeFlags.Intrinsic) { // Special handling for unknown / resolving types, they should show up as any and not unknown or __resolving writer.writeKeyword(!(globalFlags & TypeFormatFlags.WriteOwnNameForAnyLike) && __conv_self__.isTypeAny(type) ? "any" : (type).intrinsicName); } else if (type.flags & TypeFlags.TypeParameter && (type as TypeParameter).isThisType) { if (inObjectTypeLiteral) { writer.reportInaccessibleThisError(); } writer.writeKeyword("this"); } else if (__conv_self__.getObjectFlags(type) & ObjectFlags.Reference) { writeTypeReference(type, nextFlags); } else if (type.flags & TypeFlags.EnumLiteral && !(type.flags & TypeFlags.Union)) { const parent = __conv_self__.getParentOfSymbol(type.symbol); buildSymbolDisplay(parent, writer, enclosingDeclaration, SymbolFlags.Type, SymbolFormatFlags.None, nextFlags); // In a literal enum type with a single member E { A }, E and E.A denote the // same type. We always display this type simply as E. if (__conv_self__.getDeclaredTypeOfSymbol(parent) !== type) { __conv_self__.writePunctuation(writer, SyntaxKind.DotToken); appendSymbolNameOnly(type.symbol, writer); } } else if (__conv_self__.getObjectFlags(type) & ObjectFlags.ClassOrInterface || type.flags & (TypeFlags.EnumLike | TypeFlags.TypeParameter)) { // The specified symbol flags need to be reinterpreted as type flags buildSymbolDisplay(type.symbol, writer, enclosingDeclaration, SymbolFlags.Type, SymbolFormatFlags.None, nextFlags); } else if (!(flags & TypeFormatFlags.InTypeAlias) && type.aliasSymbol && __conv_self__.isSymbolAccessible(type.aliasSymbol, enclosingDeclaration, SymbolFlags.Type, /*shouldComputeAliasesToMakeVisible*/ false).accessibility === SymbolAccessibility.Accessible) { const typeArguments = type.aliasTypeArguments; writeSymbolTypeReference(type.aliasSymbol, typeArguments, 0, length(typeArguments), nextFlags); } else if (type.flags & TypeFlags.UnionOrIntersection) { writeUnionOrIntersectionType(type, nextFlags); } else if (__conv_self__.getObjectFlags(type) & (ObjectFlags.Anonymous | ObjectFlags.Mapped)) { writeAnonymousType(type, nextFlags); } else if (type.flags & TypeFlags.StringOrNumberLiteral) { writer.writeStringLiteral(__conv_self__.literalTypeToString((type))); } else if (type.flags & TypeFlags.Index) { if (flags & TypeFormatFlags.InElementType) { __conv_self__.writePunctuation(writer, SyntaxKind.OpenParenToken); } writer.writeKeyword("keyof"); __conv_self__.writeSpace(writer); writeType((type).type, TypeFormatFlags.InElementType); if (flags & TypeFormatFlags.InElementType) { __conv_self__.writePunctuation(writer, SyntaxKind.CloseParenToken); } } else if (type.flags & TypeFlags.IndexedAccess) { writeType((type).objectType, TypeFormatFlags.InElementType); __conv_self__.writePunctuation(writer, SyntaxKind.OpenBracketToken); writeType((type).indexType, TypeFormatFlags.None); __conv_self__.writePunctuation(writer, SyntaxKind.CloseBracketToken); } else { // Should never get here // { ... } __conv_self__.writePunctuation(writer, SyntaxKind.OpenBraceToken); __conv_self__.writeSpace(writer); __conv_self__.writePunctuation(writer, SyntaxKind.DotDotDotToken); __conv_self__.writeSpace(writer); __conv_self__.writePunctuation(writer, SyntaxKind.CloseBraceToken); } } function writeTypeList(types: Type[], delimiter: SyntaxKind) { for (let i = 0; i < types.length; i++) { if (i > 0) { if (delimiter !== SyntaxKind.CommaToken) { __conv_self__.writeSpace(writer); } __conv_self__.writePunctuation(writer, delimiter); __conv_self__.writeSpace(writer); } writeType(types[i], delimiter === SyntaxKind.CommaToken ? TypeFormatFlags.None : TypeFormatFlags.InElementType); } } function writeSymbolTypeReference(symbol: Symbol, typeArguments: Type[], pos: number, end: number, flags: TypeFormatFlags) { // Unnamed function expressions and arrow functions have reserved names that we don't want to display if (symbol.flags & SymbolFlags.Class || !__conv_self__.isReservedMemberName(symbol.escapedName)) { buildSymbolDisplay(symbol, writer, enclosingDeclaration, SymbolFlags.Type, SymbolFormatFlags.None, flags); } if (pos < end) { __conv_self__.writePunctuation(writer, SyntaxKind.LessThanToken); writeType(typeArguments[pos], TypeFormatFlags.InFirstTypeArgument); pos++; while (pos < end) { __conv_self__.writePunctuation(writer, SyntaxKind.CommaToken); __conv_self__.writeSpace(writer); writeType(typeArguments[pos], TypeFormatFlags.None); pos++; } __conv_self__.writePunctuation(writer, SyntaxKind.GreaterThanToken); } } function writeTypeReference(type: TypeReference, flags: TypeFormatFlags) { const typeArguments = type.typeArguments || emptyArray; if (type.target === __conv_self__.globalArrayType && !(flags & TypeFormatFlags.WriteArrayAsGenericType)) { writeType(typeArguments[0], TypeFormatFlags.InElementType | TypeFormatFlags.InArrayType); __conv_self__.writePunctuation(writer, SyntaxKind.OpenBracketToken); __conv_self__.writePunctuation(writer, SyntaxKind.CloseBracketToken); } else if (type.target.objectFlags & ObjectFlags.Tuple) { __conv_self__.writePunctuation(writer, SyntaxKind.OpenBracketToken); writeTypeList(type.typeArguments.slice(0, __conv_self__.getTypeReferenceArity(type)), SyntaxKind.CommaToken); __conv_self__.writePunctuation(writer, SyntaxKind.CloseBracketToken); } else if (flags & TypeFormatFlags.WriteClassExpressionAsTypeLiteral && type.symbol.valueDeclaration && type.symbol.valueDeclaration.kind === SyntaxKind.ClassExpression) { writeAnonymousType(__conv_self__.getDeclaredTypeOfClassOrInterface(type.symbol), flags); } else { // Write the type reference in the format f.g.C where A and B are type arguments // for outer type parameters, and f and g are the respective declaring containers of those // type parameters. const outerTypeParameters = type.target.outerTypeParameters; let i = 0; 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 = __conv_self__.getParentSymbolOfTypeParameter(outerTypeParameters[i]); do { i++; } while (i < length && __conv_self__.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)) { writeSymbolTypeReference(parent, typeArguments, start, i, flags); __conv_self__.writePunctuation(writer, SyntaxKind.DotToken); } } } const typeParameterCount = (type.target.typeParameters || emptyArray).length; writeSymbolTypeReference(type.symbol, typeArguments, i, typeParameterCount, flags); } } function writeUnionOrIntersectionType(type: UnionOrIntersectionType, flags: TypeFormatFlags) { if (flags & TypeFormatFlags.InElementType) { __conv_self__.writePunctuation(writer, SyntaxKind.OpenParenToken); } if (type.flags & TypeFlags.Union) { writeTypeList(__conv_self__.formatUnionTypes(type.types), SyntaxKind.BarToken); } else { writeTypeList(type.types, SyntaxKind.AmpersandToken); } if (flags & TypeFormatFlags.InElementType) { __conv_self__.writePunctuation(writer, SyntaxKind.CloseParenToken); } } function writeAnonymousType(type: ObjectType, flags: TypeFormatFlags) { const symbol = type.symbol; if (symbol) { // Always use 'typeof T' for type of class, enum, and module objects if (symbol.flags & SymbolFlags.Class && !__conv_self__.getBaseTypeVariableOfClass(symbol) && !(symbol.valueDeclaration.kind === SyntaxKind.ClassExpression && flags & TypeFormatFlags.WriteClassExpressionAsTypeLiteral) || symbol.flags & (SymbolFlags.Enum | SymbolFlags.ValueModule)) { writeTypeOfSymbol(type, flags); } else if (shouldWriteTypeOfFunctionSymbol()) { writeTypeOfSymbol(type, flags); } else if (contains(symbolStack, symbol)) { // If type is an anonymous type literal in a type alias declaration, use type alias name const typeAlias = __conv_self__.getTypeAliasForTypeLiteral(type); if (typeAlias) { // The specified symbol flags need to be reinterpreted as type flags buildSymbolDisplay(typeAlias, writer, enclosingDeclaration, SymbolFlags.Type, SymbolFormatFlags.None, flags); } else { // Recursive usage, use any __conv_self__.writeKeyword(writer, SyntaxKind.AnyKeyword); } } else { // 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 // However, in case of class expressions, we want to write both the static side and the instance side. // We skip adding the static side so that the instance side has a chance to be written // before checking for circular references. if (!symbolStack) { symbolStack = []; } const isConstructorObject = type.objectFlags & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & SymbolFlags.Class; if (isConstructorObject) { writeLiteralType(type, flags); } else { symbolStack.push(symbol); writeLiteralType(type, flags); symbolStack.pop(); } } } else { // Anonymous types with no symbol are never circular writeLiteralType(type, flags); } function shouldWriteTypeOfFunctionSymbol() { const isStaticMethodSymbol = !!(symbol.flags & SymbolFlags.Method) && some(symbol.declarations, declaration => hasModifier(declaration, ModifierFlags.Static)); const isNonLocalFunctionSymbol = !!(symbol.flags & SymbolFlags.Function) && (symbol.parent || some(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 !!(flags & TypeFormatFlags.UseTypeOfFunction) || contains(symbolStack, symbol); // it is type of the symbol uses itself recursively } } } function writeTypeOfSymbol(type: ObjectType, typeFormatFlags?: TypeFormatFlags) { if (typeFormatFlags & TypeFormatFlags.InArrayType) { __conv_self__.writePunctuation(writer, SyntaxKind.OpenParenToken); } __conv_self__.writeKeyword(writer, SyntaxKind.TypeOfKeyword); __conv_self__.writeSpace(writer); buildSymbolDisplay(type.symbol, writer, enclosingDeclaration, SymbolFlags.Value, SymbolFormatFlags.None, typeFormatFlags); if (typeFormatFlags & TypeFormatFlags.InArrayType) { __conv_self__.writePunctuation(writer, SyntaxKind.CloseParenToken); } } function writePropertyWithModifiers(prop: Symbol) { if (__conv_self__.isReadonlySymbol(prop)) { __conv_self__.writeKeyword(writer, SyntaxKind.ReadonlyKeyword); __conv_self__.writeSpace(writer); } buildSymbolDisplay(prop, writer); if (prop.flags & SymbolFlags.Optional) { __conv_self__.writePunctuation(writer, SyntaxKind.QuestionToken); } } function shouldAddParenthesisAroundFunctionType(callSignature: Signature, flags: TypeFormatFlags) { if (flags & TypeFormatFlags.InElementType) { return true; } else if (flags & TypeFormatFlags.InFirstTypeArgument) { // Add parenthesis around function type for the first type argument to avoid ambiguity const typeParameters = callSignature.target && (flags & TypeFormatFlags.WriteTypeArgumentsOfSignature) ? callSignature.target.typeParameters : callSignature.typeParameters; return typeParameters && typeParameters.length !== 0; } return false; } function writeLiteralType(type: ObjectType, flags: TypeFormatFlags) { if (__conv_self__.isGenericMappedType(type)) { writeMappedType(type); return; } const resolved = __conv_self__.resolveStructuredTypeMembers(type); if (!resolved.properties.length && !resolved.stringIndexInfo && !resolved.numberIndexInfo) { if (!resolved.callSignatures.length && !resolved.constructSignatures.length) { __conv_self__.writePunctuation(writer, SyntaxKind.OpenBraceToken); __conv_self__.writePunctuation(writer, SyntaxKind.CloseBraceToken); return; } if (resolved.callSignatures.length === 1 && !resolved.constructSignatures.length) { const parenthesizeSignature = shouldAddParenthesisAroundFunctionType(resolved.callSignatures[0], flags); if (parenthesizeSignature) { __conv_self__.writePunctuation(writer, SyntaxKind.OpenParenToken); } buildSignatureDisplay(resolved.callSignatures[0], writer, enclosingDeclaration, globalFlagsToPass | TypeFormatFlags.WriteArrowStyleSignature, /*kind*/ undefined, symbolStack); if (parenthesizeSignature) { __conv_self__.writePunctuation(writer, SyntaxKind.CloseParenToken); } return; } if (resolved.constructSignatures.length === 1 && !resolved.callSignatures.length) { if (flags & TypeFormatFlags.InElementType) { __conv_self__.writePunctuation(writer, SyntaxKind.OpenParenToken); } __conv_self__.writeKeyword(writer, SyntaxKind.NewKeyword); __conv_self__.writeSpace(writer); buildSignatureDisplay(resolved.constructSignatures[0], writer, enclosingDeclaration, globalFlagsToPass | TypeFormatFlags.WriteArrowStyleSignature, /*kind*/ undefined, symbolStack); if (flags & TypeFormatFlags.InElementType) { __conv_self__.writePunctuation(writer, SyntaxKind.CloseParenToken); } return; } } const saveInObjectTypeLiteral = inObjectTypeLiteral; inObjectTypeLiteral = true; __conv_self__.writePunctuation(writer, SyntaxKind.OpenBraceToken); writer.writeLine(); writer.increaseIndent(); writeObjectLiteralType(resolved); writer.decreaseIndent(); __conv_self__.writePunctuation(writer, SyntaxKind.CloseBraceToken); inObjectTypeLiteral = saveInObjectTypeLiteral; } function writeObjectLiteralType(resolved: ResolvedType) { for (const signature of resolved.callSignatures) { buildSignatureDisplay(signature, writer, enclosingDeclaration, globalFlagsToPass, /*kind*/ undefined, symbolStack); __conv_self__.writePunctuation(writer, SyntaxKind.SemicolonToken); writer.writeLine(); } for (const signature of resolved.constructSignatures) { buildSignatureDisplay(signature, writer, enclosingDeclaration, globalFlagsToPass, SignatureKind.Construct, symbolStack); __conv_self__.writePunctuation(writer, SyntaxKind.SemicolonToken); writer.writeLine(); } buildIndexSignatureDisplay(resolved.stringIndexInfo, writer, IndexKind.String, enclosingDeclaration, globalFlags, symbolStack); buildIndexSignatureDisplay(resolved.numberIndexInfo, writer, IndexKind.Number, enclosingDeclaration, globalFlags, symbolStack); for (const p of resolved.properties) { if (globalFlags & TypeFormatFlags.WriteClassExpressionAsTypeLiteral) { if (p.flags & SymbolFlags.Prototype) { continue; } if (getDeclarationModifierFlagsFromSymbol(p) & (ModifierFlags.Private | ModifierFlags.Protected)) { writer.reportPrivateInBaseOfClassExpression(unescapeLeadingUnderscores(p.escapedName)); } } const t = __conv_self__.getTypeOfSymbol(p); if (p.flags & (SymbolFlags.Function | SymbolFlags.Method) && !__conv_self__.getPropertiesOfObjectType(t).length) { const signatures = __conv_self__.getSignaturesOfType(t, SignatureKind.Call); for (const signature of signatures) { writePropertyWithModifiers(p); buildSignatureDisplay(signature, writer, enclosingDeclaration, globalFlagsToPass, /*kind*/ undefined, symbolStack); __conv_self__.writePunctuation(writer, SyntaxKind.SemicolonToken); writer.writeLine(); } } else { writePropertyWithModifiers(p); __conv_self__.writePunctuation(writer, SyntaxKind.ColonToken); __conv_self__.writeSpace(writer); writeType(t, globalFlags & TypeFormatFlags.WriteClassExpressionAsTypeLiteral); __conv_self__.writePunctuation(writer, SyntaxKind.SemicolonToken); writer.writeLine(); } } } function writeMappedType(type: MappedType) { __conv_self__.writePunctuation(writer, SyntaxKind.OpenBraceToken); writer.writeLine(); writer.increaseIndent(); if (type.declaration.readonlyToken) { __conv_self__.writeKeyword(writer, SyntaxKind.ReadonlyKeyword); __conv_self__.writeSpace(writer); } __conv_self__.writePunctuation(writer, SyntaxKind.OpenBracketToken); appendSymbolNameOnly(__conv_self__.getTypeParameterFromMappedType(type).symbol, writer); __conv_self__.writeSpace(writer); __conv_self__.writeKeyword(writer, SyntaxKind.InKeyword); __conv_self__.writeSpace(writer); writeType(__conv_self__.getConstraintTypeFromMappedType(type), TypeFormatFlags.None); __conv_self__.writePunctuation(writer, SyntaxKind.CloseBracketToken); if (type.declaration.questionToken) { __conv_self__.writePunctuation(writer, SyntaxKind.QuestionToken); } __conv_self__.writePunctuation(writer, SyntaxKind.ColonToken); __conv_self__.writeSpace(writer); writeType(__conv_self__.getTemplateTypeFromMappedType(type), TypeFormatFlags.None); __conv_self__.writePunctuation(writer, SyntaxKind.SemicolonToken); writer.writeLine(); writer.decreaseIndent(); __conv_self__.writePunctuation(writer, SyntaxKind.CloseBraceToken); } } function buildTypeParameterDisplayFromSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags) { const targetSymbol = __conv_self__.getTargetSymbol(symbol); if (targetSymbol.flags & SymbolFlags.Class || targetSymbol.flags & SymbolFlags.Interface || targetSymbol.flags & SymbolFlags.TypeAlias) { buildDisplayForTypeParametersAndDelimiters(__conv_self__.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol), writer, enclosingDeclaration, flags); } } function buildTypeParameterDisplay(tp: TypeParameter, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) { appendSymbolNameOnly(tp.symbol, writer); const constraint = __conv_self__.getConstraintOfTypeParameter(tp); if (constraint) { __conv_self__.writeSpace(writer); __conv_self__.writeKeyword(writer, SyntaxKind.ExtendsKeyword); __conv_self__.writeSpace(writer); buildTypeDisplay(constraint, writer, enclosingDeclaration, flags, symbolStack); } const defaultType = __conv_self__.getDefaultFromTypeParameter(tp); if (defaultType) { __conv_self__.writeSpace(writer); __conv_self__.writePunctuation(writer, SyntaxKind.EqualsToken); __conv_self__.writeSpace(writer); buildTypeDisplay(defaultType, writer, enclosingDeclaration, flags, symbolStack); } } function buildParameterDisplay(p: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) { const parameterNode = p.valueDeclaration; if (parameterNode ? isRestParameter(parameterNode) : __conv_self__.isTransientSymbol(p) && p.isRestParameter) { __conv_self__.writePunctuation(writer, SyntaxKind.DotDotDotToken); } if (parameterNode && isBindingPattern(parameterNode.name)) { buildBindingPatternDisplay(parameterNode.name, writer, enclosingDeclaration, flags, symbolStack); } else { appendSymbolNameOnly(p, writer); } if (parameterNode && __conv_self__.isOptionalParameter(parameterNode)) { __conv_self__.writePunctuation(writer, SyntaxKind.QuestionToken); } __conv_self__.writePunctuation(writer, SyntaxKind.ColonToken); __conv_self__.writeSpace(writer); let type = __conv_self__.getTypeOfSymbol(p); if (parameterNode && __conv_self__.isRequiredInitializedParameter(parameterNode)) { type = __conv_self__.getNullableType(type, TypeFlags.Undefined); } buildTypeDisplay(type, writer, enclosingDeclaration, flags, symbolStack); } function buildBindingPatternDisplay(bindingPattern: BindingPattern, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) { // We have to explicitly emit square bracket and bracket because these tokens are not stored inside the node. if (bindingPattern.kind === SyntaxKind.ObjectBindingPattern) { __conv_self__.writePunctuation(writer, SyntaxKind.OpenBraceToken); buildDisplayForCommaSeparatedList(bindingPattern.elements, writer, e => buildBindingElementDisplay(e, writer, enclosingDeclaration, flags, symbolStack)); __conv_self__.writePunctuation(writer, SyntaxKind.CloseBraceToken); } else if (bindingPattern.kind === SyntaxKind.ArrayBindingPattern) { __conv_self__.writePunctuation(writer, SyntaxKind.OpenBracketToken); const elements = bindingPattern.elements; buildDisplayForCommaSeparatedList(elements, writer, e => buildBindingElementDisplay(e, writer, enclosingDeclaration, flags, symbolStack)); if (elements && elements.hasTrailingComma) { __conv_self__.writePunctuation(writer, SyntaxKind.CommaToken); } __conv_self__.writePunctuation(writer, SyntaxKind.CloseBracketToken); } } function buildBindingElementDisplay(bindingElement: ArrayBindingElement, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) { if (isOmittedExpression(bindingElement)) { return; } Debug.assert(bindingElement.kind === SyntaxKind.BindingElement); if (bindingElement.propertyName) { writer.writeProperty(getTextOfNode(bindingElement.propertyName)); __conv_self__.writePunctuation(writer, SyntaxKind.ColonToken); __conv_self__.writeSpace(writer); } if (isBindingPattern(bindingElement.name)) { buildBindingPatternDisplay(bindingElement.name, writer, enclosingDeclaration, flags, symbolStack); } else { if (bindingElement.dotDotDotToken) { __conv_self__.writePunctuation(writer, SyntaxKind.DotDotDotToken); } appendSymbolNameOnly(bindingElement.symbol, writer); } } function buildDisplayForTypeParametersAndDelimiters(typeParameters: ReadonlyArray, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) { if (typeParameters && typeParameters.length) { __conv_self__.writePunctuation(writer, SyntaxKind.LessThanToken); buildDisplayForCommaSeparatedList(typeParameters, writer, p => buildTypeParameterDisplay(p, writer, enclosingDeclaration, flags, symbolStack)); __conv_self__.writePunctuation(writer, SyntaxKind.GreaterThanToken); } } function buildDisplayForCommaSeparatedList(list: ReadonlyArray, writer: SymbolWriter, action: (item: T) => void) { for (let i = 0; i < list.length; i++) { if (i > 0) { __conv_self__.writePunctuation(writer, SyntaxKind.CommaToken); __conv_self__.writeSpace(writer); } action(list[i]); } } function buildDisplayForTypeArgumentsAndDelimiters(typeParameters: ReadonlyArray, mapper: TypeMapper, writer: SymbolWriter, enclosingDeclaration?: Node) { if (typeParameters && typeParameters.length) { __conv_self__.writePunctuation(writer, SyntaxKind.LessThanToken); let flags = TypeFormatFlags.InFirstTypeArgument; for (let i = 0; i < typeParameters.length; i++) { if (i > 0) { __conv_self__.writePunctuation(writer, SyntaxKind.CommaToken); __conv_self__.writeSpace(writer); flags = TypeFormatFlags.None; } buildTypeDisplay(mapper(typeParameters[i]), writer, enclosingDeclaration, flags); } __conv_self__.writePunctuation(writer, SyntaxKind.GreaterThanToken); } } function buildDisplayForParametersAndDelimiters(thisParameter: Symbol | undefined, parameters: Symbol[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) { __conv_self__.writePunctuation(writer, SyntaxKind.OpenParenToken); if (thisParameter) { buildParameterDisplay(thisParameter, writer, enclosingDeclaration, flags, symbolStack); } for (let i = 0; i < parameters.length; i++) { if (i > 0 || thisParameter) { __conv_self__.writePunctuation(writer, SyntaxKind.CommaToken); __conv_self__.writeSpace(writer); } buildParameterDisplay(parameters[i], writer, enclosingDeclaration, flags, symbolStack); } __conv_self__.writePunctuation(writer, SyntaxKind.CloseParenToken); } function buildTypePredicateDisplay(predicate: TypePredicate, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]): void { if (isIdentifierTypePredicate(predicate)) { writer.writeParameter(predicate.parameterName); } else { __conv_self__.writeKeyword(writer, SyntaxKind.ThisKeyword); } __conv_self__.writeSpace(writer); __conv_self__.writeKeyword(writer, SyntaxKind.IsKeyword); __conv_self__.writeSpace(writer); buildTypeDisplay(predicate.type, writer, enclosingDeclaration, flags, symbolStack); } function buildReturnTypeDisplay(signature: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) { const returnType = __conv_self__.getReturnTypeOfSignature(signature); if (flags & TypeFormatFlags.SuppressAnyReturnType && __conv_self__.isTypeAny(returnType)) { return; } if (flags & TypeFormatFlags.WriteArrowStyleSignature) { __conv_self__.writeSpace(writer); __conv_self__.writePunctuation(writer, SyntaxKind.EqualsGreaterThanToken); } else { __conv_self__.writePunctuation(writer, SyntaxKind.ColonToken); } __conv_self__.writeSpace(writer); if (signature.typePredicate) { buildTypePredicateDisplay(signature.typePredicate, writer, enclosingDeclaration, flags, symbolStack); } else { buildTypeDisplay(returnType, writer, enclosingDeclaration, flags, symbolStack); } } function buildSignatureDisplay(signature: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, kind?: SignatureKind, symbolStack?: Symbol[]) { if (kind === SignatureKind.Construct) { __conv_self__.writeKeyword(writer, SyntaxKind.NewKeyword); __conv_self__.writeSpace(writer); } if (signature.target && (flags & TypeFormatFlags.WriteTypeArgumentsOfSignature)) { // Instantiated signature, write type arguments instead // This is achieved by passing in the mapper separately buildDisplayForTypeArgumentsAndDelimiters(signature.target.typeParameters, signature.mapper, writer, enclosingDeclaration); } else { buildDisplayForTypeParametersAndDelimiters(signature.typeParameters, writer, enclosingDeclaration, flags, symbolStack); } buildDisplayForParametersAndDelimiters(signature.thisParameter, signature.parameters, writer, enclosingDeclaration, flags, symbolStack); buildReturnTypeDisplay(signature, writer, enclosingDeclaration, flags, symbolStack); } function buildIndexSignatureDisplay(info: IndexInfo, writer: SymbolWriter, kind: IndexKind, enclosingDeclaration?: Node, globalFlags?: TypeFormatFlags, symbolStack?: Symbol[]) { if (info) { if (info.isReadonly) { __conv_self__.writeKeyword(writer, SyntaxKind.ReadonlyKeyword); __conv_self__.writeSpace(writer); } __conv_self__.writePunctuation(writer, SyntaxKind.OpenBracketToken); writer.writeParameter(info.declaration ? declarationNameToString(info.declaration.parameters[0].name) : "x"); __conv_self__.writePunctuation(writer, SyntaxKind.ColonToken); __conv_self__.writeSpace(writer); switch (kind) { case IndexKind.Number: __conv_self__.writeKeyword(writer, SyntaxKind.NumberKeyword); break; case IndexKind.String: __conv_self__.writeKeyword(writer, SyntaxKind.StringKeyword); break; } __conv_self__.writePunctuation(writer, SyntaxKind.CloseBracketToken); __conv_self__.writePunctuation(writer, SyntaxKind.ColonToken); __conv_self__.writeSpace(writer); buildTypeDisplay(info.type, writer, enclosingDeclaration, globalFlags, symbolStack); __conv_self__.writePunctuation(writer, SyntaxKind.SemicolonToken); writer.writeLine(); } } return this._displayBuilder || (this._displayBuilder = { buildSymbolDisplay, buildTypeDisplay, buildTypeParameterDisplay, buildTypePredicateDisplay, buildParameterDisplay, buildDisplayForParametersAndDelimiters, buildDisplayForTypeParametersAndDelimiters, buildTypeParameterDisplayFromSymbol, buildSignatureDisplay, buildIndexSignatureDisplay, buildReturnTypeDisplay }); } public isDeclarationVisible(node: Declaration): boolean { const __conv_self__ = this; if (node) { const links = this.getNodeLinks(node); if (links.isVisible === undefined) { links.isVisible = !!determineIfDeclarationIsVisible(); } return links.isVisible; } return false; function determineIfDeclarationIsVisible() { switch (node.kind) { case SyntaxKind.BindingElement: return __conv_self__.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 = __conv_self__.getDeclarationContainer(node); // If the node is not exported or it is not ambient module element (except import declaration) if (!(getCombinedModifierFlags(node) & ModifierFlags.Export) && !(node.kind !== SyntaxKind.ImportEqualsDeclaration && parent.kind !== SyntaxKind.SourceFile && isInAmbientContext(parent))) { return __conv_self__.isGlobalSourceFile(parent); } // Exported members/ambient module elements (exception import declaration) are visible if parent is visible return __conv_self__.isDeclarationVisible((parent)); case SyntaxKind.PropertyDeclaration: case SyntaxKind.PropertySignature: case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: case SyntaxKind.MethodDeclaration: case SyntaxKind.MethodSignature: if (hasModifier(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: return __conv_self__.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 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; } } } public collectLinkedAliases(node: Identifier): Node[] { const __conv_self__ = this; let exportSymbol: Symbol; if (node.parent && node.parent.kind === SyntaxKind.ExportAssignment) { exportSymbol = this.resolveName(node.parent, node.escapedText, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias, Diagnostics.Cannot_find_name_0, node); } else if (node.parent.kind === SyntaxKind.ExportSpecifier) { exportSymbol = this.getTargetOfExportSpecifier((node.parent), SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias); } const result: Node[] = []; if (exportSymbol) { buildVisibleNodeList(exportSymbol.declarations); } return result; function buildVisibleNodeList(declarations: Declaration[]) { forEach(declarations, declaration => { __conv_self__.getNodeLinks(declaration).isVisible = true; const resultNode = __conv_self__.getAnyImportSyntax(declaration) || declaration; if (!contains(result, resultNode)) { result.push(resultNode); } if (isInternalModuleImportEqualsDeclaration(declaration)) { // Add the referenced top container visible const internalModuleReference = (declaration).moduleReference; const firstIdentifier = __conv_self__.getFirstIdentifier(internalModuleReference); const importSymbol = __conv_self__.resolveName(declaration, firstIdentifier.escapedText, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace, undefined, undefined); if (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 */ public pushTypeResolution(target: TypeSystemEntity, propertyName: TypeSystemPropertyName): boolean { const resolutionCycleStartIndex = this.findResolutionCycleStartIndex(target, propertyName); if (resolutionCycleStartIndex >= 0) { // A cycle was found const { length } = this.resolutionTargets; for (let i = resolutionCycleStartIndex; i < length; i++) { this.resolutionResults[i] = false; } return false; } this.resolutionTargets.push(target); this.resolutionResults.push(/*items*/ true); this.resolutionPropertyNames.push(propertyName); return true; } public findResolutionCycleStartIndex(target: TypeSystemEntity, propertyName: TypeSystemPropertyName): number { for (let i = this.resolutionTargets.length - 1; i >= 0; i--) { if (this.hasType(this.resolutionTargets[i], this.resolutionPropertyNames[i])) { return -1; } if (this.resolutionTargets[i] === target && this.resolutionPropertyNames[i] === propertyName) { return i; } } return -1; } public hasType(target: TypeSystemEntity, propertyName: TypeSystemPropertyName): Type { if (propertyName === TypeSystemPropertyName.Type) { return this.getSymbolLinks((target)).type; } if (propertyName === TypeSystemPropertyName.DeclaredType) { return this.getSymbolLinks((target)).declaredType; } if (propertyName === TypeSystemPropertyName.ResolvedBaseConstructorType) { return (target).resolvedBaseConstructorType; } if (propertyName === TypeSystemPropertyName.ResolvedReturnType) { return (target).resolvedReturnType; } Debug.fail("Unhandled TypeSystemPropertyName " + 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. public popTypeResolution(): boolean { this.resolutionTargets.pop(); this.resolutionPropertyNames.pop(); return this.resolutionResults.pop(); } public getDeclarationContainer(node: Node): Node { node = 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; } }); return node && node.parent; } public getTypeOfPrototypeProperty(prototype: Symbol): Type { const __conv_self__ = this; // 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 = (this.getDeclaredTypeOfSymbol(this.getParentOfSymbol(prototype))); return classType.typeParameters ? this.createTypeReference((classType), map(classType.typeParameters, _ => __conv_self__.anyType)) : classType; } // Return the type of the given property in the given type, or undefined if no such property exists public getTypeOfPropertyOfType(type: Type, name: __String): Type { const prop = this.getPropertyOfType(type, name); return prop ? this.getTypeOfSymbol(prop) : undefined; } public isTypeAny(type: Type) { return type && (type.flags & TypeFlags.Any) !== 0; } // Return the type of a binding element parent. We check SymbolLinks first to see if a type has been // assigned by contextual typing. public getTypeForBindingElementParent(node: VariableLikeDeclaration) { const symbol = this.getSymbolOfNode(node); return symbol && this.getSymbolLinks(symbol).type || this.getTypeForVariableLikeDeclaration(node, /*includeOptionality*/ false); } public isComputedNonLiteralName(name: PropertyName): boolean { return name.kind === SyntaxKind.ComputedPropertyName && !isStringOrNumericLiteral((name).expression); } public getRestType(source: Type, properties: PropertyName[], symbol: Symbol): Type { const __conv_self__ = this; source = this.filterType(source, t => !(t.flags & TypeFlags.Nullable)); if (source.flags & TypeFlags.Never) { return this.emptyObjectType; } if (source.flags & TypeFlags.Union) { return this.mapType(source, t => __conv_self__.getRestType(t, properties, symbol)); } const members = createSymbolTable(); const names = createUnderscoreEscapedMap(); for (const name of properties) { names.set(getTextOfPropertyName(name), true); } for (const prop of this.getPropertiesOfType(source)) { const inNamesToRemove = names.has(prop.escapedName); const isPrivate = getDeclarationModifierFlagsFromSymbol(prop) & (ModifierFlags.Private | ModifierFlags.Protected); const isSetOnlyAccessor = prop.flags & SymbolFlags.SetAccessor && !(prop.flags & SymbolFlags.GetAccessor); if (!inNamesToRemove && !isPrivate && !this.isClassMethod(prop) && !isSetOnlyAccessor) { members.set(prop.escapedName, prop); } } const stringIndexInfo = this.getIndexInfoOfType(source, IndexKind.String); const numberIndexInfo = this.getIndexInfoOfType(source, IndexKind.Number); return this.createAnonymousType(symbol, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo); } /** Return the inferred type for a binding element */ public getTypeForBindingElement(declaration: BindingElement): Type { const pattern = declaration.parent; const parentType = this.getTypeForBindingElementParent(pattern.parent); // If parent has the unknown (error) type, then so does this binding element if (parentType === this.unknownType) { return this.unknownType; } // If no type was specified or inferred for parent, or if the specified or inferred type is any, // infer from the initializer of the binding element if one is present. Otherwise, go with the // undefined or any type of the parent. if (!parentType || this.isTypeAny(parentType)) { if (declaration.initializer) { return this.checkDeclarationInitializer(declaration); } return parentType; } let type: Type; if (pattern.kind === SyntaxKind.ObjectBindingPattern) { if (declaration.dotDotDotToken) { if (!this.isValidSpreadType(parentType)) { this.error(declaration, Diagnostics.Rest_types_may_only_be_created_from_object_types); return this.unknownType; } const literalMembers: PropertyName[] = []; for (const element of pattern.elements) { if (!(element as BindingElement).dotDotDotToken) { literalMembers.push(element.propertyName || element.name as Identifier); } } type = this.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; if (this.isComputedNonLiteralName(name)) { // computed properties with non-literal names are treated as 'any' return this.anyType; } if (declaration.initializer) { this.getContextualType(declaration.initializer); } // Use type of the specified property, or otherwise, for a numeric name, the type of the numeric index signature, // or otherwise the type of the string index signature. const text = getTextOfPropertyName(name); const declaredType = this.getTypeOfPropertyOfType(parentType, text); type = declaredType && this.getFlowTypeOfReference(declaration, declaredType) || this.isNumericLiteralName(text) && this.getIndexTypeOfType(parentType, IndexKind.Number) || this.getIndexTypeOfType(parentType, IndexKind.String); if (!type) { this.error(name, Diagnostics.Type_0_has_no_property_1_and_no_string_index_signature, this.typeToString(parentType), declarationNameToString(name)); return this.unknownType; } } } 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 = this.checkIteratedTypeOrElementType(parentType, pattern, /*allowStringInput*/ false, /*allowAsyncIterables*/ false); if (declaration.dotDotDotToken) { // Rest element has an array type with the same element type as the parent type type = this.createArrayType(elementType); } else { // Use specific property type when parent is a tuple or numeric index type when parent is an array const propName = "" + indexOf(pattern.elements, declaration); type = this.isTupleLikeType(parentType) ? this.getTypeOfPropertyOfType(parentType, (propName as __String)) : elementType; if (!type) { if (this.isTupleType(parentType)) { this.error(declaration, Diagnostics.Tuple_type_0_with_length_1_cannot_be_assigned_to_tuple_with_length_2, this.typeToString(parentType), this.getTypeReferenceArity((parentType)), pattern.elements.length); } else { this.error(declaration, Diagnostics.Type_0_has_no_property_1, this.typeToString(parentType), propName); } return this.unknownType; } } } // In strict null checking mode, if a default value of a non-undefined type is specified, remove // undefined from the final type. if (this.strictNullChecks && declaration.initializer && !(this.getFalsyFlags(this.checkExpressionCached(declaration.initializer)) & TypeFlags.Undefined)) { type = this.getTypeWithFacts(type, TypeFacts.NEUndefined); } return declaration.initializer ? this.getUnionType([type, this.checkExpressionCached(declaration.initializer)], /*subtypeReduction*/ true) : type; } public getTypeForDeclarationFromJSDocComment(declaration: Node) { const jsdocType = getJSDocType(declaration); if (jsdocType) { return this.getTypeFromTypeNode(jsdocType); } return undefined; } public isNullOrUndefined(node: Expression) { const expr = skipParentheses(node); return expr.kind === SyntaxKind.NullKeyword || expr.kind === SyntaxKind.Identifier && this.getResolvedSymbol((expr)) === this.undefinedSymbol; } public isEmptyArrayLiteral(node: Expression) { const expr = skipParentheses(node); return expr.kind === SyntaxKind.ArrayLiteralExpression && (expr).elements.length === 0; } public addOptionality(type: Type, optional: boolean): Type { return this.strictNullChecks && optional ? this.getNullableType(type, TypeFlags.Undefined) : type; } // Return the inferred type for a variable, parameter, or property declaration public getTypeForVariableLikeDeclaration(declaration: VariableLikeDeclaration, includeOptionality: boolean): Type { // 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 (declaration.parent.parent.kind === SyntaxKind.ForInStatement) { const indexType = this.getIndexType(this.checkNonNullExpression((declaration.parent.parent).expression)); return indexType.flags & (TypeFlags.TypeParameter | TypeFlags.Index) ? indexType : this.stringType; } if (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 this.checkRightHandSideOfForOf(forOfStatement.expression, forOfStatement.awaitModifier) || this.anyType; } if (isBindingPattern(declaration.parent)) { return this.getTypeForBindingElement((declaration)); } // Use type from type annotation if one is present const typeNode = getEffectiveTypeAnnotationNode(declaration); if (typeNode) { const declaredType = this.getTypeFromTypeNode(typeNode); return this.addOptionality(declaredType, /*optional*/ declaration.questionToken && includeOptionality); } if ((this.noImplicitAny || isInJavaScriptFile(declaration)) && declaration.kind === SyntaxKind.VariableDeclaration && !isBindingPattern(declaration.name) && !(getCombinedModifierFlags(declaration) & ModifierFlags.Export) && !isInAmbientContext(declaration)) { // 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 (!(getCombinedNodeFlags(declaration) & NodeFlags.Const) && (!declaration.initializer || this.isNullOrUndefined(declaration.initializer))) { return this.autoType; } // Use control flow tracked 'any[]' type for non-ambient, non-exported variables with an empty array // literal initializer. if (declaration.initializer && this.isEmptyArrayLiteral(declaration.initializer)) { return this.autoArrayType; } } if (declaration.kind === SyntaxKind.Parameter) { const func = declaration.parent; // For a parameter of a set accessor, use the type of the get accessor if one is present if (func.kind === SyntaxKind.SetAccessor && !hasDynamicName(func)) { const getter = getDeclarationOfKind(declaration.parent.symbol, SyntaxKind.GetAccessor); if (getter) { const getterSignature = this.getSignatureFromDeclaration(getter); const thisParameter = this.getAccessorThisParameter((func as AccessorDeclaration)); if (thisParameter && declaration === thisParameter) { // Use the type from the *getter* Debug.assert(!thisParameter.type); return this.getTypeOfSymbol(getterSignature.thisParameter); } return this.getReturnTypeOfSignature(getterSignature); } } // Use contextual parameter type if one is available let type: Type; if (declaration.symbol.escapedName === "this") { type = this.getContextualThisParameterType(func); } else { type = this.getContextuallyTypedParameterType((declaration)); } if (type) { return this.addOptionality(type, /*optional*/ declaration.questionToken && includeOptionality); } } // Use the type of the initializer expression if one is present if (declaration.initializer) { const type = this.checkDeclarationInitializer(declaration); return this.addOptionality(type, /*optional*/ declaration.questionToken && includeOptionality); } 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 this.trueType; } // If it is a short-hand property assignment, use the type of the identifier if (declaration.kind === SyntaxKind.ShorthandPropertyAssignment) { return this.checkIdentifier((declaration.name)); } // If the declaration specifies a binding pattern, use the type implied by the binding pattern if (isBindingPattern(declaration.name)) { return this.getTypeFromBindingPattern((declaration.name), /*includePatternInType*/ false, /*reportErrors*/ true); } // No type specified and nothing can be inferred return undefined; } public getWidenedTypeFromJSSpecialPropertyDeclarations(symbol: Symbol) { const types: Type[] = []; let definedInConstructor = false; let definedInMethod = false; let jsDocType: Type; for (const declaration of symbol.declarations) { const expression = declaration.kind === SyntaxKind.BinaryExpression ? declaration : declaration.kind === SyntaxKind.PropertyAccessExpression ? getAncestor(declaration, SyntaxKind.BinaryExpression) : undefined; if (!expression) { return this.unknownType; } if (isPropertyAccessExpression(expression.left) && expression.left.expression.kind === SyntaxKind.ThisKeyword) { if (getThisContainer(expression, /*includeArrowFunctions*/ false).kind === SyntaxKind.Constructor) { definedInConstructor = true; } else { definedInMethod = true; } } // If there is a JSDoc type, use it const type = this.getTypeForDeclarationFromJSDocComment(expression.parent); if (type) { const declarationType = this.getWidenedType(type); if (!jsDocType) { jsDocType = declarationType; } else if (jsDocType !== this.unknownType && declarationType !== this.unknownType && !this.isTypeIdenticalTo(jsDocType, declarationType)) { const name = getNameOfDeclaration(declaration); this.error(name, Diagnostics.Subsequent_variable_declarations_must_have_the_same_type_Variable_0_must_be_of_type_1_but_here_has_type_2, declarationNameToString(name), this.typeToString(jsDocType), this.typeToString(declarationType)); } } else if (!jsDocType) { // If we don't have an explicit JSDoc type, get the type from the expression. types.push(this.getWidenedLiteralType(this.checkExpressionCached(expression.right))); } } const type = jsDocType || this.getUnionType(types, /*subtypeReduction*/ true); return this.getWidenedType(this.addOptionality(type, definedInMethod && !definedInConstructor)); } // 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. public getTypeFromBindingElement(element: BindingElement, includePatternInType?: boolean, reportErrors?: boolean): Type { if (element.initializer) { return this.checkDeclarationInitializer(element); } if (isBindingPattern(element.name)) { return this.getTypeFromBindingPattern((element.name), includePatternInType, reportErrors); } if (reportErrors && this.noImplicitAny && !this.declarationBelongsToPrivateAmbientMember(element)) { this.reportImplicitAnyError(element, this.anyType); } return this.anyType; } // Return the type implied by an object binding pattern public getTypeFromObjectBindingPattern(pattern: ObjectBindingPattern, includePatternInType: boolean, reportErrors: boolean): Type { const __conv_self__ = this; const members = createSymbolTable(); let stringIndexInfo: IndexInfo; let hasComputedProperties = false; forEach(pattern.elements, e => { const name = e.propertyName || e.name; if (__conv_self__.isComputedNonLiteralName(name)) { // do not include computed properties in the implied type hasComputedProperties = true; return; } if (e.dotDotDotToken) { stringIndexInfo = __conv_self__.createIndexInfo(__conv_self__.anyType, /*isReadonly*/ false); return; } const text = getTextOfPropertyName(name); const flags = SymbolFlags.Property | (e.initializer ? SymbolFlags.Optional : 0); const symbol = __conv_self__.createSymbol(flags, text); symbol.type = __conv_self__.getTypeFromBindingElement(e, includePatternInType, reportErrors); symbol.bindingElement = e; members.set(symbol.escapedName, symbol); }); const result = this.createAnonymousType(undefined, members, emptyArray, emptyArray, stringIndexInfo, undefined); if (includePatternInType) { result.pattern = pattern; } if (hasComputedProperties) { result.objectFlags |= ObjectFlags.ObjectLiteralPatternWithComputedProperties; } return result; } // Return the type implied by an array binding pattern public getTypeFromArrayBindingPattern(pattern: BindingPattern, includePatternInType: boolean, reportErrors: boolean): Type { const __conv_self__ = this; const elements = pattern.elements; const lastElement = lastOrUndefined(elements); if (elements.length === 0 || (!isOmittedExpression(lastElement) && lastElement.dotDotDotToken)) { return this.languageVersion >= ScriptTarget.ES2015 ? this.createIterableType(this.anyType) : this.anyArrayType; } // If the pattern has at least one element, and no rest element, then it should imply a tuple type. const elementTypes = map(elements, e => isOmittedExpression(e) ? __conv_self__.anyType : __conv_self__.getTypeFromBindingElement(e, includePatternInType, reportErrors)); let result = this.createTupleType(elementTypes); if (includePatternInType) { result = this.cloneTypeReference(result); result.pattern = pattern; } 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. public getTypeFromBindingPattern(pattern: BindingPattern, includePatternInType?: boolean, reportErrors?: boolean): Type { return pattern.kind === SyntaxKind.ObjectBindingPattern ? this.getTypeFromObjectBindingPattern((pattern), includePatternInType, reportErrors) : this.getTypeFromArrayBindingPattern((pattern), includePatternInType, reportErrors); } // 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. public getWidenedTypeForVariableLikeDeclaration(declaration: VariableLikeDeclaration, reportErrors?: boolean): Type { let type = this.getTypeForVariableLikeDeclaration(declaration, /*includeOptionality*/ true); if (type) { if (reportErrors) { this.reportErrorsFromWidening(declaration, type); } // During a normal type check we'll never get to here with a property assignment (the check of the containing // object literal uses a different path). We exclude widening only so that language services and type verification // tools see the actual type. if (declaration.kind === SyntaxKind.PropertyAssignment) { return type; } return this.getWidenedType(type); } // Rest parameters default to type any[], other parameters default to type any type = declaration.dotDotDotToken ? this.anyArrayType : this.anyType; // Report implicit any errors unless this is a private property within an ambient declaration if (reportErrors && this.noImplicitAny) { if (!this.declarationBelongsToPrivateAmbientMember(declaration)) { this.reportImplicitAnyError(declaration, type); } } return type; } public declarationBelongsToPrivateAmbientMember(declaration: VariableLikeDeclaration) { const root = getRootDeclaration(declaration); const memberDeclaration = root.kind === SyntaxKind.Parameter ? root.parent : root; return this.isPrivateWithinAmbient(memberDeclaration); } public getTypeOfVariableOrParameterOrProperty(symbol: Symbol): Type { const links = this.getSymbolLinks(symbol); if (!links.type) { // Handle prototype property if (symbol.flags & SymbolFlags.Prototype) { return links.type = this.getTypeOfPrototypeProperty(symbol); } // Handle catch clause variables const declaration = symbol.valueDeclaration; if (isCatchClauseVariableDeclarationOrBindingElement(declaration)) { return links.type = this.anyType; } // Handle export default expressions if (declaration.kind === SyntaxKind.ExportAssignment) { return links.type = this.checkExpression((declaration).expression); } if (isInJavaScriptFile(declaration) && isJSDocPropertyLikeTag(declaration) && declaration.typeExpression) { return links.type = this.getTypeFromTypeNode(declaration.typeExpression.type); } // Handle variable, parameter or property if (!this.pushTypeResolution(symbol, TypeSystemPropertyName.Type)) { return this.unknownType; } let type: Type; // Handle certain special assignment kinds, which happen to union across multiple declarations: // * module.exports = expr // * exports.p = expr // * this.p = expr // * className.prototype.method = expr if (declaration.kind === SyntaxKind.BinaryExpression || declaration.kind === SyntaxKind.PropertyAccessExpression && declaration.parent.kind === SyntaxKind.BinaryExpression) { type = this.getWidenedTypeFromJSSpecialPropertyDeclarations(symbol); } else { type = this.getWidenedTypeForVariableLikeDeclaration((declaration), /*reportErrors*/ true); } if (!this.popTypeResolution()) { type = this.reportCircularityError(symbol); } links.type = type; } return links.type; } public getAnnotatedAccessorType(accessor: AccessorDeclaration): Type { if (accessor) { if (accessor.kind === SyntaxKind.GetAccessor) { const getterTypeAnnotation = getEffectiveReturnTypeNode(accessor); return getterTypeAnnotation && this.getTypeFromTypeNode(getterTypeAnnotation); } else { const setterTypeAnnotation = getEffectiveSetAccessorTypeAnnotationNode(accessor); return setterTypeAnnotation && this.getTypeFromTypeNode(setterTypeAnnotation); } } return undefined; } public getAnnotatedAccessorThisParameter(accessor: AccessorDeclaration): Symbol | undefined { const parameter = this.getAccessorThisParameter(accessor); return parameter && parameter.symbol; } public getThisTypeOfDeclaration(declaration: SignatureDeclaration): Type | undefined { return this.getThisTypeOfSignature(this.getSignatureFromDeclaration(declaration)); } public getTypeOfAccessors(symbol: Symbol): Type { const links = this.getSymbolLinks(symbol); if (!links.type) { const getter = getDeclarationOfKind(symbol, SyntaxKind.GetAccessor); const setter = getDeclarationOfKind(symbol, SyntaxKind.SetAccessor); if (getter && isInJavaScriptFile(getter)) { const jsDocType = this.getTypeForDeclarationFromJSDocComment(getter); if (jsDocType) { return links.type = jsDocType; } } if (!this.pushTypeResolution(symbol, TypeSystemPropertyName.Type)) { return this.unknownType; } let type: Type; // First try to see if the user specified a return type on the get-accessor. const getterReturnType = this.getAnnotatedAccessorType(getter); if (getterReturnType) { type = getterReturnType; } else { // If the user didn't specify a return type, try to use the set-accessor's parameter type. const setterParameterType = this.getAnnotatedAccessorType(setter); if (setterParameterType) { type = setterParameterType; } else { // If there are no specified types, try to infer it from the body of the get accessor if it exists. if (getter && getter.body) { type = this.getReturnTypeFromBody(getter); } else { if (this.noImplicitAny) { if (setter) { this.error(setter, Diagnostics.Property_0_implicitly_has_type_any_because_its_set_accessor_lacks_a_parameter_type_annotation, this.symbolToString(symbol)); } else { Debug.assert(!!getter, "there must existed getter as we are current checking either setter or getter in this function"); this.error(getter, Diagnostics.Property_0_implicitly_has_type_any_because_its_get_accessor_lacks_a_return_type_annotation, this.symbolToString(symbol)); } } type = this.anyType; } } } if (!this.popTypeResolution()) { type = this.anyType; if (this.noImplicitAny) { const getter = getDeclarationOfKind(symbol, SyntaxKind.GetAccessor); this.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, this.symbolToString(symbol)); } } links.type = type; } return links.type; } public getBaseTypeVariableOfClass(symbol: Symbol) { const baseConstructorType = this.getBaseConstructorTypeOfClass(this.getDeclaredTypeOfClassOrInterface(symbol)); return baseConstructorType.flags & TypeFlags.TypeVariable ? baseConstructorType : undefined; } public getTypeOfFuncClassEnumModule(symbol: Symbol): Type { const links = this.getSymbolLinks(symbol); if (!links.type) { if (symbol.flags & SymbolFlags.Module && isShorthandAmbientModuleSymbol(symbol)) { links.type = this.anyType; } else { const type = this.createObjectType(ObjectFlags.Anonymous, symbol); if (symbol.flags & SymbolFlags.Class) { const baseTypeVariable = this.getBaseTypeVariableOfClass(symbol); links.type = baseTypeVariable ? this.getIntersectionType([type, baseTypeVariable]) : type; } else { links.type = this.strictNullChecks && symbol.flags & SymbolFlags.Optional ? this.getNullableType(type, TypeFlags.Undefined) : type; } } } return links.type; } public getTypeOfEnumMember(symbol: Symbol): Type { const links = this.getSymbolLinks(symbol); if (!links.type) { links.type = this.getDeclaredTypeOfEnumMember(symbol); } return links.type; } public getTypeOfAlias(symbol: Symbol): Type { const links = this.getSymbolLinks(symbol); if (!links.type) { const targetSymbol = this.resolveAlias(symbol); // 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 = targetSymbol.flags & SymbolFlags.Value ? this.getTypeOfSymbol(targetSymbol) : this.unknownType; } return links.type; } public getTypeOfInstantiatedSymbol(symbol: Symbol): Type { const links = this.getSymbolLinks(symbol); if (!links.type) { if (this.symbolInstantiationDepth === 100) { this.error(symbol.valueDeclaration, Diagnostics.Generic_type_instantiation_is_excessively_deep_and_possibly_infinite); links.type = this.unknownType; } else { if (!this.pushTypeResolution(symbol, TypeSystemPropertyName.Type)) { return this.unknownType; } this.symbolInstantiationDepth++; let type = this.instantiateType(this.getTypeOfSymbol(links.target), links.mapper); this.symbolInstantiationDepth--; if (!this.popTypeResolution()) { type = this.reportCircularityError(symbol); } links.type = type; } } return links.type; } public reportCircularityError(symbol: Symbol) { // Check if variable has type annotation that circularly references the variable itself if (getEffectiveTypeAnnotationNode(symbol.valueDeclaration)) { this.error(symbol.valueDeclaration, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_type_annotation, this.symbolToString(symbol)); return this.unknownType; } // Otherwise variable has initializer that circularly references the variable itself if (this.noImplicitAny) { this.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, this.symbolToString(symbol)); } return this.anyType; } public getTypeOfSymbol(symbol: Symbol): Type { if (getCheckFlags(symbol) & CheckFlags.Instantiated) { return this.getTypeOfInstantiatedSymbol(symbol); } if (symbol.flags & (SymbolFlags.Variable | SymbolFlags.Property)) { return this.getTypeOfVariableOrParameterOrProperty(symbol); } if (symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.Enum | SymbolFlags.ValueModule)) { return this.getTypeOfFuncClassEnumModule(symbol); } if (symbol.flags & SymbolFlags.EnumMember) { return this.getTypeOfEnumMember(symbol); } if (symbol.flags & SymbolFlags.Accessor) { return this.getTypeOfAccessors(symbol); } if (symbol.flags & SymbolFlags.Alias) { return this.getTypeOfAlias(symbol); } return this.unknownType; } public isReferenceToType(type: Type, target: Type) { return type !== undefined && target !== undefined && (this.getObjectFlags(type) & ObjectFlags.Reference) !== 0 && (type).target === target; } public getTargetType(type: Type): Type { return this.getObjectFlags(type) & ObjectFlags.Reference ? (type).target : type; } public hasBaseType(type: Type, checkBase: Type) { const __conv_self__ = this; return check(type); function check(type: Type): boolean { if (__conv_self__.getObjectFlags(type) & (ObjectFlags.ClassOrInterface | ObjectFlags.Reference)) { const target = (__conv_self__.getTargetType(type)); return target === checkBase || forEach(__conv_self__.getBaseTypes(target), check); } else if (type.flags & TypeFlags.Intersection) { return forEach((type).types, check); } } } // 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. public appendTypeParameters(typeParameters: TypeParameter[], declarations: ReadonlyArray): TypeParameter[] { for (const declaration of declarations) { const tp = this.getDeclaredTypeOfTypeParameter(this.getSymbolOfNode(declaration)); if (!typeParameters) { typeParameters = [tp]; } else if (!contains(typeParameters, tp)) { typeParameters.push(tp); } } return typeParameters; } // Appends the outer type parameters of a node 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. public appendOuterTypeParameters(typeParameters: TypeParameter[], node: Node): TypeParameter[] { while (true) { node = node.parent; if (!node) { return typeParameters; } if (node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.ClassExpression || node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.FunctionExpression || node.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.ArrowFunction) { const declarations = (node).typeParameters; if (declarations) { return this.appendTypeParameters(this.appendOuterTypeParameters(typeParameters, node), declarations); } } } } // The outer type parameters are those defined by enclosing generic classes, methods, or functions. public getOuterTypeParametersOfClassOrInterface(symbol: Symbol): TypeParameter[] { const declaration = symbol.flags & SymbolFlags.Class ? symbol.valueDeclaration : getDeclarationOfKind(symbol, SyntaxKind.InterfaceDeclaration); return this.appendOuterTypeParameters(/*typeParameters*/ undefined, declaration); } // The local type parameters are the combined set of type parameters from all declarations of the class, // interface, or type alias. public getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol: Symbol): TypeParameter[] { let result: TypeParameter[]; for (const node of symbol.declarations) { if (node.kind === SyntaxKind.InterfaceDeclaration || node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.ClassExpression || node.kind === SyntaxKind.TypeAliasDeclaration) { const declaration = node; if (declaration.typeParameters) { result = this.appendTypeParameters(result, declaration.typeParameters); } } } 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. public getTypeParametersOfClassOrInterface(symbol: Symbol): TypeParameter[] { return concatenate(this.getOuterTypeParametersOfClassOrInterface(symbol), this.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[]. public isMixinConstructorType(type: Type) { const signatures = this.getSignaturesOfType(type, SignatureKind.Construct); if (signatures.length === 1) { const s = signatures[0]; return !s.typeParameters && s.parameters.length === 1 && s.hasRestParameter && this.getTypeOfParameter(s.parameters[0]) === this.anyArrayType; } return false; } public isConstructorType(type: Type): boolean { if (this.isValidBaseType(type) && this.getSignaturesOfType(type, SignatureKind.Construct).length > 0) { return true; } if (type.flags & TypeFlags.TypeVariable) { const constraint = this.getBaseConstraintOfType(type); return constraint && this.isValidBaseType(constraint) && this.isMixinConstructorType(constraint); } return false; } public getBaseTypeNodeOfClass(type: InterfaceType): ExpressionWithTypeArguments { return getClassExtendsHeritageClauseElement(type.symbol.valueDeclaration); } public getConstructorsForTypeArguments(type: Type, typeArgumentNodes: ReadonlyArray, location: Node): Signature[] { const __conv_self__ = this; const typeArgCount = length(typeArgumentNodes); const isJavaScript = isInJavaScriptFile(location); return filter(this.getSignaturesOfType(type, SignatureKind.Construct), sig => (isJavaScript || typeArgCount >= __conv_self__.getMinTypeArgumentCount(sig.typeParameters)) && typeArgCount <= length(sig.typeParameters)); } public getInstantiatedConstructorsForTypeArguments(type: Type, typeArgumentNodes: ReadonlyArray, location: Node): Signature[] { const __conv_self__ = this; const signatures = this.getConstructorsForTypeArguments(type, typeArgumentNodes, location); const typeArguments = map(typeArgumentNodes, this.getTypeFromTypeNode); return sameMap(signatures, sig => some(sig.typeParameters) ? __conv_self__.getSignatureInstantiation(sig, typeArguments) : sig); } /** * The base constructor of a class can resolve to * * undefinedType if the class has no extends clause, * * unknownType 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. */ public getBaseConstructorTypeOfClass(type: InterfaceType): Type { if (!type.resolvedBaseConstructorType) { const baseTypeNode = this.getBaseTypeNodeOfClass(type); if (!baseTypeNode) { return type.resolvedBaseConstructorType = this.undefinedType; } if (!this.pushTypeResolution(type, TypeSystemPropertyName.ResolvedBaseConstructorType)) { return this.unknownType; } const baseConstructorType = this.checkExpression(baseTypeNode.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. this.resolveStructuredTypeMembers((baseConstructorType)); } if (!this.popTypeResolution()) { this.error(type.symbol.valueDeclaration, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_base_expression, this.symbolToString(type.symbol)); return type.resolvedBaseConstructorType = this.unknownType; } if (!(baseConstructorType.flags & TypeFlags.Any) && baseConstructorType !== this.nullWideningType && !this.isConstructorType(baseConstructorType)) { this.error(baseTypeNode.expression, Diagnostics.Type_0_is_not_a_constructor_function_type, this.typeToString(baseConstructorType)); return type.resolvedBaseConstructorType = this.unknownType; } type.resolvedBaseConstructorType = baseConstructorType; } return type.resolvedBaseConstructorType; } public getBaseTypes(type: InterfaceType): BaseType[] { if (!type.resolvedBaseTypes) { if (type.objectFlags & ObjectFlags.Tuple) { type.resolvedBaseTypes = [this.createArrayType(this.getUnionType(type.typeParameters))]; } else if (type.symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { if (type.symbol.flags & SymbolFlags.Class) { this.resolveBaseTypesOfClass(type); } if (type.symbol.flags & SymbolFlags.Interface) { this.resolveBaseTypesOfInterface(type); } } else { Debug.fail("type must be class or interface"); } } return type.resolvedBaseTypes; } public resolveBaseTypesOfClass(type: InterfaceType): void { type.resolvedBaseTypes = type.resolvedBaseTypes || emptyArray; const baseConstructorType = this.getApparentType(this.getBaseConstructorTypeOfClass(type)); if (!(baseConstructorType.flags & (TypeFlags.Object | TypeFlags.Intersection | TypeFlags.Any))) { return; } const baseTypeNode = this.getBaseTypeNodeOfClass(type); const typeArgs = this.typeArgumentsFromTypeReferenceNode(baseTypeNode); let baseType: Type; const originalBaseType = baseConstructorType && baseConstructorType.symbol ? this.getDeclaredTypeOfSymbol(baseConstructorType.symbol) : undefined; if (baseConstructorType.symbol && baseConstructorType.symbol.flags & SymbolFlags.Class && this.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 = this.getTypeFromClassOrInterfaceReference(baseTypeNode, baseConstructorType.symbol, typeArgs); } 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 = this.getInstantiatedConstructorsForTypeArguments(baseConstructorType, baseTypeNode.typeArguments, baseTypeNode); if (!constructors.length) { this.error(baseTypeNode.expression, Diagnostics.No_base_constructor_has_the_specified_number_of_type_arguments); return; } baseType = this.getReturnTypeOfSignature(constructors[0]); } // In a JS file, you can use the @augments jsdoc tag to specify a base type with type parameters const valueDecl = type.symbol.valueDeclaration; if (valueDecl && isInJavaScriptFile(valueDecl)) { const augTag = getJSDocAugmentsTag(type.symbol.valueDeclaration); if (augTag) { baseType = this.getTypeFromTypeNode(augTag.typeExpression.type); } } if (baseType === this.unknownType) { return; } if (!this.isValidBaseType(baseType)) { this.error(baseTypeNode.expression, Diagnostics.Base_constructor_return_type_0_is_not_a_class_or_interface_type, this.typeToString(baseType)); return; } if (type === baseType || this.hasBaseType((baseType), type)) { this.error(valueDecl, Diagnostics.Type_0_recursively_references_itself_as_a_base_type, this.typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType)); return; } if (type.resolvedBaseTypes === emptyArray) { type.resolvedBaseTypes = [baseType]; } else { type.resolvedBaseTypes.push(baseType); } } public areAllOuterTypeParametersApplied(type: Type): boolean { // 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).outerTypeParameters; if (outerTypeParameters) { const last = outerTypeParameters.length - 1; const typeArguments = (type).typeArguments; return outerTypeParameters[last].symbol !== typeArguments[last].symbol; } return true; } // A valid base type is `any`, any non-generic object type or intersection of non-generic // object types. public isValidBaseType(type: Type): boolean { const __conv_self__ = this; return type.flags & (TypeFlags.Object | TypeFlags.NonPrimitive | TypeFlags.Any) && !this.isGenericMappedType(type) || type.flags & TypeFlags.Intersection && !forEach((type).types, t => !__conv_self__.isValidBaseType(t)); } public resolveBaseTypesOfInterface(type: InterfaceType): void { type.resolvedBaseTypes = type.resolvedBaseTypes || emptyArray; for (const declaration of type.symbol.declarations) { if (declaration.kind === SyntaxKind.InterfaceDeclaration && getInterfaceBaseTypeNodes(declaration)) { for (const node of getInterfaceBaseTypeNodes(declaration)) { const baseType = this.getTypeFromTypeNode(node); if (baseType !== this.unknownType) { if (this.isValidBaseType(baseType)) { if (type !== baseType && !this.hasBaseType((baseType), type)) { if (type.resolvedBaseTypes === emptyArray) { type.resolvedBaseTypes = [baseType]; } else { type.resolvedBaseTypes.push(baseType); } } else { this.error(declaration, Diagnostics.Type_0_recursively_references_itself_as_a_base_type, this.typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType)); } } else { this.error(node, Diagnostics.An_interface_may_only_extend_a_class_or_another_interface); } } } } } } // 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. public isIndependentInterface(symbol: Symbol): boolean { for (const declaration of symbol.declarations) { if (declaration.kind === SyntaxKind.InterfaceDeclaration) { if (declaration.flags & NodeFlags.ContainsThis) { return false; } const baseTypeNodes = getInterfaceBaseTypeNodes(declaration); if (baseTypeNodes) { for (const node of baseTypeNodes) { if (isEntityNameExpression(node.expression)) { const baseSymbol = this.resolveEntityName(node.expression, SymbolFlags.Type, /*ignoreErrors*/ true); if (!baseSymbol || !(baseSymbol.flags & SymbolFlags.Interface) || this.getDeclaredTypeOfClassOrInterface(baseSymbol).thisType) { return false; } } } } } } return true; } public getDeclaredTypeOfClassOrInterface(symbol: Symbol): InterfaceType { const links = this.getSymbolLinks(symbol); if (!links.declaredType) { const kind = symbol.flags & SymbolFlags.Class ? ObjectFlags.Class : ObjectFlags.Interface; const type = links.declaredType = (this.createObjectType(kind, symbol)); const outerTypeParameters = this.getOuterTypeParametersOfClassOrInterface(symbol); const localTypeParameters = this.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 || !this.isIndependentInterface(symbol)) { type.objectFlags |= ObjectFlags.Reference; type.typeParameters = concatenate(outerTypeParameters, localTypeParameters); type.outerTypeParameters = outerTypeParameters; type.localTypeParameters = localTypeParameters; (type).instantiations = createMap(); (type).instantiations.set(this.getTypeListId(type.typeParameters), (type)); (type).target = type; (type).typeArguments = type.typeParameters; type.thisType = (this.createType(TypeFlags.TypeParameter)); type.thisType.isThisType = true; type.thisType.symbol = symbol; type.thisType.constraint = type; } } return links.declaredType; } public getDeclaredTypeOfTypeAlias(symbol: Symbol): Type { const links = this.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 (!this.pushTypeResolution(symbol, TypeSystemPropertyName.DeclaredType)) { return this.unknownType; } const declaration = find(symbol.declarations, d => d.kind === SyntaxKind.JSDocTypedefTag || d.kind === SyntaxKind.TypeAliasDeclaration); let type = this.getTypeFromTypeNode(declaration.kind === SyntaxKind.JSDocTypedefTag ? declaration.typeExpression : declaration.type); if (this.popTypeResolution()) { const typeParameters = this.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 = createMap(); links.instantiations.set(this.getTypeListId(typeParameters), type); } } else { type = this.unknownType; this.error(declaration.name, Diagnostics.Type_alias_0_circularly_references_itself, this.symbolToString(symbol)); } links.declaredType = type; } return links.declaredType; } public isLiteralEnumMember(member: EnumMember) { const expr = member.initializer; if (!expr) { return !isInAmbientContext(member); } switch (expr.kind) { case SyntaxKind.StringLiteral: case SyntaxKind.NumericLiteral: return true; case SyntaxKind.PrefixUnaryExpression: return (expr).operator === SyntaxKind.MinusToken && (expr).operand.kind === SyntaxKind.NumericLiteral; case SyntaxKind.Identifier: return nodeIsMissing(expr) || !!this.getSymbolOfNode(member.parent).exports.get((expr).escapedText); default: return false; } } public getEnumKind(symbol: Symbol): EnumKind { const links = this.getSymbolLinks(symbol); if (links.enumKind !== undefined) { return links.enumKind; } let hasNonLiteralMember = false; for (const declaration of symbol.declarations) { if (declaration.kind === SyntaxKind.EnumDeclaration) { for (const member of (declaration).members) { if (member.initializer && member.initializer.kind === SyntaxKind.StringLiteral) { return links.enumKind = EnumKind.Literal; } if (!this.isLiteralEnumMember(member)) { hasNonLiteralMember = true; } } } } return links.enumKind = hasNonLiteralMember ? EnumKind.Numeric : EnumKind.Literal; } public getBaseTypeOfEnumLiteralType(type: Type) { return type.flags & TypeFlags.EnumLiteral && !(type.flags & TypeFlags.Union) ? this.getDeclaredTypeOfSymbol(this.getParentOfSymbol(type.symbol)) : type; } public getDeclaredTypeOfEnum(symbol: Symbol): Type { const links = this.getSymbolLinks(symbol); if (links.declaredType) { return links.declaredType; } if (this.getEnumKind(symbol) === EnumKind.Literal) { this.enumCount++; const memberTypeList: Type[] = []; for (const declaration of symbol.declarations) { if (declaration.kind === SyntaxKind.EnumDeclaration) { for (const member of (declaration).members) { const memberType = this.getLiteralType(this.getEnumMemberValue(member), this.enumCount, this.getSymbolOfNode(member)); this.getSymbolLinks(this.getSymbolOfNode(member)).declaredType = memberType; memberTypeList.push(memberType); } } } if (memberTypeList.length) { const enumType = this.getUnionType(memberTypeList, /*subtypeReduction*/ false, symbol, /*aliasTypeArguments*/ undefined); if (enumType.flags & TypeFlags.Union) { enumType.flags |= TypeFlags.EnumLiteral; enumType.symbol = symbol; } return links.declaredType = enumType; } } const enumType = this.createType(TypeFlags.Enum); enumType.symbol = symbol; return links.declaredType = enumType; } public getDeclaredTypeOfEnumMember(symbol: Symbol): Type { const links = this.getSymbolLinks(symbol); if (!links.declaredType) { const enumType = this.getDeclaredTypeOfEnum(this.getParentOfSymbol(symbol)); if (!links.declaredType) { links.declaredType = enumType; } } return links.declaredType; } public getDeclaredTypeOfTypeParameter(symbol: Symbol): TypeParameter { const links = this.getSymbolLinks(symbol); if (!links.declaredType) { const type = (this.createType(TypeFlags.TypeParameter)); type.symbol = symbol; links.declaredType = type; } return links.declaredType; } public getDeclaredTypeOfAlias(symbol: Symbol): Type { const links = this.getSymbolLinks(symbol); if (!links.declaredType) { links.declaredType = this.getDeclaredTypeOfSymbol(this.resolveAlias(symbol)); } return links.declaredType; } public getDeclaredTypeOfSymbol(symbol: Symbol): Type { if (symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { return this.getDeclaredTypeOfClassOrInterface(symbol); } if (symbol.flags & SymbolFlags.TypeAlias) { return this.getDeclaredTypeOfTypeAlias(symbol); } if (symbol.flags & SymbolFlags.TypeParameter) { return this.getDeclaredTypeOfTypeParameter(symbol); } if (symbol.flags & SymbolFlags.Enum) { return this.getDeclaredTypeOfEnum(symbol); } if (symbol.flags & SymbolFlags.EnumMember) { return this.getDeclaredTypeOfEnumMember(symbol); } if (symbol.flags & SymbolFlags.Alias) { return this.getDeclaredTypeOfAlias(symbol); } return this.unknownType; } // A type reference is considered independent if each type argument is considered independent. public isIndependentTypeReference(node: TypeReferenceNode): boolean { if (node.typeArguments) { for (const typeNode of node.typeArguments) { if (!this.isIndependentType(typeNode)) { return false; } } } return true; } // A type is considered independent if it the any, string, number, boolean, symbol, or void keyword, a string // literal type, an array with an element type that is considered independent, or a type reference that is // considered independent. public isIndependentType(node: TypeNode): boolean { switch (node.kind) { case SyntaxKind.AnyKeyword: case SyntaxKind.StringKeyword: case SyntaxKind.NumberKeyword: case SyntaxKind.BooleanKeyword: case SyntaxKind.SymbolKeyword: case SyntaxKind.ObjectKeyword: case SyntaxKind.VoidKeyword: case SyntaxKind.UndefinedKeyword: case SyntaxKind.NullKeyword: case SyntaxKind.NeverKeyword: case SyntaxKind.LiteralType: return true; case SyntaxKind.ArrayType: return this.isIndependentType((node).elementType); case SyntaxKind.TypeReference: return this.isIndependentTypeReference((node)); } return false; } // A variable-like declaration is considered independent (free of this references) if it has a type annotation // that specifies an independent type, or if it has no type annotation and no initializer (and thus of type any). public isIndependentVariableLikeDeclaration(node: VariableLikeDeclaration): boolean { const typeNode = getEffectiveTypeAnnotationNode(node); return typeNode ? this.isIndependentType(typeNode) : !node.initializer; } // A function-like declaration is considered independent (free of this references) if it has a return type // annotation that is considered independent and if each parameter is considered independent. public isIndependentFunctionLikeDeclaration(node: FunctionLikeDeclaration): boolean { if (node.kind !== SyntaxKind.Constructor) { const typeNode = getEffectiveReturnTypeNode(node); if (!typeNode || !this.isIndependentType(typeNode)) { return false; } } for (const parameter of node.parameters) { if (!this.isIndependentVariableLikeDeclaration(parameter)) { return false; } } return true; } // 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. public isIndependentMember(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 this.isIndependentVariableLikeDeclaration((declaration)); case SyntaxKind.MethodDeclaration: case SyntaxKind.MethodSignature: case SyntaxKind.Constructor: return this.isIndependentFunctionLikeDeclaration((declaration)); } } } 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. public createInstantiatedSymbolTable(symbols: Symbol[], mapper: TypeMapper, mappingThisOnly: boolean): SymbolTable { const result = createSymbolTable(); for (const symbol of symbols) { result.set(symbol.escapedName, mappingThisOnly && this.isIndependentMember(symbol) ? symbol : this.instantiateSymbol(symbol, mapper)); } return result; } public addInheritedMembers(symbols: SymbolTable, baseSymbols: Symbol[]) { for (const s of baseSymbols) { if (!symbols.has(s.escapedName)) { symbols.set(s.escapedName, s); } } } public resolveDeclaredMembers(type: InterfaceType): InterfaceTypeWithDeclaredMembers { if (!(type).declaredProperties) { const symbol = type.symbol; (type).declaredProperties = this.getNamedMembers(symbol.members); (type).declaredCallSignatures = this.getSignaturesOfSymbol(symbol.members.get(InternalSymbolName.Call)); (type).declaredConstructSignatures = this.getSignaturesOfSymbol(symbol.members.get(InternalSymbolName.New)); (type).declaredStringIndexInfo = this.getIndexInfoOfSymbol(symbol, IndexKind.String); (type).declaredNumberIndexInfo = this.getIndexInfoOfSymbol(symbol, IndexKind.Number); } return type; } public getTypeWithThisArgument(type: Type, thisArgument?: Type): Type { const __conv_self__ = this; if (this.getObjectFlags(type) & ObjectFlags.Reference) { const target = (type).target; const typeArguments = (type).typeArguments; if (length(target.typeParameters) === length(typeArguments)) { return this.createTypeReference(target, concatenate(typeArguments, [thisArgument || target.thisType])); } } else if (type.flags & TypeFlags.Intersection) { return this.getIntersectionType(map((type).types, t => __conv_self__.getTypeWithThisArgument(t, thisArgument))); } return type; } public resolveObjectTypeMembers(type: ObjectType, source: InterfaceTypeWithDeclaredMembers, typeParameters: TypeParameter[], typeArguments: Type[]) { let mapper: TypeMapper; let members: SymbolTable; let callSignatures: Signature[]; let constructSignatures: Signature[]; let stringIndexInfo: IndexInfo; let numberIndexInfo: IndexInfo; if (rangeEquals(typeParameters, typeArguments, 0, typeParameters.length)) { mapper = this.identityMapper; members = source.symbol ? source.symbol.members : createSymbolTable(source.declaredProperties); callSignatures = source.declaredCallSignatures; constructSignatures = source.declaredConstructSignatures; stringIndexInfo = source.declaredStringIndexInfo; numberIndexInfo = source.declaredNumberIndexInfo; } else { mapper = this.createTypeMapper(typeParameters, typeArguments); members = this.createInstantiatedSymbolTable(source.declaredProperties, mapper, /*mappingThisOnly*/ typeParameters.length === 1); callSignatures = this.instantiateSignatures(source.declaredCallSignatures, mapper); constructSignatures = this.instantiateSignatures(source.declaredConstructSignatures, mapper); stringIndexInfo = this.instantiateIndexInfo(source.declaredStringIndexInfo, mapper); numberIndexInfo = this.instantiateIndexInfo(source.declaredNumberIndexInfo, mapper); } const baseTypes = this.getBaseTypes(source); if (baseTypes.length) { if (source.symbol && members === source.symbol.members) { members = createSymbolTable(source.declaredProperties); } const thisArgument = lastOrUndefined(typeArguments); for (const baseType of baseTypes) { const instantiatedBaseType = thisArgument ? this.getTypeWithThisArgument(this.instantiateType(baseType, mapper), thisArgument) : baseType; this.addInheritedMembers(members, this.getPropertiesOfType(instantiatedBaseType)); callSignatures = concatenate(callSignatures, this.getSignaturesOfType(instantiatedBaseType, SignatureKind.Call)); constructSignatures = concatenate(constructSignatures, this.getSignaturesOfType(instantiatedBaseType, SignatureKind.Construct)); if (!stringIndexInfo) { stringIndexInfo = instantiatedBaseType === this.anyType ? this.createIndexInfo(this.anyType, /*isReadonly*/ false) : this.getIndexInfoOfType(instantiatedBaseType, IndexKind.String); } numberIndexInfo = numberIndexInfo || this.getIndexInfoOfType(instantiatedBaseType, IndexKind.Number); } } this.setStructuredTypeMembers(type, members, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo); } public resolveClassOrInterfaceMembers(type: InterfaceType): void { this.resolveObjectTypeMembers(type, this.resolveDeclaredMembers(type), emptyArray, emptyArray); } public resolveTypeReferenceMembers(type: TypeReference): void { const source = this.resolveDeclaredMembers(type.target); const typeParameters = concatenate(source.typeParameters, [source.thisType]); const typeArguments = type.typeArguments && type.typeArguments.length === typeParameters.length ? type.typeArguments : concatenate(type.typeArguments, [type]); this.resolveObjectTypeMembers(type, source, typeParameters, typeArguments); } public createSignature(declaration: SignatureDeclaration, typeParameters: TypeParameter[], thisParameter: Symbol | undefined, parameters: Symbol[], resolvedReturnType: Type, typePredicate: TypePredicate, minArgumentCount: number, hasRestParameter: boolean, hasLiteralTypes: boolean): Signature { const sig = new this.Signature(this.checker); sig.declaration = declaration; sig.typeParameters = typeParameters; sig.parameters = parameters; sig.thisParameter = thisParameter; sig.resolvedReturnType = resolvedReturnType; sig.typePredicate = typePredicate; sig.minArgumentCount = minArgumentCount; sig.hasRestParameter = hasRestParameter; sig.hasLiteralTypes = hasLiteralTypes; return sig; } public cloneSignature(sig: Signature): Signature { return this.createSignature(sig.declaration, sig.typeParameters, sig.thisParameter, sig.parameters, sig.resolvedReturnType, sig.typePredicate, sig.minArgumentCount, sig.hasRestParameter, sig.hasLiteralTypes); } public getDefaultConstructSignatures(classType: InterfaceType): Signature[] { const baseConstructorType = this.getBaseConstructorTypeOfClass(classType); const baseSignatures = this.getSignaturesOfType(baseConstructorType, SignatureKind.Construct); if (baseSignatures.length === 0) { return [this.createSignature(undefined, classType.localTypeParameters, undefined, emptyArray, classType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false)]; } const baseTypeNode = this.getBaseTypeNodeOfClass(classType); const isJavaScript = isInJavaScriptFile(baseTypeNode); const typeArguments = this.typeArgumentsFromTypeReferenceNode(baseTypeNode); const typeArgCount = length(typeArguments); const result: Signature[] = []; for (const baseSig of baseSignatures) { const minTypeArgumentCount = this.getMinTypeArgumentCount(baseSig.typeParameters); const typeParamCount = length(baseSig.typeParameters); if ((isJavaScript || typeArgCount >= minTypeArgumentCount) && typeArgCount <= typeParamCount) { const sig = typeParamCount ? this.createSignatureInstantiation(baseSig, this.fillMissingTypeArguments(typeArguments, baseSig.typeParameters, minTypeArgumentCount, baseTypeNode)) : this.cloneSignature(baseSig); sig.typeParameters = classType.localTypeParameters; sig.resolvedReturnType = classType; result.push(sig); } } return result; } public findMatchingSignature(signatureList: Signature[], signature: Signature, partialMatch: boolean, ignoreThisTypes: boolean, ignoreReturnTypes: boolean): Signature { for (const s of signatureList) { if (this.compareSignaturesIdentical(s, signature, partialMatch, ignoreThisTypes, ignoreReturnTypes, this.compareTypesIdentical)) { return s; } } } public findMatchingSignatures(signatureLists: Signature[][], signature: Signature, listIndex: number): Signature[] { 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 (!this.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 and different return types const match = i === listIndex ? signature : this.findMatchingSignature(signatureLists[i], signature, /*partialMatch*/ true, /*ignoreThisTypes*/ true, /*ignoreReturnTypes*/ true); if (!match) { return undefined; } if (!contains(result, match)) { (result || (result = [])).push(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. public getUnionSignatures(types: Type[], kind: SignatureKind): Signature[] { const __conv_self__ = this; const signatureLists = map(types, t => __conv_self__.getSignaturesOfType(t, kind)); let result: Signature[] = undefined; for (let i = 0; i < signatureLists.length; i++) { for (const signature of signatureLists[i]) { // Only process signatures with parameter lists that aren't already in the result list if (!result || !this.findMatchingSignature(result, signature, /*partialMatch*/ false, /*ignoreThisTypes*/ true, /*ignoreReturnTypes*/ true)) { const unionSignatures = this.findMatchingSignatures(signatureLists, signature, i); if (unionSignatures) { let s = signature; // Union the result types when more than one signature matches if (unionSignatures.length > 1) { s = this.cloneSignature(signature); if (forEach(unionSignatures, sig => sig.thisParameter)) { const thisType = this.getUnionType(map(unionSignatures, sig => __conv_self__.getTypeOfSymbol(sig.thisParameter) || __conv_self__.anyType), /*subtypeReduction*/ true); s.thisParameter = this.createSymbolWithType(signature.thisParameter, thisType); } // Clear resolved return type we possibly got from cloneSignature s.resolvedReturnType = undefined; s.unionSignatures = unionSignatures; } (result || (result = [])).push(s); } } } } return result || emptyArray; } public getUnionIndexInfo(types: Type[], kind: IndexKind): IndexInfo { const indexTypes: Type[] = []; let isAnyReadonly = false; for (const type of types) { const indexInfo = this.getIndexInfoOfType(type, kind); if (!indexInfo) { return undefined; } indexTypes.push(indexInfo.type); isAnyReadonly = isAnyReadonly || indexInfo.isReadonly; } return this.createIndexInfo(this.getUnionType(indexTypes, /*subtypeReduction*/ true), isAnyReadonly); } public 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 = this.getUnionSignatures(type.types, SignatureKind.Call); const constructSignatures = this.getUnionSignatures(type.types, SignatureKind.Construct); const stringIndexInfo = this.getUnionIndexInfo(type.types, IndexKind.String); const numberIndexInfo = this.getUnionIndexInfo(type.types, IndexKind.Number); this.setStructuredTypeMembers(type, this.emptySymbols, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo); } public intersectTypes(type1: Type, type2: Type): Type { return !type1 ? type2 : !type2 ? type1 : this.getIntersectionType([type1, type2]); } public intersectIndexInfos(info1: IndexInfo, info2: IndexInfo): IndexInfo { return !info1 ? info2 : !info2 ? info1 : this.createIndexInfo(this.getIntersectionType([info1.type, info2.type]), info1.isReadonly && info2.isReadonly); } public unionSpreadIndexInfos(info1: IndexInfo, info2: IndexInfo): IndexInfo { return info1 && info2 && this.createIndexInfo(this.getUnionType([info1.type, info2.type]), info1.isReadonly || info2.isReadonly); } public includeMixinType(type: Type, types: Type[], index: number): Type { const mixedTypes: Type[] = []; for (let i = 0; i < types.length; i++) { if (i === index) { mixedTypes.push(type); } else if (this.isMixinConstructorType(types[i])) { mixedTypes.push(this.getReturnTypeOfSignature(this.getSignaturesOfType(types[i], SignatureKind.Construct)[0])); } } return this.getIntersectionType(mixedTypes); } public resolveIntersectionTypeMembers(type: IntersectionType) { const __conv_self__ = this; // 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[] = emptyArray; let constructSignatures: Signature[] = emptyArray; let stringIndexInfo: IndexInfo; let numberIndexInfo: IndexInfo; const types = type.types; const mixinCount = countWhere(types, this.isMixinConstructorType); 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 (mixinCount === 0 || mixinCount === types.length && i === 0 || !this.isMixinConstructorType(t)) { let signatures = this.getSignaturesOfType(t, SignatureKind.Construct); if (signatures.length && mixinCount > 0) { signatures = map(signatures, s => { const clone = __conv_self__.cloneSignature(s); clone.resolvedReturnType = __conv_self__.includeMixinType(__conv_self__.getReturnTypeOfSignature(s), types, i); return clone; }); } constructSignatures = concatenate(constructSignatures, signatures); } callSignatures = concatenate(callSignatures, this.getSignaturesOfType(t, SignatureKind.Call)); stringIndexInfo = this.intersectIndexInfos(stringIndexInfo, this.getIndexInfoOfType(t, IndexKind.String)); numberIndexInfo = this.intersectIndexInfos(numberIndexInfo, this.getIndexInfoOfType(t, IndexKind.Number)); } this.setStructuredTypeMembers(type, this.emptySymbols, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo); } /** * Converts an AnonymousType to a ResolvedType. */ public resolveAnonymousTypeMembers(type: AnonymousType) { const symbol = type.symbol; if (type.target) { const members = this.createInstantiatedSymbolTable(this.getPropertiesOfObjectType(type.target), type.mapper, /*mappingThisOnly*/ false); const callSignatures = this.instantiateSignatures(this.getSignaturesOfType(type.target, SignatureKind.Call), type.mapper); const constructSignatures = this.instantiateSignatures(this.getSignaturesOfType(type.target, SignatureKind.Construct), type.mapper); const stringIndexInfo = this.instantiateIndexInfo(this.getIndexInfoOfType(type.target, IndexKind.String), type.mapper); const numberIndexInfo = this.instantiateIndexInfo(this.getIndexInfoOfType(type.target, IndexKind.Number), type.mapper); this.setStructuredTypeMembers(type, members, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo); } else if (symbol.flags & SymbolFlags.TypeLiteral) { const members = symbol.members; const callSignatures = this.getSignaturesOfSymbol(members.get(InternalSymbolName.Call)); const constructSignatures = this.getSignaturesOfSymbol(members.get(InternalSymbolName.New)); const stringIndexInfo = this.getIndexInfoOfSymbol(symbol, IndexKind.String); const numberIndexInfo = this.getIndexInfoOfSymbol(symbol, IndexKind.Number); this.setStructuredTypeMembers(type, members, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo); } else { // Combinations of function, class, enum and module let members = this.emptySymbols; let constructSignatures: Signature[] = emptyArray; let stringIndexInfo: IndexInfo = undefined; if (symbol.exports) { members = this.getExportsOfSymbol(symbol); } if (symbol.flags & SymbolFlags.Class) { const classType = this.getDeclaredTypeOfClassOrInterface(symbol); constructSignatures = this.getSignaturesOfSymbol(symbol.members.get(InternalSymbolName.Constructor)); if (!constructSignatures.length) { constructSignatures = this.getDefaultConstructSignatures(classType); } const baseConstructorType = this.getBaseConstructorTypeOfClass(classType); if (baseConstructorType.flags & (TypeFlags.Object | TypeFlags.Intersection | TypeFlags.TypeVariable)) { members = createSymbolTable(this.getNamedMembers(members)); this.addInheritedMembers(members, this.getPropertiesOfType(baseConstructorType)); } else if (baseConstructorType === this.anyType) { stringIndexInfo = this.createIndexInfo(this.anyType, /*isReadonly*/ false); } } const numberIndexInfo = symbol.flags & SymbolFlags.Enum ? this.enumNumberIndexInfo : undefined; this.setStructuredTypeMembers(type, members, emptyArray, constructSignatures, stringIndexInfo, numberIndexInfo); // 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 = this.getSignaturesOfSymbol(symbol); } } } /** Resolve the members of a mapped type { [P in K]: T } */ public resolveMappedTypeMembers(type: MappedType) { const __conv_self__ = this; const members: SymbolTable = createSymbolTable(); let stringIndexInfo: IndexInfo; // Resolve upfront such that recursive references see an empty object type. this.setStructuredTypeMembers(type, this.emptySymbols, emptyArray, emptyArray, undefined, undefined); // 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 = this.getTypeParameterFromMappedType(type); const constraintType = this.getConstraintTypeFromMappedType(type); const templateType = this.getTemplateTypeFromMappedType(type); const modifiersType = this.getApparentType(this.getModifiersTypeFromMappedType(type)); // The 'T' in 'keyof T' const templateReadonly = !!type.declaration.readonlyToken; const templateOptional = !!type.declaration.questionToken; if (type.declaration.typeParameter.constraint.kind === SyntaxKind.TypeOperator) { // We have a { [P in keyof T]: X } for (const propertySymbol of this.getPropertiesOfType(modifiersType)) { addMemberForKeyType(this.getLiteralTypeFromPropertyName(propertySymbol), propertySymbol); } if (this.getIndexInfoOfType(modifiersType, IndexKind.String)) { addMemberForKeyType(this.stringType); } } else { // First, if the constraint type is a type parameter, obtain the base constraint. Then, // if the key type is a 'keyof X', obtain 'keyof C' where C is the base constraint of X. // Finally, iterate over the constituents of the resulting iteration type. const keyType = constraintType.flags & TypeFlags.TypeVariable ? this.getApparentType(constraintType) : constraintType; const iterationType = keyType.flags & TypeFlags.Index ? this.getIndexType(this.getApparentType((keyType).type)) : keyType; this.forEachType(iterationType, addMemberForKeyType); } this.setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, undefined); function addMemberForKeyType(t: Type, propertySymbol?: Symbol) { // Create a mapper from T to the current iteration type constituent. Then, if the // mapped type is itself an instantiated type, combine the iteration mapper with the // instantiation mapper. const iterationMapper = __conv_self__.createTypeMapper([typeParameter], [t]); const templateMapper = type.mapper ? __conv_self__.combineTypeMappers(type.mapper, iterationMapper) : iterationMapper; const propType = __conv_self__.instantiateType(templateType, templateMapper); // If the current iteration type constituent is a string literal type, create a property. // Otherwise, for type string create a string index signature. if (t.flags & TypeFlags.StringLiteral) { const propName = escapeLeadingUnderscores((t).value); const modifiersProp = __conv_self__.getPropertyOfType(modifiersType, propName); const isOptional = templateOptional || !!(modifiersProp && modifiersProp.flags & SymbolFlags.Optional); const prop = __conv_self__.createSymbol(SymbolFlags.Property | (isOptional ? SymbolFlags.Optional : 0), propName); prop.checkFlags = templateReadonly || modifiersProp && __conv_self__.isReadonlySymbol(modifiersProp) ? CheckFlags.Readonly : 0; prop.type = propType; if (propertySymbol) { prop.syntheticOrigin = propertySymbol; prop.declarations = propertySymbol.declarations; } members.set(propName, prop); } else if (t.flags & TypeFlags.String) { stringIndexInfo = __conv_self__.createIndexInfo(propType, templateReadonly); } } } public getTypeParameterFromMappedType(type: MappedType) { return type.typeParameter || (type.typeParameter = this.getDeclaredTypeOfTypeParameter(this.getSymbolOfNode(type.declaration.typeParameter))); } public getConstraintTypeFromMappedType(type: MappedType) { return type.constraintType || (type.constraintType = this.instantiateType(this.getConstraintOfTypeParameter(this.getTypeParameterFromMappedType(type)), type.mapper || this.identityMapper) || this.unknownType); } public getTemplateTypeFromMappedType(type: MappedType) { return type.templateType || (type.templateType = type.declaration.type ? this.instantiateType(this.addOptionality(this.getTypeFromTypeNode(type.declaration.type), !!type.declaration.questionToken), type.mapper || this.identityMapper) : this.unknownType); } public getModifiersTypeFromMappedType(type: MappedType) { if (!type.modifiersType) { const constraintDeclaration = type.declaration.typeParameter.constraint; if (constraintDeclaration.kind === SyntaxKind.TypeOperator) { // 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 = this.instantiateType(this.getTypeFromTypeNode((constraintDeclaration).type), type.mapper || this.identityMapper); } 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 {}. const declaredType = (this.getTypeFromMappedTypeNode(type.declaration)); const constraint = this.getConstraintTypeFromMappedType(declaredType); const extendedConstraint = constraint && constraint.flags & TypeFlags.TypeParameter ? this.getConstraintOfTypeParameter((constraint)) : constraint; type.modifiersType = extendedConstraint && extendedConstraint.flags & TypeFlags.Index ? this.instantiateType((extendedConstraint).type, type.mapper || this.identityMapper) : this.emptyObjectType; } } return type.modifiersType; } public isPartialMappedType(type: Type) { return this.getObjectFlags(type) & ObjectFlags.Mapped && !!(type).declaration.questionToken; } public isGenericMappedType(type: Type) { return this.getObjectFlags(type) & ObjectFlags.Mapped && this.isGenericIndexType(this.getConstraintTypeFromMappedType((type))); } public resolveStructuredTypeMembers(type: StructuredType): ResolvedType { if (!(type).members) { if (type.flags & TypeFlags.Object) { if ((type).objectFlags & ObjectFlags.Reference) { this.resolveTypeReferenceMembers((type)); } else if ((type).objectFlags & ObjectFlags.ClassOrInterface) { this.resolveClassOrInterfaceMembers((type)); } else if ((type).objectFlags & ObjectFlags.Anonymous) { this.resolveAnonymousTypeMembers((type)); } else if ((type).objectFlags & ObjectFlags.Mapped) { this.resolveMappedTypeMembers((type)); } } else if (type.flags & TypeFlags.Union) { this.resolveUnionTypeMembers((type)); } else if (type.flags & TypeFlags.Intersection) { this.resolveIntersectionTypeMembers((type)); } } return type; } /** Return properties of an object type or an empty array for other types */ public getPropertiesOfObjectType(type: Type): Symbol[] { if (type.flags & TypeFlags.Object) { return this.resolveStructuredTypeMembers((type)).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. */ public getPropertyOfObjectType(type: Type, name: __String): Symbol { if (type.flags & TypeFlags.Object) { const resolved = this.resolveStructuredTypeMembers((type)); const symbol = resolved.members.get(name); if (symbol && this.symbolIsValue(symbol)) { return symbol; } } } public getPropertiesOfUnionOrIntersectionType(type: UnionOrIntersectionType): Symbol[] { if (!type.resolvedProperties) { const members = createSymbolTable(); for (const current of type.types) { for (const prop of this.getPropertiesOfType(current)) { if (!members.has(prop.escapedName)) { const combinedProp = this.getPropertyOfUnionOrIntersectionType(type, prop.escapedName); 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 if (type.flags & TypeFlags.Union) { break; } } type.resolvedProperties = this.getNamedMembers(members); } return type.resolvedProperties; } public getPropertiesOfType(type: Type): Symbol[] { type = this.getApparentType(type); return type.flags & TypeFlags.UnionOrIntersection ? this.getPropertiesOfUnionOrIntersectionType((type)) : this.getPropertiesOfObjectType(type); } public getAllPossiblePropertiesOfType(type: Type): Symbol[] { if (type.flags & TypeFlags.Union) { const props = createSymbolTable(); for (const memberType of (type as UnionType).types) { if (memberType.flags & TypeFlags.Primitive) { continue; } for (const { escapedName } of this.getPropertiesOfType(memberType)) { if (!props.has(escapedName)) { props.set(escapedName, this.createUnionOrIntersectionProperty((type as UnionType), escapedName)); } } } return arrayFrom(props.values()); } else { return this.getPropertiesOfType(type); } } public getConstraintOfType(type: TypeVariable | UnionOrIntersectionType): Type { return type.flags & TypeFlags.TypeParameter ? this.getConstraintOfTypeParameter((type)) : type.flags & TypeFlags.IndexedAccess ? this.getConstraintOfIndexedAccess((type)) : this.getBaseConstraintOfType(type); } public getConstraintOfTypeParameter(typeParameter: TypeParameter): Type { return this.hasNonCircularBaseConstraint(typeParameter) ? this.getConstraintFromTypeParameter(typeParameter) : undefined; } public getConstraintOfIndexedAccess(type: IndexedAccessType) { const transformed = this.getTransformedIndexedAccessType(type); if (transformed) { return transformed; } const baseObjectType = this.getBaseConstraintOfType(type.objectType); const baseIndexType = this.getBaseConstraintOfType(type.indexType); return baseObjectType || baseIndexType ? this.getIndexedAccessType(baseObjectType || type.objectType, baseIndexType || type.indexType) : undefined; } public getBaseConstraintOfType(type: Type): Type { if (type.flags & (TypeFlags.TypeVariable | TypeFlags.UnionOrIntersection)) { const constraint = this.getResolvedBaseConstraint((type)); if (constraint !== this.noConstraintType && constraint !== this.circularConstraintType) { return constraint; } } else if (type.flags & TypeFlags.Index) { return this.stringType; } return undefined; } public hasNonCircularBaseConstraint(type: TypeVariable): boolean { return this.getResolvedBaseConstraint(type) !== this.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. */ public getResolvedBaseConstraint(type: TypeVariable | UnionOrIntersectionType): Type { const __conv_self__ = this; let typeStack: Type[]; let circular: boolean; if (!type.resolvedBaseConstraint) { typeStack = []; const constraint = getBaseConstraint(type); type.resolvedBaseConstraint = circular ? this.circularConstraintType : this.getTypeWithThisArgument(constraint || this.noConstraintType, type); } return type.resolvedBaseConstraint; function getBaseConstraint(t: Type): Type { if (contains(typeStack, t)) { circular = true; return undefined; } typeStack.push(t); const result = computeBaseConstraint(t); typeStack.pop(); return result; } function computeBaseConstraint(t: Type): Type { if (t.flags & TypeFlags.TypeParameter) { const constraint = __conv_self__.getConstraintFromTypeParameter((t)); return (t).isThisType ? constraint : constraint ? getBaseConstraint(constraint) : undefined; } if (t.flags & TypeFlags.UnionOrIntersection) { const types = (t).types; const baseTypes: Type[] = []; for (const type of types) { const baseType = getBaseConstraint(type); if (baseType) { baseTypes.push(baseType); } } return t.flags & TypeFlags.Union && baseTypes.length === types.length ? __conv_self__.getUnionType(baseTypes) : t.flags & TypeFlags.Intersection && baseTypes.length ? __conv_self__.getIntersectionType(baseTypes) : undefined; } if (t.flags & TypeFlags.Index) { return __conv_self__.stringType; } if (t.flags & TypeFlags.IndexedAccess) { const transformed = __conv_self__.getTransformedIndexedAccessType((t)); if (transformed) { return getBaseConstraint(transformed); } const baseObjectType = getBaseConstraint((t).objectType); const baseIndexType = getBaseConstraint((t).indexType); const baseIndexedAccess = baseObjectType && baseIndexType ? __conv_self__.getIndexedAccessType(baseObjectType, baseIndexType) : undefined; return baseIndexedAccess && baseIndexedAccess !== __conv_self__.unknownType ? getBaseConstraint(baseIndexedAccess) : undefined; } if (__conv_self__.isGenericMappedType(t)) { return __conv_self__.emptyObjectType; } return t; } } public getApparentTypeOfIntersectionType(type: IntersectionType) { return type.resolvedApparentType || (type.resolvedApparentType = this.getTypeWithThisArgument(type, type)); } /** * 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, `undefined` * is returned. * * This function *does not* perform a circularity check. */ public getDefaultFromTypeParameter(typeParameter: TypeParameter): Type | undefined { if (!typeParameter.default) { if (typeParameter.target) { const targetDefault = this.getDefaultFromTypeParameter(typeParameter.target); typeParameter.default = targetDefault ? this.instantiateType(targetDefault, typeParameter.mapper) : this.noConstraintType; } else { const defaultDeclaration = typeParameter.symbol && forEach(typeParameter.symbol.declarations, decl => isTypeParameterDeclaration(decl) && decl.default); typeParameter.default = defaultDeclaration ? this.getTypeFromTypeNode(defaultDeclaration) : this.noConstraintType; } } return typeParameter.default === this.noConstraintType ? undefined : typeParameter.default; } /** * 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. Note that the apparent type of a union type is the union type itself. */ public getApparentType(type: Type): Type { const t = type.flags & TypeFlags.TypeVariable ? this.getBaseConstraintOfType(type) || this.emptyObjectType : type; return t.flags & TypeFlags.Intersection ? this.getApparentTypeOfIntersectionType((t)) : t.flags & TypeFlags.StringLike ? this.globalStringType : t.flags & TypeFlags.NumberLike ? this.globalNumberType : t.flags & TypeFlags.BooleanLike ? this.globalBooleanType : t.flags & TypeFlags.ESSymbol ? this.getGlobalESSymbolType(/*reportErrors*/ this.languageVersion >= ScriptTarget.ES2015) : t.flags & TypeFlags.NonPrimitive ? this.emptyObjectType : t; } public createUnionOrIntersectionProperty(containingType: UnionOrIntersectionType, name: __String): Symbol { let props: Symbol[]; const types = containingType.types; const isUnion = containingType.flags & TypeFlags.Union; const excludeModifiers = isUnion ? ModifierFlags.NonPublicAccessibilityModifier : 0; // Flags we want to propagate to the result if they exist in all source symbols let commonFlags = isUnion ? SymbolFlags.None : SymbolFlags.Optional; let syntheticFlag = CheckFlags.SyntheticMethod; let checkFlags = 0; for (const current of types) { const type = this.getApparentType(current); if (type !== this.unknownType) { const prop = this.getPropertyOfType(type, name); const modifiers = prop ? getDeclarationModifierFlagsFromSymbol(prop) : 0; if (prop && !(modifiers & excludeModifiers)) { commonFlags &= prop.flags; if (!props) { props = [prop]; } else if (!contains(props, prop)) { props.push(prop); } checkFlags |= (this.isReadonlySymbol(prop) ? CheckFlags.Readonly : 0) | (!(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 (!this.isMethodLike(prop)) { syntheticFlag = CheckFlags.SyntheticProperty; } } else if (isUnion) { checkFlags |= CheckFlags.Partial; } } } if (!props) { return undefined; } if (props.length === 1 && !(checkFlags & CheckFlags.Partial)) { return props[0]; } const propTypes: Type[] = []; const declarations: Declaration[] = []; let commonType: Type = undefined; for (const prop of props) { if (prop.declarations) { addRange(declarations, prop.declarations); } const type = this.getTypeOfSymbol(prop); if (!commonType) { commonType = type; } else if (type !== commonType) { checkFlags |= CheckFlags.HasNonUniformType; } propTypes.push(type); } const result = this.createSymbol(SymbolFlags.Property | commonFlags, name); result.checkFlags = syntheticFlag | checkFlags; result.containingType = containingType; result.declarations = declarations; result.type = isUnion ? this.getUnionType(propTypes) : this.getIntersectionType(propTypes); 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. public getUnionOrIntersectionProperty(type: UnionOrIntersectionType, name: __String): Symbol { const properties = type.propertyCache || (type.propertyCache = createSymbolTable()); let property = properties.get(name); if (!property) { property = this.createUnionOrIntersectionProperty(type, name); if (property) { properties.set(name, property); } } return property; } public getPropertyOfUnionOrIntersectionType(type: UnionOrIntersectionType, name: __String): Symbol { const property = this.getUnionOrIntersectionProperty(type, name); // We need to filter out partial properties in union types return property && !(getCheckFlags(property) & CheckFlags.Partial) ? property : undefined; } /** * 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 */ public getPropertyOfType(type: Type, name: __String): Symbol | undefined { type = this.getApparentType(type); if (type.flags & TypeFlags.Object) { const resolved = this.resolveStructuredTypeMembers((type)); const symbol = resolved.members.get(name); if (symbol && this.symbolIsValue(symbol)) { return symbol; } if (resolved === this.anyFunctionType || resolved.callSignatures.length || resolved.constructSignatures.length) { const symbol = this.getPropertyOfObjectType(this.globalFunctionType, name); if (symbol) { return symbol; } } return this.getPropertyOfObjectType(this.globalObjectType, name); } if (type.flags & TypeFlags.UnionOrIntersection) { return this.getPropertyOfUnionOrIntersectionType((type), name); } return undefined; } public getSignaturesOfStructuredType(type: Type, kind: SignatureKind): Signature[] { if (type.flags & TypeFlags.StructuredType) { const resolved = this.resolveStructuredTypeMembers((type)); 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. */ public getSignaturesOfType(type: Type, kind: SignatureKind): Signature[] { return this.getSignaturesOfStructuredType(this.getApparentType(type), kind); } public getIndexInfoOfStructuredType(type: Type, kind: IndexKind): IndexInfo | undefined { if (type.flags & TypeFlags.StructuredType) { const resolved = this.resolveStructuredTypeMembers((type)); return kind === IndexKind.String ? resolved.stringIndexInfo : resolved.numberIndexInfo; } } public getIndexTypeOfStructuredType(type: Type, kind: IndexKind): Type | undefined { const info = this.getIndexInfoOfStructuredType(type, kind); return info && info.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. public getIndexInfoOfType(type: Type, kind: IndexKind): IndexInfo { return this.getIndexInfoOfStructuredType(this.getApparentType(type), kind); } // 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. public getIndexTypeOfType(type: Type, kind: IndexKind): Type { return this.getIndexTypeOfStructuredType(this.getApparentType(type), kind); } public getImplicitIndexTypeOfType(type: Type, kind: IndexKind): Type { if (this.isObjectLiteralType(type)) { const propTypes: Type[] = []; for (const prop of this.getPropertiesOfType(type)) { if (kind === IndexKind.String || this.isNumericLiteralName(prop.escapedName)) { propTypes.push(this.getTypeOfSymbol(prop)); } } if (propTypes.length) { return this.getUnionType(propTypes, /*subtypeReduction*/ true); } } return undefined; } // Return list of type parameters with duplicates removed (duplicate identifier errors are generated in the actual // type checking functions). public getTypeParametersFromDeclaration(declaration: DeclarationWithTypeParameters): TypeParameter[] { const __conv_self__ = this; let result: TypeParameter[]; forEach(getEffectiveTypeParameterDeclarations(declaration), node => { const tp = __conv_self__.getDeclaredTypeOfTypeParameter(node.symbol); if (!contains(result, tp)) { if (!result) { result = []; } result.push(tp); } }); return result; } public symbolsToArray(symbols: SymbolTable): Symbol[] { const __conv_self__ = this; const result: Symbol[] = []; symbols.forEach((symbol, id) => { if (!__conv_self__.isReservedMemberName(id)) { result.push(symbol); } }); return result; } public isJSDocOptionalParameter(node: ParameterDeclaration) { if (isInJavaScriptFile(node)) { if (node.type && node.type.kind === SyntaxKind.JSDocOptionalType) { return true; } const paramTags = getJSDocParameterTags(node); if (paramTags) { for (const paramTag of paramTags) { if (paramTag.isBracketed) { return true; } if (paramTag.typeExpression) { return paramTag.typeExpression.type.kind === SyntaxKind.JSDocOptionalType; } } } } } public tryFindAmbientModule(moduleName: string, withAugmentations: boolean) { if (isExternalModuleNameRelative(moduleName)) { return undefined; } const symbol = this.getSymbol(this.globals, (`"${moduleName}"` as __String), SymbolFlags.ValueModule); // merged symbol is module declaration symbol combined with all augmentations return symbol && withAugmentations ? this.getMergedSymbol(symbol) : symbol; } public isOptionalParameter(node: ParameterDeclaration) { if (hasQuestionToken(node) || this.isJSDocOptionalParameter(node)) { return true; } if (node.initializer) { const signatureDeclaration = node.parent; const signature = this.getSignatureFromDeclaration(signatureDeclaration); const parameterIndex = ts.indexOf(signatureDeclaration.parameters, node); Debug.assert(parameterIndex >= 0); return parameterIndex >= signature.minArgumentCount; } const iife = getImmediatelyInvokedFunctionExpression(node.parent); if (iife) { return !node.type && !node.dotDotDotToken && indexOf((node.parent as SignatureDeclaration).parameters, node) >= iife.arguments.length; } return false; } public createTypePredicateFromTypePredicateNode(node: TypePredicateNode): IdentifierTypePredicate | ThisTypePredicate { const { parameterName } = node; if (parameterName.kind === SyntaxKind.Identifier) { return { kind: TypePredicateKind.Identifier, parameterName: parameterName ? parameterName.escapedText : undefined, parameterIndex: parameterName ? this.getTypePredicateParameterIndex((node.parent as SignatureDeclaration).parameters, parameterName) : undefined, type: this.getTypeFromTypeNode(node.type) } as IdentifierTypePredicate; } else { return { kind: TypePredicateKind.This, type: this.getTypeFromTypeNode(node.type) }; } } /** * Gets the minimum number of type arguments needed to satisfy all non-optional type * parameters. */ public getMinTypeArgumentCount(typeParameters: TypeParameter[] | undefined): number { let minTypeArgumentCount = 0; if (typeParameters) { for (let i = 0; i < typeParameters.length; i++) { if (!this.getDefaultFromTypeParameter(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. */ public fillMissingTypeArguments(typeArguments: Type[] | undefined, typeParameters: TypeParameter[] | undefined, minTypeArgumentCount: number, location?: Node) { const numTypeParameters = length(typeParameters); if (numTypeParameters) { const numTypeArguments = length(typeArguments); const isJavaScript = isInJavaScriptFile(location); if ((isJavaScript || numTypeArguments >= minTypeArgumentCount) && numTypeArguments <= numTypeParameters) { if (!typeArguments) { typeArguments = []; } // Map an unsatisfied type parameter with a default type. // If a type parameter does not have a default type, or if the default type // is a forward reference, the empty object type is used. for (let i = numTypeArguments; i < numTypeParameters; i++) { typeArguments[i] = this.getDefaultTypeArgumentType(isJavaScript); } for (let i = numTypeArguments; i < numTypeParameters; i++) { const mapper = this.createTypeMapper(typeParameters, typeArguments); const defaultType = this.getDefaultFromTypeParameter(typeParameters[i]); typeArguments[i] = defaultType ? this.instantiateType(defaultType, mapper) : this.getDefaultTypeArgumentType(isJavaScript); } } } return typeArguments; } public getSignatureFromDeclaration(declaration: SignatureDeclaration): Signature { const links = this.getNodeLinks(declaration); if (!links.resolvedSignature) { const parameters: Symbol[] = []; let hasLiteralTypes = false; let minArgumentCount = 0; let thisParameter: Symbol = undefined; let hasThisParameter: boolean; const iife = getImmediatelyInvokedFunctionExpression(declaration); const isJSConstructSignature = isJSDocConstructSignature(declaration); const isUntypedSignatureInJSFile = !iife && !isJSConstructSignature && isInJavaScriptFile(declaration) && !hasJSDocParameterTags(declaration); // 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]; let paramSymbol = param.symbol; // Include parameter symbol instead of property symbol in the signature if (paramSymbol && !!(paramSymbol.flags & SymbolFlags.Property) && !isBindingPattern(param.name)) { const resolvedSymbol = this.resolveName(param, paramSymbol.escapedName, SymbolFlags.Value, undefined, undefined); paramSymbol = resolvedSymbol; } if (i === 0 && paramSymbol.escapedName === "this") { hasThisParameter = true; thisParameter = param.symbol; } else { parameters.push(paramSymbol); } if (param.type && param.type.kind === SyntaxKind.LiteralType) { hasLiteralTypes = true; } // Record a new minimum argument count if this is not an optional parameter const isOptionalParameter = param.initializer || param.questionToken || param.dotDotDotToken || iife && parameters.length > iife.arguments.length && !param.type || this.isJSDocOptionalParameter(param) || isUntypedSignatureInJSFile; 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) && !hasDynamicName(declaration) && (!hasThisParameter || !thisParameter)) { const otherKind = declaration.kind === SyntaxKind.GetAccessor ? SyntaxKind.SetAccessor : SyntaxKind.GetAccessor; const other = getDeclarationOfKind(declaration.symbol, otherKind); if (other) { thisParameter = this.getAnnotatedAccessorThisParameter(other); } } const classType = declaration.kind === SyntaxKind.Constructor ? this.getDeclaredTypeOfClassOrInterface(this.getMergedSymbol((declaration.parent).symbol)) : undefined; const typeParameters = classType ? classType.localTypeParameters : this.getTypeParametersFromDeclaration(declaration); const returnType = this.getSignatureReturnTypeFromDeclaration(declaration, isJSConstructSignature, classType); const typePredicate = declaration.type && declaration.type.kind === SyntaxKind.TypePredicate ? this.createTypePredicateFromTypePredicateNode((declaration.type as TypePredicateNode)) : undefined; // JS functions get a free rest parameter if they reference `arguments` let hasRestLikeParameter = hasRestParameter(declaration); if (!hasRestLikeParameter && isInJavaScriptFile(declaration) && this.containsArgumentsReference(declaration)) { hasRestLikeParameter = true; const syntheticArgsSymbol = this.createSymbol(SymbolFlags.Variable, ("args" as __String)); syntheticArgsSymbol.type = this.anyArrayType; syntheticArgsSymbol.isRestParameter = true; parameters.push(syntheticArgsSymbol); } links.resolvedSignature = this.createSignature(declaration, typeParameters, thisParameter, parameters, returnType, typePredicate, minArgumentCount, hasRestLikeParameter, hasLiteralTypes); } return links.resolvedSignature; } public getSignatureReturnTypeFromDeclaration(declaration: SignatureDeclaration, isJSConstructSignature: boolean, classType: Type) { if (isJSConstructSignature) { return this.getTypeFromTypeNode(declaration.parameters[0].type); } else if (classType) { return classType; } const typeNode = getEffectiveReturnTypeNode(declaration); if (typeNode) { return this.getTypeFromTypeNode(typeNode); } // TypeScript 1.0 spec (April 2014): // If only one accessor includes a type annotation, the other behaves as if it had the same type annotation. if (declaration.kind === SyntaxKind.GetAccessor && !hasDynamicName(declaration)) { const setter = getDeclarationOfKind(declaration.symbol, SyntaxKind.SetAccessor); return this.getAnnotatedAccessorType(setter); } if (nodeIsMissing((declaration).body)) { return this.anyType; } } public containsArgumentsReference(declaration: SignatureDeclaration): boolean { const links = this.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).escapedText === "arguments" && isPartOfExpression(node); case SyntaxKind.PropertyDeclaration: case SyntaxKind.MethodDeclaration: case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: return (node).name.kind === SyntaxKind.ComputedPropertyName && traverse((node).name); default: return !nodeStartsNewLexicalEnvironment(node) && !isPartOfTypeNode(node) && forEachChild(node, traverse); } } } public getSignaturesOfSymbol(symbol: Symbol): Signature[] { if (!symbol) return emptyArray; const result: Signature[] = []; for (let i = 0; i < symbol.declarations.length; i++) { const node = symbol.declarations[i]; switch (node.kind) { case SyntaxKind.FunctionType: case SyntaxKind.ConstructorType: case SyntaxKind.FunctionDeclaration: case SyntaxKind.MethodDeclaration: case SyntaxKind.MethodSignature: case SyntaxKind.Constructor: case SyntaxKind.CallSignature: case SyntaxKind.ConstructSignature: case SyntaxKind.IndexSignature: case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: case SyntaxKind.JSDocFunctionType: // 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 && (node).body) { const previous = symbol.declarations[i - 1]; if (node.parent === previous.parent && node.kind === previous.kind && node.pos === previous.end) { break; } } result.push(this.getSignatureFromDeclaration((node))); } } return result; } public resolveExternalModuleTypeByLiteral(name: StringLiteral) { const moduleSym = this.resolveExternalModuleName(name, name); if (moduleSym) { const resolvedModuleSymbol = this.resolveExternalModuleSymbol(moduleSym); if (resolvedModuleSymbol) { return this.getTypeOfSymbol(resolvedModuleSymbol); } } return this.anyType; } public getThisTypeOfSignature(signature: Signature): Type | undefined { if (signature.thisParameter) { return this.getTypeOfSymbol(signature.thisParameter); } } public getReturnTypeOfSignature(signature: Signature): Type { if (!signature.resolvedReturnType) { if (!this.pushTypeResolution(signature, TypeSystemPropertyName.ResolvedReturnType)) { return this.unknownType; } let type: Type; if (signature.target) { type = this.instantiateType(this.getReturnTypeOfSignature(signature.target), signature.mapper); } else if (signature.unionSignatures) { type = this.getUnionType(map(signature.unionSignatures, this.getReturnTypeOfSignature), /*subtypeReduction*/ true); } else { type = this.getReturnTypeFromBody((signature.declaration)); } if (!this.popTypeResolution()) { type = this.anyType; if (this.noImplicitAny) { const declaration = signature.declaration; const name = getNameOfDeclaration(declaration); if (name) { this.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 { this.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); } } } signature.resolvedReturnType = type; } return signature.resolvedReturnType; } public getRestTypeOfSignature(signature: Signature): Type { if (signature.hasRestParameter) { const type = this.getTypeOfSymbol(lastOrUndefined(signature.parameters)); if (this.getObjectFlags(type) & ObjectFlags.Reference && (type).target === this.globalArrayType) { return (type).typeArguments[0]; } } return this.anyType; } public getSignatureInstantiation(signature: Signature, typeArguments: Type[]): Signature { typeArguments = this.fillMissingTypeArguments(typeArguments, signature.typeParameters, this.getMinTypeArgumentCount(signature.typeParameters)); const instantiations = signature.instantiations || (signature.instantiations = createMap()); const id = this.getTypeListId(typeArguments); let instantiation = instantiations.get(id); if (!instantiation) { instantiations.set(id, instantiation = this.createSignatureInstantiation(signature, typeArguments)); } return instantiation; } public createSignatureInstantiation(signature: Signature, typeArguments: Type[]): Signature { return this.instantiateSignature(signature, this.createTypeMapper(signature.typeParameters, typeArguments), /*eraseTypeParameters*/ true); } public getErasedSignature(signature: Signature): Signature { if (!signature.typeParameters) return signature; if (!signature.erasedSignatureCache) { signature.erasedSignatureCache = this.instantiateSignature(signature, this.createTypeEraser(signature.typeParameters), /*eraseTypeParameters*/ true); } return signature.erasedSignatureCache; } public getOrCreateTypeFromSignature(signature: Signature): 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 isConstructor = signature.declaration.kind === SyntaxKind.Constructor || signature.declaration.kind === SyntaxKind.ConstructSignature; const type = (this.createObjectType(ObjectFlags.Anonymous)); type.members = this.emptySymbols; type.properties = emptyArray; type.callSignatures = !isConstructor ? [signature] : emptyArray; type.constructSignatures = isConstructor ? [signature] : emptyArray; signature.isolatedSignatureType = type; } return signature.isolatedSignatureType; } public getIndexSymbol(symbol: Symbol): Symbol { return symbol.members.get(InternalSymbolName.Index); } public getIndexDeclarationOfSymbol(symbol: Symbol, kind: IndexKind): SignatureDeclaration { const syntaxKind = kind === IndexKind.Number ? SyntaxKind.NumberKeyword : SyntaxKind.StringKeyword; const indexSymbol = this.getIndexSymbol(symbol); if (indexSymbol) { for (const decl of indexSymbol.declarations) { const node = decl; if (node.parameters.length === 1) { const parameter = node.parameters[0]; if (parameter && parameter.type && parameter.type.kind === syntaxKind) { return node; } } } } return undefined; } public createIndexInfo(type: Type, isReadonly: boolean, declaration?: SignatureDeclaration): IndexInfo { return { type, isReadonly, declaration }; } public getIndexInfoOfSymbol(symbol: Symbol, kind: IndexKind): IndexInfo { const declaration = this.getIndexDeclarationOfSymbol(symbol, kind); if (declaration) { return this.createIndexInfo(declaration.type ? this.getTypeFromTypeNode(declaration.type) : this.anyType, hasModifier(declaration, ModifierFlags.Readonly), declaration); } return undefined; } public getConstraintDeclaration(type: TypeParameter) { return getDeclarationOfKind(type.symbol, SyntaxKind.TypeParameter).constraint; } public getConstraintFromTypeParameter(typeParameter: TypeParameter): Type { if (!typeParameter.constraint) { if (typeParameter.target) { const targetConstraint = this.getConstraintOfTypeParameter(typeParameter.target); typeParameter.constraint = targetConstraint ? this.instantiateType(targetConstraint, typeParameter.mapper) : this.noConstraintType; } else { const constraintDeclaration = this.getConstraintDeclaration(typeParameter); typeParameter.constraint = constraintDeclaration ? this.getTypeFromTypeNode(constraintDeclaration) : this.noConstraintType; } } return typeParameter.constraint === this.noConstraintType ? undefined : typeParameter.constraint; } public getParentSymbolOfTypeParameter(typeParameter: TypeParameter): Symbol { return this.getSymbolOfNode(getDeclarationOfKind(typeParameter.symbol, SyntaxKind.TypeParameter).parent); } public getTypeListId(types: Type[]) { 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; } // 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 the anyFunctionType. 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. public getPropagatingFlagsOfTypes(types: Type[], excludeKinds: TypeFlags): TypeFlags { let result: TypeFlags = 0; for (const type of types) { if (!(type.flags & excludeKinds)) { result |= type.flags; } } return result & TypeFlags.PropagatingFlags; } public createTypeReference(target: GenericType, typeArguments: Type[]): TypeReference { const id = this.getTypeListId(typeArguments); let type = target.instantiations.get(id); if (!type) { type = (this.createObjectType(ObjectFlags.Reference, target.symbol)); target.instantiations.set(id, type); type.flags |= typeArguments ? this.getPropagatingFlagsOfTypes(typeArguments, /*excludeKinds*/ 0) : 0; type.target = target; type.typeArguments = typeArguments; } return type; } public cloneTypeReference(source: TypeReference): TypeReference { const type = (this.createType(source.flags)); type.symbol = source.symbol; type.objectFlags = source.objectFlags; type.target = source.target; type.typeArguments = source.typeArguments; return type; } public getTypeReferenceArity(type: TypeReference): number { return length(type.target.typeParameters); } /** * Get type from type-reference that reference to class or interface */ public getTypeFromClassOrInterfaceReference(node: TypeReferenceType, symbol: Symbol, typeArgs: Type[]): Type { const type = (this.getDeclaredTypeOfSymbol(this.getMergedSymbol(symbol))); const typeParameters = type.localTypeParameters; if (typeParameters) { const numTypeArguments = length(node.typeArguments); const minTypeArgumentCount = this.getMinTypeArgumentCount(typeParameters); if (!isInJavaScriptFile(node) && (numTypeArguments < minTypeArgumentCount || numTypeArguments > typeParameters.length)) { this.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, this.typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType), minTypeArgumentCount, typeParameters.length); return this.unknownType; } // 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, this.fillMissingTypeArguments(typeArgs, typeParameters, minTypeArgumentCount, node)); return this.createTypeReference((type), typeArguments); } if (node.typeArguments) { this.error(node, Diagnostics.Type_0_is_not_generic, this.typeToString(type)); return this.unknownType; } return type; } public getTypeAliasInstantiation(symbol: Symbol, typeArguments: Type[]): Type { const type = this.getDeclaredTypeOfSymbol(symbol); const links = this.getSymbolLinks(symbol); const typeParameters = links.typeParameters; const id = this.getTypeListId(typeArguments); let instantiation = links.instantiations.get(id); if (!instantiation) { links.instantiations.set(id, instantiation = this.instantiateTypeNoAlias(type, this.createTypeMapper(typeParameters, this.fillMissingTypeArguments(typeArguments, typeParameters, this.getMinTypeArgumentCount(typeParameters))))); } 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. */ public getTypeFromTypeAliasReference(node: TypeReferenceType, symbol: Symbol, typeArguments: Type[]): Type { const type = this.getDeclaredTypeOfSymbol(symbol); const typeParameters = this.getSymbolLinks(symbol).typeParameters; if (typeParameters) { const numTypeArguments = length(node.typeArguments); const minTypeArgumentCount = this.getMinTypeArgumentCount(typeParameters); if (numTypeArguments < minTypeArgumentCount || numTypeArguments > typeParameters.length) { this.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, this.symbolToString(symbol), minTypeArgumentCount, typeParameters.length); return this.unknownType; } return this.getTypeAliasInstantiation(symbol, typeArguments); } if (node.typeArguments) { this.error(node, Diagnostics.Type_0_is_not_generic, this.symbolToString(symbol)); return this.unknownType; } return type; } /** * Get type from reference to named type that cannot be generic (enum or type parameter) */ public getTypeFromNonGenericTypeReference(node: TypeReferenceType, symbol: Symbol): Type { if (node.typeArguments) { this.error(node, Diagnostics.Type_0_is_not_generic, this.symbolToString(symbol)); return this.unknownType; } return this.getDeclaredTypeOfSymbol(symbol); } public 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; } } return undefined; } public resolveTypeReferenceName(typeReferenceName: EntityNameExpression | EntityName, meaning: SymbolFlags) { if (!typeReferenceName) { return this.unknownSymbol; } return this.resolveEntityName(typeReferenceName, meaning) || this.unknownSymbol; } public getTypeReferenceType(node: TypeReferenceType, symbol: Symbol) { const typeArguments = this.typeArgumentsFromTypeReferenceNode(node); // Do unconditionally so we mark type arguments as referenced. if (symbol === this.unknownSymbol) { return this.unknownType; } const type = this.getTypeReferenceTypeWorker(node, symbol, typeArguments); if (type) { return type; } if (symbol.flags & SymbolFlags.Value && this.isJSDocTypeReference(node)) { // A jsdoc TypeReference may have resolved to a value (as opposed to a type). If // the symbol is a constructor function, return the inferred class type; otherwise, // the type of this reference is just the type of the value we resolved to. const valueType = this.getTypeOfSymbol(symbol); if (valueType.symbol && !this.isInferredClassType(valueType)) { const referenceType = this.getTypeReferenceTypeWorker(node, valueType.symbol, typeArguments); if (referenceType) { return referenceType; } } // Resolve the type reference as a Type for the purpose of reporting errors. this.resolveTypeReferenceName(this.getTypeReferenceName(node), SymbolFlags.Type); return valueType; } return this.getTypeFromNonGenericTypeReference(node, symbol); } public getTypeReferenceTypeWorker(node: TypeReferenceType, symbol: Symbol, typeArguments: Type[]): Type | undefined { if (symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { return this.getTypeFromClassOrInterfaceReference(node, symbol, typeArguments); } if (symbol.flags & SymbolFlags.TypeAlias) { return this.getTypeFromTypeAliasReference(node, symbol, typeArguments); } if (symbol.flags & SymbolFlags.Function && this.isJSDocTypeReference(node) && (symbol.members || getJSDocClassTag(symbol.valueDeclaration))) { return this.getInferredClassType(symbol); } } public isJSDocTypeReference(node: TypeReferenceType): node is TypeReferenceNode { return node.flags & NodeFlags.JSDoc && node.kind === SyntaxKind.TypeReference; } public getIntendedTypeFromJSDocTypeReference(node: TypeReferenceNode): Type { if (isIdentifier(node.typeName)) { if (node.typeName.escapedText === "Object") { if (node.typeArguments && node.typeArguments.length === 2) { const indexed = this.getTypeFromTypeNode(node.typeArguments[0]); const target = this.getTypeFromTypeNode(node.typeArguments[1]); const index = this.createIndexInfo(target, /*isReadonly*/ false); if (indexed === this.stringType || indexed === this.numberType) { return this.createAnonymousType(undefined, this.emptySymbols, emptyArray, emptyArray, indexed === this.stringType && index, indexed === this.numberType && index); } } return this.anyType; } switch (node.typeName.escapedText) { case "String": return this.stringType; case "Number": return this.numberType; case "Boolean": return this.booleanType; case "Void": return this.voidType; case "Undefined": return this.undefinedType; case "Null": return this.nullType; case "Function": case "function": return this.globalFunctionType; case "Array": case "array": return !node.typeArguments || !node.typeArguments.length ? this.anyArrayType : undefined; case "Promise": case "promise": return !node.typeArguments || !node.typeArguments.length ? this.createPromiseType(this.anyType) : undefined; } } } public getTypeFromJSDocNullableTypeNode(node: JSDocNullableType) { const type = this.getTypeFromTypeNode(node.type); return this.strictNullChecks ? this.getUnionType([type, this.nullType]) : type; } public getTypeFromTypeReference(node: TypeReferenceType): Type { const links = this.getNodeLinks(node); if (!links.resolvedType) { let symbol: Symbol; let type: Type; let meaning = SymbolFlags.Type; if (this.isJSDocTypeReference(node)) { type = this.getIntendedTypeFromJSDocTypeReference(node); meaning |= SymbolFlags.Value; } if (!type) { symbol = this.resolveTypeReferenceName(this.getTypeReferenceName(node), meaning); type = this.getTypeReferenceType(node, symbol); } // Cache both the resolved symbol and the resolved type. The resolved symbol is needed in when we check the // type reference in checkTypeReferenceOrExpressionWithTypeArguments. links.resolvedSymbol = symbol; links.resolvedType = type; } return links.resolvedType; } public typeArgumentsFromTypeReferenceNode(node: TypeReferenceType): Type[] { return map(node.typeArguments, this.getTypeFromTypeNode); } public getTypeFromTypeQueryNode(node: TypeQueryNode): Type { const links = this.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. links.resolvedType = this.getWidenedType(this.checkExpression(node.exprName)); } return links.resolvedType; } public getTypeOfGlobalSymbol(symbol: Symbol, arity: number): ObjectType { function getTypeDeclaration(symbol: Symbol): Declaration { const declarations = symbol.declarations; for (const declaration of declarations) { switch (declaration.kind) { case SyntaxKind.ClassDeclaration: case SyntaxKind.InterfaceDeclaration: case SyntaxKind.EnumDeclaration: return declaration; } } } if (!symbol) { return arity ? this.emptyGenericType : this.emptyObjectType; } const type = this.getDeclaredTypeOfSymbol(symbol); if (!(type.flags & TypeFlags.Object)) { this.error(getTypeDeclaration(symbol), Diagnostics.Global_type_0_must_be_a_class_or_interface_type, unescapeLeadingUnderscores(symbol.escapedName)); return arity ? this.emptyGenericType : this.emptyObjectType; } if (length((type).typeParameters) !== arity) { this.error(getTypeDeclaration(symbol), Diagnostics.Global_type_0_must_have_1_type_parameter_s, unescapeLeadingUnderscores(symbol.escapedName), arity); return arity ? this.emptyGenericType : this.emptyObjectType; } return type; } public getGlobalValueSymbol(name: __String, reportErrors: boolean): Symbol { return this.getGlobalSymbol(name, SymbolFlags.Value, reportErrors ? Diagnostics.Cannot_find_global_value_0 : undefined); } public getGlobalTypeSymbol(name: __String, reportErrors: boolean): Symbol { return this.getGlobalSymbol(name, SymbolFlags.Type, reportErrors ? Diagnostics.Cannot_find_global_type_0 : undefined); } public getGlobalSymbol(name: __String, meaning: SymbolFlags, diagnostic: DiagnosticMessage): Symbol { return this.resolveName(undefined, name, meaning, diagnostic, name); } public getGlobalType(name: __String, arity: 0, reportErrors: boolean): ObjectType; public getGlobalType(name: __String, arity: number, reportErrors: boolean): GenericType; public getGlobalType(name: __String, arity: number, reportErrors: boolean): ObjectType { const symbol = this.getGlobalTypeSymbol(name, reportErrors); return symbol || reportErrors ? this.getTypeOfGlobalSymbol(symbol, arity) : undefined; } public getGlobalTypedPropertyDescriptorType() { return this.deferredGlobalTypedPropertyDescriptorType || (this.deferredGlobalTypedPropertyDescriptorType = this.getGlobalType(("TypedPropertyDescriptor" as __String), /*arity*/ 1, /*reportErrors*/ true)) || this.emptyGenericType; } public getGlobalTemplateStringsArrayType() { return this.deferredGlobalTemplateStringsArrayType || (this.deferredGlobalTemplateStringsArrayType = this.getGlobalType(("TemplateStringsArray" as __String), /*arity*/ 0, /*reportErrors*/ true)) || this.emptyObjectType; } public getGlobalESSymbolConstructorSymbol(reportErrors: boolean) { return this.deferredGlobalESSymbolConstructorSymbol || (this.deferredGlobalESSymbolConstructorSymbol = this.getGlobalValueSymbol(("Symbol" as __String), reportErrors)); } public getGlobalESSymbolType(reportErrors: boolean) { return this.deferredGlobalESSymbolType || (this.deferredGlobalESSymbolType = this.getGlobalType(("Symbol" as __String), /*arity*/ 0, reportErrors)) || this.emptyObjectType; } public getGlobalPromiseType(reportErrors: boolean) { return this.deferredGlobalPromiseType || (this.deferredGlobalPromiseType = this.getGlobalType(("Promise" as __String), /*arity*/ 1, reportErrors)) || this.emptyGenericType; } public getGlobalPromiseConstructorSymbol(reportErrors: boolean): Symbol | undefined { return this.deferredGlobalPromiseConstructorSymbol || (this.deferredGlobalPromiseConstructorSymbol = this.getGlobalValueSymbol(("Promise" as __String), reportErrors)); } public getGlobalPromiseConstructorLikeType(reportErrors: boolean) { return this.deferredGlobalPromiseConstructorLikeType || (this.deferredGlobalPromiseConstructorLikeType = this.getGlobalType(("PromiseConstructorLike" as __String), /*arity*/ 0, reportErrors)) || this.emptyObjectType; } public getGlobalAsyncIterableType(reportErrors: boolean) { return this.deferredGlobalAsyncIterableType || (this.deferredGlobalAsyncIterableType = this.getGlobalType(("AsyncIterable" as __String), /*arity*/ 1, reportErrors)) || this.emptyGenericType; } public getGlobalAsyncIteratorType(reportErrors: boolean) { return this.deferredGlobalAsyncIteratorType || (this.deferredGlobalAsyncIteratorType = this.getGlobalType(("AsyncIterator" as __String), /*arity*/ 1, reportErrors)) || this.emptyGenericType; } public getGlobalAsyncIterableIteratorType(reportErrors: boolean) { return this.deferredGlobalAsyncIterableIteratorType || (this.deferredGlobalAsyncIterableIteratorType = this.getGlobalType(("AsyncIterableIterator" as __String), /*arity*/ 1, reportErrors)) || this.emptyGenericType; } public getGlobalIterableType(reportErrors: boolean) { return this.deferredGlobalIterableType || (this.deferredGlobalIterableType = this.getGlobalType(("Iterable" as __String), /*arity*/ 1, reportErrors)) || this.emptyGenericType; } public getGlobalIteratorType(reportErrors: boolean) { return this.deferredGlobalIteratorType || (this.deferredGlobalIteratorType = this.getGlobalType(("Iterator" as __String), /*arity*/ 1, reportErrors)) || this.emptyGenericType; } public getGlobalIterableIteratorType(reportErrors: boolean) { return this.deferredGlobalIterableIteratorType || (this.deferredGlobalIterableIteratorType = this.getGlobalType(("IterableIterator" as __String), /*arity*/ 1, reportErrors)) || this.emptyGenericType; } public getGlobalTypeOrUndefined(name: __String, arity = 0): ObjectType { const symbol = this.getGlobalSymbol(name, SymbolFlags.Type, /*diagnostic*/ undefined); return symbol && (this.getTypeOfGlobalSymbol(symbol, arity)); } /** * Returns a type that is inside a namespace at the global scope, e.g. * getExportedTypeFromNamespace('JSX', 'Element') returns the JSX.Element type */ public getExportedTypeFromNamespace(namespace: __String, name: __String): Type { const namespaceSymbol = this.getGlobalSymbol(namespace, SymbolFlags.Namespace, /*diagnosticMessage*/ undefined); const typeSymbol = namespaceSymbol && this.getSymbol(namespaceSymbol.exports, name, SymbolFlags.Type); return typeSymbol && this.getDeclaredTypeOfSymbol(typeSymbol); } /** * Instantiates a global type that is generic with some element type, and returns that instantiation. */ public createTypeFromGenericGlobalType(genericGlobalType: GenericType, typeArguments: Type[]): ObjectType { return genericGlobalType !== this.emptyGenericType ? this.createTypeReference(genericGlobalType, typeArguments) : this.emptyObjectType; } public createTypedPropertyDescriptorType(propertyType: Type): Type { return this.createTypeFromGenericGlobalType(this.getGlobalTypedPropertyDescriptorType(), [propertyType]); } public createAsyncIterableType(iteratedType: Type): Type { return this.createTypeFromGenericGlobalType(this.getGlobalAsyncIterableType(/*reportErrors*/ true), [iteratedType]); } public createAsyncIterableIteratorType(iteratedType: Type): Type { return this.createTypeFromGenericGlobalType(this.getGlobalAsyncIterableIteratorType(/*reportErrors*/ true), [iteratedType]); } public createIterableType(iteratedType: Type): Type { return this.createTypeFromGenericGlobalType(this.getGlobalIterableType(/*reportErrors*/ true), [iteratedType]); } public createIterableIteratorType(iteratedType: Type): Type { return this.createTypeFromGenericGlobalType(this.getGlobalIterableIteratorType(/*reportErrors*/ true), [iteratedType]); } public createArrayType(elementType: Type): ObjectType { return this.createTypeFromGenericGlobalType(this.globalArrayType, [elementType]); } public getTypeFromArrayTypeNode(node: ArrayTypeNode): Type { const links = this.getNodeLinks(node); if (!links.resolvedType) { links.resolvedType = this.createArrayType(this.getTypeFromTypeNode(node.elementType)); } return links.resolvedType; } // 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. public createTupleTypeOfArity(arity: number): GenericType { const typeParameters: TypeParameter[] = []; const properties: Symbol[] = []; for (let i = 0; i < arity; i++) { const typeParameter = (this.createType(TypeFlags.TypeParameter)); typeParameters.push(typeParameter); const property = this.createSymbol(SymbolFlags.Property, ("" + i as __String)); property.type = typeParameter; properties.push(property); } const type = (this.createObjectType(ObjectFlags.Tuple | ObjectFlags.Reference)); type.typeParameters = typeParameters; type.outerTypeParameters = undefined; type.localTypeParameters = typeParameters; type.instantiations = createMap(); type.instantiations.set(this.getTypeListId(type.typeParameters), (type)); type.target = type; type.typeArguments = type.typeParameters; type.thisType = (this.createType(TypeFlags.TypeParameter)); type.thisType.isThisType = true; type.thisType.constraint = type; type.declaredProperties = properties; type.declaredCallSignatures = emptyArray; type.declaredConstructSignatures = emptyArray; type.declaredStringIndexInfo = undefined; type.declaredNumberIndexInfo = undefined; return type; } public getTupleTypeOfArity(arity: number): GenericType { return this.tupleTypes[arity] || (this.tupleTypes[arity] = this.createTupleTypeOfArity(arity)); } public createTupleType(elementTypes: Type[]) { return this.createTypeReference(this.getTupleTypeOfArity(elementTypes.length), elementTypes); } public getTypeFromTupleTypeNode(node: TupleTypeNode): Type { const links = this.getNodeLinks(node); if (!links.resolvedType) { links.resolvedType = this.createTupleType(map(node.elementTypes, this.getTypeFromTypeNode)); } return links.resolvedType; } public binarySearchTypes(types: Type[], type: Type): number { let low = 0; let high = types.length - 1; const typeId = type.id; while (low <= high) { const middle = low + ((high - low) >> 1); const id = types[middle].id; if (id === typeId) { return middle; } else if (id > typeId) { high = middle - 1; } else { low = middle + 1; } } return ~low; } public containsType(types: Type[], type: Type): boolean { return this.binarySearchTypes(types, type) >= 0; } public addTypeToUnion(typeSet: TypeSet, type: Type) { const flags = type.flags; if (flags & TypeFlags.Union) { this.addTypesToUnion(typeSet, (type).types); } else if (flags & TypeFlags.Any) { typeSet.containsAny = true; } else if (!this.strictNullChecks && flags & TypeFlags.Nullable) { if (flags & TypeFlags.Undefined) typeSet.containsUndefined = true; if (flags & TypeFlags.Null) typeSet.containsNull = true; if (!(flags & TypeFlags.ContainsWideningType)) typeSet.containsNonWideningType = true; } else if (!(flags & TypeFlags.Never)) { if (flags & TypeFlags.String) typeSet.containsString = true; if (flags & TypeFlags.Number) typeSet.containsNumber = true; if (flags & TypeFlags.StringOrNumberLiteral) typeSet.containsStringOrNumberLiteral = true; const len = typeSet.length; const index = len && type.id > typeSet[len - 1].id ? ~len : this.binarySearchTypes(typeSet, type); if (index < 0) { if (!(flags & TypeFlags.Object && (type).objectFlags & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method) && this.containsIdenticalType(typeSet, type))) { typeSet.splice(~index, 0, type); } } } } // 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. public addTypesToUnion(typeSet: TypeSet, types: Type[]) { for (const type of types) { this.addTypeToUnion(typeSet, type); } } public containsIdenticalType(types: Type[], type: Type) { for (const t of types) { if (this.isTypeIdenticalTo(t, type)) { return true; } } return false; } public isSubtypeOfAny(candidate: Type, types: Type[]): boolean { for (const type of types) { if (candidate !== type && this.isTypeSubtypeOf(candidate, type)) { return true; } } return false; } public isSetOfLiteralsFromSameEnum(types: TypeSet): boolean { const first = types[0]; if (first.flags & TypeFlags.EnumLiteral) { const firstEnum = this.getParentOfSymbol(first.symbol); for (let i = 1; i < types.length; i++) { const other = types[i]; if (!(other.flags & TypeFlags.EnumLiteral) || (firstEnum !== this.getParentOfSymbol(other.symbol))) { return false; } } return true; } return false; } public removeSubtypes(types: TypeSet) { if (types.length === 0 || this.isSetOfLiteralsFromSameEnum(types)) { return; } let i = types.length; while (i > 0) { i--; if (this.isSubtypeOfAny(types[i], types)) { orderedRemoveItemAt(types, i); } } } public removeRedundantLiteralTypes(types: TypeSet) { let i = types.length; while (i > 0) { i--; const t = types[i]; const remove = t.flags & TypeFlags.StringLiteral && types.containsString || t.flags & TypeFlags.NumberLiteral && types.containsNumber || t.flags & TypeFlags.StringOrNumberLiteral && t.flags & TypeFlags.FreshLiteral && this.containsType(types, (t).regularType); if (remove) { orderedRemoveItemAt(types, i); } } } // 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. public getUnionType(types: Type[], subtypeReduction?: boolean, aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type { if (types.length === 0) { return this.neverType; } if (types.length === 1) { return types[0]; } const typeSet = [] as TypeSet; this.addTypesToUnion(typeSet, types); if (typeSet.containsAny) { return this.anyType; } if (subtypeReduction) { this.removeSubtypes(typeSet); } else if (typeSet.containsStringOrNumberLiteral) { this.removeRedundantLiteralTypes(typeSet); } if (typeSet.length === 0) { return typeSet.containsNull ? typeSet.containsNonWideningType ? this.nullType : this.nullWideningType : typeSet.containsUndefined ? typeSet.containsNonWideningType ? this.undefinedType : this.undefinedWideningType : this.neverType; } return this.getUnionTypeFromSortedList(typeSet, aliasSymbol, aliasTypeArguments); } // This function assumes the constituent type list is sorted and deduplicated. public getUnionTypeFromSortedList(types: Type[], aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type { if (types.length === 0) { return this.neverType; } if (types.length === 1) { return types[0]; } const id = this.getTypeListId(types); let type = this.unionTypes.get(id); if (!type) { const propagatedFlags = this.getPropagatingFlagsOfTypes(types, /*excludeKinds*/ TypeFlags.Nullable); type = (this.createType(TypeFlags.Union | propagatedFlags)); this.unionTypes.set(id, type); type.types = types; type.aliasSymbol = aliasSymbol; type.aliasTypeArguments = aliasTypeArguments; } return type; } public getTypeFromUnionTypeNode(node: UnionTypeNode): Type { const links = this.getNodeLinks(node); if (!links.resolvedType) { links.resolvedType = this.getUnionType(map(node.types, this.getTypeFromTypeNode), /*subtypeReduction*/ false, this.getAliasSymbolForTypeNode(node), this.getAliasTypeArgumentsForTypeNode(node)); } return links.resolvedType; } public addTypeToIntersection(typeSet: TypeSet, type: Type) { if (type.flags & TypeFlags.Intersection) { this.addTypesToIntersection(typeSet, (type).types); } else if (type.flags & TypeFlags.Any) { typeSet.containsAny = true; } else if (type.flags & TypeFlags.Never) { typeSet.containsNever = true; } else if (this.getObjectFlags(type) & ObjectFlags.Anonymous && this.isEmptyObjectType(type)) { typeSet.containsEmptyObject = true; } else if ((this.strictNullChecks || !(type.flags & TypeFlags.Nullable)) && !contains(typeSet, type)) { if (type.flags & TypeFlags.Object) { typeSet.containsObjectType = true; } if (type.flags & TypeFlags.Union && typeSet.unionIndex === undefined) { typeSet.unionIndex = typeSet.length; } if (!(type.flags & TypeFlags.Object && (type).objectFlags & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method) && this.containsIdenticalType(typeSet, type))) { typeSet.push(type); } } } // 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. public addTypesToIntersection(typeSet: TypeSet, types: Type[]) { for (const type of types) { this.addTypeToIntersection(typeSet, type); } } // 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. public getIntersectionType(types: Type[], aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type { const __conv_self__ = this; if (types.length === 0) { return this.emptyObjectType; } const typeSet = [] as TypeSet; this.addTypesToIntersection(typeSet, types); if (typeSet.containsNever) { return this.neverType; } if (typeSet.containsAny) { return this.anyType; } if (typeSet.containsEmptyObject && !typeSet.containsObjectType) { typeSet.push(this.emptyObjectType); } if (typeSet.length === 1) { return typeSet[0]; } const unionIndex = typeSet.unionIndex; if (unionIndex !== undefined) { // We are attempting to construct a type of the form X & (A | B) & Y. Transform this into a type of // the form X & A & Y | X & B & Y and recursively reduce until no union type constituents remain. const unionType = typeSet[unionIndex]; return this.getUnionType(map(unionType.types, t => __conv_self__.getIntersectionType(replaceElement(typeSet, unionIndex, t))), /*subtypeReduction*/ false, aliasSymbol, aliasTypeArguments); } const id = this.getTypeListId(typeSet); let type = this.intersectionTypes.get(id); if (!type) { const propagatedFlags = this.getPropagatingFlagsOfTypes(typeSet, /*excludeKinds*/ TypeFlags.Nullable); type = (this.createType(TypeFlags.Intersection | propagatedFlags)); this.intersectionTypes.set(id, type); type.types = typeSet; type.aliasSymbol = aliasSymbol; type.aliasTypeArguments = aliasTypeArguments; } return type; } public getTypeFromIntersectionTypeNode(node: IntersectionTypeNode): Type { const links = this.getNodeLinks(node); if (!links.resolvedType) { links.resolvedType = this.getIntersectionType(map(node.types, this.getTypeFromTypeNode), this.getAliasSymbolForTypeNode(node), this.getAliasTypeArgumentsForTypeNode(node)); } return links.resolvedType; } public getIndexTypeForGenericType(type: TypeVariable | UnionOrIntersectionType) { if (!type.resolvedIndexType) { type.resolvedIndexType = (this.createType(TypeFlags.Index)); type.resolvedIndexType.type = type; } return type.resolvedIndexType; } public getLiteralTypeFromPropertyName(prop: Symbol) { return getDeclarationModifierFlagsFromSymbol(prop) & ModifierFlags.NonPublicAccessibilityModifier || startsWith(prop.escapedName as string, "__@") ? this.neverType : this.getLiteralType(unescapeLeadingUnderscores(prop.escapedName)); } public getLiteralTypeFromPropertyNames(type: Type) { return this.getUnionType(map(this.getPropertiesOfType(type), this.getLiteralTypeFromPropertyName)); } public getIndexType(type: Type): Type { return this.maybeTypeOfKind(type, TypeFlags.TypeVariable) ? this.getIndexTypeForGenericType((type)) : this.getObjectFlags(type) & ObjectFlags.Mapped ? this.getConstraintTypeFromMappedType((type)) : type.flags & TypeFlags.Any || this.getIndexInfoOfType(type, IndexKind.String) ? this.stringType : this.getLiteralTypeFromPropertyNames(type); } public getIndexTypeOrString(type: Type): Type { const indexType = this.getIndexType(type); return indexType !== this.neverType ? indexType : this.stringType; } public getTypeFromTypeOperatorNode(node: TypeOperatorNode) { const links = this.getNodeLinks(node); if (!links.resolvedType) { links.resolvedType = this.getIndexType(this.getTypeFromTypeNode(node.type)); } return links.resolvedType; } public createIndexedAccessType(objectType: Type, indexType: Type) { const type = (this.createType(TypeFlags.IndexedAccess)); type.objectType = objectType; type.indexType = indexType; return type; } public getPropertyTypeForIndexType(objectType: Type, indexType: Type, accessNode: ElementAccessExpression | IndexedAccessTypeNode, cacheSymbol: boolean) { const accessExpression = accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression ? accessNode : undefined; const propName = indexType.flags & TypeFlags.StringOrNumberLiteral ? escapeLeadingUnderscores("" + (indexType).value) : accessExpression && this.checkThatExpressionIsProperSymbolReference(accessExpression.argumentExpression, indexType, /*reportError*/ false) ? getPropertyNameForKnownSymbolName(unescapeLeadingUnderscores(((accessExpression.argumentExpression).name).escapedText)) : undefined; if (propName !== undefined) { const prop = this.getPropertyOfType(objectType, propName); if (prop) { if (accessExpression) { if (isAssignmentTarget(accessExpression) && (this.isReferenceToReadonlyEntity(accessExpression, prop) || this.isReferenceThroughNamespaceImport(accessExpression))) { this.error(accessExpression.argumentExpression, Diagnostics.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property, this.symbolToString(prop)); return this.unknownType; } if (cacheSymbol) { this.getNodeLinks(accessNode).resolvedSymbol = prop; } } return this.getTypeOfSymbol(prop); } } if (!(indexType.flags & TypeFlags.Nullable) && this.isTypeAssignableToKind(indexType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbol)) { if (this.isTypeAny(objectType)) { return this.anyType; } const indexInfo = this.isTypeAssignableToKind(indexType, TypeFlags.NumberLike) && this.getIndexInfoOfType(objectType, IndexKind.Number) || this.getIndexInfoOfType(objectType, IndexKind.String) || undefined; if (indexInfo) { if (accessExpression && indexInfo.isReadonly && (isAssignmentTarget(accessExpression) || isDeleteTarget(accessExpression))) { this.error(accessExpression, Diagnostics.Index_signature_in_type_0_only_permits_reading, this.typeToString(objectType)); return this.unknownType; } return indexInfo.type; } if (accessExpression && !this.isConstEnumObjectType(objectType)) { if (this.noImplicitAny && !this.compilerOptions.suppressImplicitAnyIndexErrors) { if (this.getIndexTypeOfType(objectType, IndexKind.Number)) { this.error(accessExpression.argumentExpression, Diagnostics.Element_implicitly_has_an_any_type_because_index_expression_is_not_of_type_number); } else { this.error(accessExpression, Diagnostics.Element_implicitly_has_an_any_type_because_type_0_has_no_index_signature, this.typeToString(objectType)); } } return this.anyType; } } if (accessNode) { const indexNode = accessNode.kind === SyntaxKind.ElementAccessExpression ? (accessNode).argumentExpression : (accessNode).indexType; if (indexType.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral)) { this.error(indexNode, Diagnostics.Property_0_does_not_exist_on_type_1, "" + (indexType).value, this.typeToString(objectType)); } else if (indexType.flags & (TypeFlags.String | TypeFlags.Number)) { this.error(indexNode, Diagnostics.Type_0_has_no_matching_index_signature_for_type_1, this.typeToString(objectType), this.typeToString(indexType)); } else { this.error(indexNode, Diagnostics.Type_0_cannot_be_used_as_an_index_type, this.typeToString(indexType)); } return this.unknownType; } return this.anyType; } public getIndexedAccessForMappedType(type: MappedType, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode) { if (accessNode) { // Check if the index type is assignable to 'keyof T' for the object type. if (!this.isTypeAssignableTo(indexType, this.getIndexType(type))) { this.error(accessNode, Diagnostics.Type_0_cannot_be_used_to_index_type_1, this.typeToString(indexType), this.typeToString(type)); return this.unknownType; } if (accessNode.kind === SyntaxKind.ElementAccessExpression && isAssignmentTarget(accessNode) && type.declaration.readonlyToken) { this.error(accessNode, Diagnostics.Index_signature_in_type_0_only_permits_reading, this.typeToString(type)); return this.unknownType; } } const mapper = this.createTypeMapper([this.getTypeParameterFromMappedType(type)], [indexType]); const templateMapper = type.mapper ? this.combineTypeMappers(type.mapper, mapper) : mapper; return this.instantiateType(this.getTemplateTypeFromMappedType(type), templateMapper); } public isGenericObjectType(type: Type): boolean { return type.flags & TypeFlags.TypeVariable ? true : this.getObjectFlags(type) & ObjectFlags.Mapped ? this.isGenericIndexType(this.getConstraintTypeFromMappedType((type))) : type.flags & TypeFlags.UnionOrIntersection ? forEach((type).types, this.isGenericObjectType) : false; } public isGenericIndexType(type: Type): boolean { return type.flags & (TypeFlags.TypeVariable | TypeFlags.Index) ? true : type.flags & TypeFlags.UnionOrIntersection ? forEach((type).types, this.isGenericIndexType) : false; } // Return true if the given type is a non-generic object type with a string index signature and no // other members. public isStringIndexOnlyType(type: Type) { if (type.flags & TypeFlags.Object && !this.isGenericMappedType(type)) { const t = this.resolveStructuredTypeMembers((type)); return t.properties.length === 0 && t.callSignatures.length === 0 && t.constructSignatures.length === 0 && t.stringIndexInfo && !t.numberIndexInfo; } return false; } // Given an indexed access type T[K], if T is an intersection containing one or more generic types and one or // more object types with only a string index signature, e.g. '(U & V & { [x: string]: D })[K]', return a // transformed type of the form '(U & V)[K] | D'. This allows us to properly reason about higher order indexed // access types with default property values as expressed by D. public getTransformedIndexedAccessType(type: IndexedAccessType): Type { const objectType = type.objectType; if (objectType.flags & TypeFlags.Intersection && this.isGenericObjectType(objectType) && some((objectType).types, this.isStringIndexOnlyType)) { const regularTypes: Type[] = []; const stringIndexTypes: Type[] = []; for (const t of (objectType).types) { if (this.isStringIndexOnlyType(t)) { stringIndexTypes.push(this.getIndexTypeOfType(t, IndexKind.String)); } else { regularTypes.push(t); } } return this.getUnionType([ this.getIndexedAccessType(this.getIntersectionType(regularTypes), type.indexType), this.getIntersectionType(stringIndexTypes) ]); } return undefined; } public getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode): Type { // If the object type is a mapped type { [P in K]: E }, where K is generic, we instantiate E using a mapper // that substitutes the index type for P. For example, for an index access { [P in K]: Box }[X], we // construct the type Box. if (this.isGenericMappedType(objectType)) { return this.getIndexedAccessForMappedType((objectType), indexType, accessNode); } // Otherwise, if the index type is generic, or if the object type is generic and doesn't originate in an // expression, we are performing a higher-order index access where we cannot meaningfully access the properties // of the object type. Note that for a generic T and a non-generic K, we eagerly resolve T[K] if it originates // in an expression. This is to preserve backwards compatibility. For example, an element access 'this["foo"]' // has always been resolved eagerly using the constraint type of 'this' at the given location. if (this.isGenericIndexType(indexType) || !(accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression) && this.isGenericObjectType(objectType)) { if (objectType.flags & TypeFlags.Any) { return objectType; } // Defer the operation by creating an indexed access type. const id = objectType.id + "," + indexType.id; let type = this.indexedAccessTypes.get(id); if (!type) { this.indexedAccessTypes.set(id, type = this.createIndexedAccessType(objectType, indexType)); } return type; } // In the following we resolve T[K] to the type of the property in T selected by K. const apparentObjectType = this.getApparentType(objectType); if (indexType.flags & TypeFlags.Union && !(indexType.flags & TypeFlags.Primitive)) { const propTypes: Type[] = []; for (const t of (indexType).types) { const propType = this.getPropertyTypeForIndexType(apparentObjectType, t, accessNode, /*cacheSymbol*/ false); if (propType === this.unknownType) { return this.unknownType; } propTypes.push(propType); } return this.getUnionType(propTypes); } return this.getPropertyTypeForIndexType(apparentObjectType, indexType, accessNode, /*cacheSymbol*/ true); } public getTypeFromIndexedAccessTypeNode(node: IndexedAccessTypeNode) { const links = this.getNodeLinks(node); if (!links.resolvedType) { links.resolvedType = this.getIndexedAccessType(this.getTypeFromTypeNode(node.objectType), this.getTypeFromTypeNode(node.indexType), node); } return links.resolvedType; } public getTypeFromMappedTypeNode(node: MappedTypeNode): Type { const links = this.getNodeLinks(node); if (!links.resolvedType) { const type = (this.createObjectType(ObjectFlags.Mapped, node.symbol)); type.declaration = node; type.aliasSymbol = this.getAliasSymbolForTypeNode(node); type.aliasTypeArguments = this.getAliasTypeArgumentsForTypeNode(node); links.resolvedType = type; // Eagerly resolve the constraint type which forces an error if the constraint type circularly // references itself through one or more type aliases. this.getConstraintTypeFromMappedType(type); } return links.resolvedType; } public getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node: TypeNode): Type { const links = this.getNodeLinks(node); if (!links.resolvedType) { // Deferred resolution of members is handled by resolveObjectTypeMembers const aliasSymbol = this.getAliasSymbolForTypeNode(node); if (node.symbol.members.size === 0 && !aliasSymbol) { links.resolvedType = this.emptyTypeLiteralType; } else { let type = this.createObjectType(ObjectFlags.Anonymous, node.symbol); type.aliasSymbol = aliasSymbol; type.aliasTypeArguments = this.getAliasTypeArgumentsForTypeNode(node); if (isJSDocTypeLiteral(node) && node.isArrayType) { type = this.createArrayType(type); } links.resolvedType = type; } } return links.resolvedType; } public getAliasSymbolForTypeNode(node: TypeNode) { return node.parent.kind === SyntaxKind.TypeAliasDeclaration ? this.getSymbolOfNode(node.parent) : undefined; } public getAliasTypeArgumentsForTypeNode(node: TypeNode) { const symbol = this.getAliasSymbolForTypeNode(node); return symbol ? this.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol) : undefined; } /** * Since the source of spread types are object literals, which are not binary, * this function should be called in a left folding style, with left = previous result of getSpreadType * and right = the new element to be spread. */ public getSpreadType(left: Type, right: Type): Type { const __conv_self__ = this; if (left.flags & TypeFlags.Any || right.flags & TypeFlags.Any) { return this.anyType; } if (left.flags & TypeFlags.Never) { return right; } if (right.flags & TypeFlags.Never) { return left; } if (left.flags & TypeFlags.Union) { return this.mapType(left, t => __conv_self__.getSpreadType(t, right)); } if (right.flags & TypeFlags.Union) { return this.mapType(right, t => __conv_self__.getSpreadType(left, t)); } if (right.flags & TypeFlags.NonPrimitive) { return this.emptyObjectType; } const members = createSymbolTable(); const skippedPrivateMembers = createUnderscoreEscapedMap(); let stringIndexInfo: IndexInfo; let numberIndexInfo: IndexInfo; if (left === this.emptyObjectType) { // for the first spread element, left === emptyObjectType, so take the right's string indexer stringIndexInfo = this.getIndexInfoOfType(right, IndexKind.String); numberIndexInfo = this.getIndexInfoOfType(right, IndexKind.Number); } else { stringIndexInfo = this.unionSpreadIndexInfos(this.getIndexInfoOfType(left, IndexKind.String), this.getIndexInfoOfType(right, IndexKind.String)); numberIndexInfo = this.unionSpreadIndexInfos(this.getIndexInfoOfType(left, IndexKind.Number), this.getIndexInfoOfType(right, IndexKind.Number)); } for (const rightProp of this.getPropertiesOfType(right)) { // we approximate own properties as non-methods plus methods that are inside the object literal const isSetterWithoutGetter = rightProp.flags & SymbolFlags.SetAccessor && !(rightProp.flags & SymbolFlags.GetAccessor); if (getDeclarationModifierFlagsFromSymbol(rightProp) & (ModifierFlags.Private | ModifierFlags.Protected)) { skippedPrivateMembers.set(rightProp.escapedName, true); } else if (!this.isClassMethod(rightProp) && !isSetterWithoutGetter) { members.set(rightProp.escapedName, this.getNonReadonlySymbol(rightProp)); } } for (const leftProp of this.getPropertiesOfType(left)) { if (leftProp.flags & SymbolFlags.SetAccessor && !(leftProp.flags & SymbolFlags.GetAccessor) || skippedPrivateMembers.has(leftProp.escapedName) || this.isClassMethod(leftProp)) { continue; } if (members.has(leftProp.escapedName)) { const rightProp = members.get(leftProp.escapedName); const rightType = this.getTypeOfSymbol(rightProp); if (rightProp.flags & SymbolFlags.Optional) { const declarations: Declaration[] = concatenate(leftProp.declarations, rightProp.declarations); const flags = SymbolFlags.Property | (leftProp.flags & SymbolFlags.Optional); const result = this.createSymbol(flags, leftProp.escapedName); result.type = this.getUnionType([this.getTypeOfSymbol(leftProp), this.getTypeWithFacts(rightType, TypeFacts.NEUndefined)]); result.leftSpread = leftProp; result.rightSpread = rightProp; result.declarations = declarations; members.set(leftProp.escapedName, result); } } else { members.set(leftProp.escapedName, this.getNonReadonlySymbol(leftProp)); } } return this.createAnonymousType(undefined, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo); } public getNonReadonlySymbol(prop: Symbol) { if (!this.isReadonlySymbol(prop)) { return prop; } const flags = SymbolFlags.Property | (prop.flags & SymbolFlags.Optional); const result = this.createSymbol(flags, prop.escapedName); result.type = this.getTypeOfSymbol(prop); result.declarations = prop.declarations; result.syntheticOrigin = prop; return result; } public isClassMethod(prop: Symbol) { return prop.flags & SymbolFlags.Method && find(prop.declarations, decl => isClassLike(decl.parent)); } public createLiteralType(flags: TypeFlags, value: string | number, symbol: Symbol) { const type = (this.createType(flags)); type.symbol = symbol; type.value = value; return type; } public getFreshTypeOfLiteralType(type: Type) { if (type.flags & TypeFlags.StringOrNumberLiteral && !(type.flags & TypeFlags.FreshLiteral)) { if (!(type).freshType) { const freshType = (this.createLiteralType(type.flags | TypeFlags.FreshLiteral, (type).value, (type).symbol)); freshType.regularType = type; (type).freshType = freshType; } return (type).freshType; } return type; } public getRegularTypeOfLiteralType(type: Type) { return type.flags & TypeFlags.StringOrNumberLiteral && type.flags & TypeFlags.FreshLiteral ? (type).regularType : type; } public getLiteralType(value: string | number, enumId?: number, symbol?: Symbol) { // We store all literal types in a single map with keys of the form '#NNN' and '@SSS', // where NNN is the text representation of a numeric literal and SSS are the characters // of a string literal. For literal enum members we use 'EEE#NNN' and 'EEE@SSS', where // EEE is a unique id for the containing enum type. const qualifier = typeof value === "number" ? "#" : "@"; const key = enumId ? enumId + qualifier + value : qualifier + value; let type = this.literalTypes.get(key); if (!type) { const flags = (typeof value === "number" ? TypeFlags.NumberLiteral : TypeFlags.StringLiteral) | (enumId ? TypeFlags.EnumLiteral : 0); this.literalTypes.set(key, type = this.createLiteralType(flags, value, symbol)); } return type; } public getTypeFromLiteralTypeNode(node: LiteralTypeNode): Type { const links = this.getNodeLinks(node); if (!links.resolvedType) { links.resolvedType = this.getRegularTypeOfLiteralType(this.checkExpression(node.literal)); } return links.resolvedType; } public getTypeFromJSDocVariadicType(node: JSDocVariadicType): Type { const links = this.getNodeLinks(node); if (!links.resolvedType) { const type = this.getTypeFromTypeNode(node.type); links.resolvedType = type ? this.createArrayType(type) : this.unknownType; } return links.resolvedType; } public getThisType(node: Node): Type { const container = getThisContainer(node, /*includeArrowFunctions*/ false); const parent = container && container.parent; if (parent && (isClassLike(parent) || parent.kind === SyntaxKind.InterfaceDeclaration)) { if (!hasModifier(container, ModifierFlags.Static) && (container.kind !== SyntaxKind.Constructor || isNodeDescendantOf(node, (container).body))) { return this.getDeclaredTypeOfClassOrInterface(this.getSymbolOfNode(parent)).thisType; } } this.error(node, Diagnostics.A_this_type_is_available_only_in_a_non_static_member_of_a_class_or_interface); return this.unknownType; } public getTypeFromThisTypeNode(node: TypeNode): Type { const links = this.getNodeLinks(node); if (!links.resolvedType) { links.resolvedType = this.getThisType(node); } return links.resolvedType; } public getTypeFromTypeNode(node: TypeNode): Type { switch (node.kind) { case SyntaxKind.AnyKeyword: case SyntaxKind.JSDocAllType: case SyntaxKind.JSDocUnknownType: return this.anyType; case SyntaxKind.StringKeyword: return this.stringType; case SyntaxKind.NumberKeyword: return this.numberType; case SyntaxKind.BooleanKeyword: return this.booleanType; case SyntaxKind.SymbolKeyword: return this.esSymbolType; case SyntaxKind.VoidKeyword: return this.voidType; case SyntaxKind.UndefinedKeyword: return this.undefinedType; case SyntaxKind.NullKeyword: return this.nullType; case SyntaxKind.NeverKeyword: return this.neverType; case SyntaxKind.ObjectKeyword: return node.flags & NodeFlags.JavaScriptFile ? this.anyType : this.nonPrimitiveType; case SyntaxKind.ThisType: case SyntaxKind.ThisKeyword: return this.getTypeFromThisTypeNode(node); case SyntaxKind.LiteralType: return this.getTypeFromLiteralTypeNode((node)); case SyntaxKind.TypeReference: return this.getTypeFromTypeReference((node)); case SyntaxKind.TypePredicate: return this.booleanType; case SyntaxKind.ExpressionWithTypeArguments: return this.getTypeFromTypeReference((node)); case SyntaxKind.TypeQuery: return this.getTypeFromTypeQueryNode((node)); case SyntaxKind.ArrayType: return this.getTypeFromArrayTypeNode((node)); case SyntaxKind.TupleType: return this.getTypeFromTupleTypeNode((node)); case SyntaxKind.UnionType: return this.getTypeFromUnionTypeNode((node)); case SyntaxKind.IntersectionType: return this.getTypeFromIntersectionTypeNode((node)); case SyntaxKind.JSDocNullableType: return this.getTypeFromJSDocNullableTypeNode((node)); case SyntaxKind.ParenthesizedType: case SyntaxKind.JSDocNonNullableType: case SyntaxKind.JSDocOptionalType: case SyntaxKind.JSDocTypeExpression: return this.getTypeFromTypeNode((node).type); case SyntaxKind.FunctionType: case SyntaxKind.ConstructorType: case SyntaxKind.TypeLiteral: case SyntaxKind.JSDocTypeLiteral: case SyntaxKind.JSDocFunctionType: return this.getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node); case SyntaxKind.TypeOperator: return this.getTypeFromTypeOperatorNode((node)); case SyntaxKind.IndexedAccessType: return this.getTypeFromIndexedAccessTypeNode((node)); case SyntaxKind.MappedType: return this.getTypeFromMappedTypeNode((node)); // This function assumes that an identifier or qualified name is a type expression // Callers should first ensure this by calling isTypeNode case SyntaxKind.Identifier: case SyntaxKind.QualifiedName: const symbol = this.getSymbolAtLocation(node); return symbol && this.getDeclaredTypeOfSymbol(symbol); case SyntaxKind.JSDocVariadicType: return this.getTypeFromJSDocVariadicType((node)); default: return this.unknownType; } } public instantiateList(items: T[], mapper: TypeMapper, instantiator: (item: T, mapper: TypeMapper) => T): T[] { if (items && items.length) { const result: T[] = []; for (const v of items) { result.push(instantiator(v, mapper)); } return result; } return items; } public instantiateTypes(types: Type[], mapper: TypeMapper) { return this.instantiateList(types, mapper, this.instantiateType); } public instantiateSignatures(signatures: Signature[], mapper: TypeMapper) { return this.instantiateList(signatures, mapper, this.instantiateSignature); } public instantiateCached(type: T, mapper: TypeMapper, instantiator: (item: T, mapper: TypeMapper) => T): T { const instantiations = mapper.instantiations || (mapper.instantiations = []); return instantiations[type.id] || (instantiations[type.id] = instantiator(type, mapper)); } public makeUnaryTypeMapper(source: Type, target: Type) { return (t: Type) => t === source ? target : t; } public makeBinaryTypeMapper(source1: Type, target1: Type, source2: Type, target2: Type) { return (t: Type) => t === source1 ? target1 : t === source2 ? target2 : t; } public makeArrayTypeMapper(sources: Type[], targets: Type[]) { const __conv_self__ = this; return (t: Type) => { for (let i = 0; i < sources.length; i++) { if (t === sources[i]) { return targets ? targets[i] : __conv_self__.anyType; } } return t; }; } public createTypeMapper(sources: TypeParameter[], targets: Type[]): TypeMapper { Debug.assert(targets === undefined || sources.length === targets.length); const mapper: TypeMapper = sources.length === 1 ? this.makeUnaryTypeMapper(sources[0], targets ? targets[0] : this.anyType) : sources.length === 2 ? this.makeBinaryTypeMapper(sources[0], targets ? targets[0] : this.anyType, sources[1], targets ? targets[1] : this.anyType) : this.makeArrayTypeMapper(sources, targets); mapper.mappedTypes = sources; return mapper; } public createTypeEraser(sources: TypeParameter[]): TypeMapper { return this.createTypeMapper(sources, /*targets*/ undefined); } /** * Maps forward-references to later types parameters to the empty object type. * This is used during inference when instantiating type parameter defaults. */ public createBackreferenceMapper(typeParameters: TypeParameter[], index: number) { const __conv_self__ = this; const mapper: TypeMapper = t => indexOf(typeParameters, t) >= index ? __conv_self__.emptyObjectType : t; mapper.mappedTypes = typeParameters; return mapper; } public isInferenceContext(mapper: TypeMapper): mapper is InferenceContext { return !!(mapper).signature; } public cloneTypeMapper(mapper: TypeMapper): TypeMapper { return mapper && this.isInferenceContext(mapper) ? this.createInferenceContext(mapper.signature, mapper.flags | InferenceFlags.NoDefault, mapper.compareTypes, mapper.inferences) : mapper; } public identityMapper(type: Type): Type { return type; } public combineTypeMappers(mapper1: TypeMapper, mapper2: TypeMapper): TypeMapper { const __conv_self__ = this; const mapper: TypeMapper = t => __conv_self__.instantiateType(mapper1(t), mapper2); mapper.mappedTypes = concatenate(mapper1.mappedTypes, mapper2.mappedTypes); return mapper; } public createReplacementMapper(source: Type, target: Type, baseMapper: TypeMapper) { const mapper: TypeMapper = t => t === source ? target : baseMapper(t); mapper.mappedTypes = baseMapper.mappedTypes; return mapper; } public cloneTypeParameter(typeParameter: TypeParameter): TypeParameter { const result = (this.createType(TypeFlags.TypeParameter)); result.symbol = typeParameter.symbol; result.target = typeParameter; return result; } public cloneTypePredicate(predicate: TypePredicate, mapper: TypeMapper): ThisTypePredicate | IdentifierTypePredicate { if (isIdentifierTypePredicate(predicate)) { return { kind: TypePredicateKind.Identifier, parameterName: predicate.parameterName, parameterIndex: predicate.parameterIndex, type: this.instantiateType(predicate.type, mapper) }; } else { return { kind: TypePredicateKind.This, type: this.instantiateType(predicate.type, mapper) }; } } public instantiateSignature(signature: Signature, mapper: TypeMapper, eraseTypeParameters?: boolean): Signature { let freshTypeParameters: TypeParameter[]; let freshTypePredicate: TypePredicate; if (signature.typeParameters && !eraseTypeParameters) { // First create a fresh set of type parameters, then include a mapping from the old to the // new type parameters in the mapper function. Finally store this mapper in the new type // parameters such that we can use it when instantiating constraints. freshTypeParameters = map(signature.typeParameters, this.cloneTypeParameter); mapper = this.combineTypeMappers(this.createTypeMapper(signature.typeParameters, freshTypeParameters), mapper); for (const tp of freshTypeParameters) { tp.mapper = mapper; } } if (signature.typePredicate) { freshTypePredicate = this.cloneTypePredicate(signature.typePredicate, mapper); } const result = this.createSignature(signature.declaration, freshTypeParameters, signature.thisParameter && this.instantiateSymbol(signature.thisParameter, mapper), this.instantiateList(signature.parameters, mapper, this.instantiateSymbol), /*resolvedReturnType*/ undefined, freshTypePredicate, signature.minArgumentCount, signature.hasRestParameter, signature.hasLiteralTypes); result.target = signature; result.mapper = mapper; return result; } public instantiateSymbol(symbol: Symbol, mapper: TypeMapper): Symbol { if (getCheckFlags(symbol) & CheckFlags.Instantiated) { const links = this.getSymbolLinks(symbol); // If symbol being instantiated is itself a instantiation, fetch the original target and combine the // type mappers. This ensures that original type identities are properly preserved and that aliases // always reference a non-aliases. symbol = links.target; mapper = this.combineTypeMappers(links.mapper, mapper); } // Keep the flags from the symbol we're instantiating. Mark that is instantiated, and // also transient so that we can just store data on it directly. const result = this.createSymbol(symbol.flags, symbol.escapedName); result.checkFlags = CheckFlags.Instantiated; result.declarations = symbol.declarations; result.parent = symbol.parent; result.target = symbol; result.mapper = mapper; if (symbol.valueDeclaration) { result.valueDeclaration = symbol.valueDeclaration; } return result; } public instantiateAnonymousType(type: AnonymousType, mapper: TypeMapper): AnonymousType { const result = (this.createObjectType(ObjectFlags.Anonymous | ObjectFlags.Instantiated, type.symbol)); result.target = type.objectFlags & ObjectFlags.Instantiated ? type.target : type; result.mapper = type.objectFlags & ObjectFlags.Instantiated ? this.combineTypeMappers(type.mapper, mapper) : mapper; result.aliasSymbol = type.aliasSymbol; result.aliasTypeArguments = this.instantiateTypes(type.aliasTypeArguments, mapper); return result; } public instantiateMappedType(type: MappedType, mapper: TypeMapper): Type { const __conv_self__ = this; // Check if we have a homomorphic mapped type, i.e. a type of the form { [P in keyof T]: X } for some // type variable T. If so, the mapped type is distributive over a union type and when T is instantiated // to a union type A | B, we produce { [P in keyof A]: X } | { [P in keyof B]: X }. Furthermore, for // homomorphic mapped types we leave primitive types alone. For example, when T is instantiated to a // union type A | undefined, we produce { [P in keyof A]: X } | undefined. const constraintType = this.getConstraintTypeFromMappedType(type); if (constraintType.flags & TypeFlags.Index) { const typeVariable = (constraintType).type; if (typeVariable.flags & TypeFlags.TypeParameter) { const mappedTypeVariable = this.instantiateType(typeVariable, mapper); if (typeVariable !== mappedTypeVariable) { return this.mapType(mappedTypeVariable, t => { if (__conv_self__.isMappableType(t)) { return __conv_self__.instantiateMappedObjectType(type, __conv_self__.createReplacementMapper(typeVariable, t, mapper)); } return t; }); } } } return this.instantiateMappedObjectType(type, mapper); } public isMappableType(type: Type) { return type.flags & (TypeFlags.TypeParameter | TypeFlags.Object | TypeFlags.Intersection | TypeFlags.IndexedAccess); } public instantiateMappedObjectType(type: MappedType, mapper: TypeMapper): Type { const result = (this.createObjectType(ObjectFlags.Mapped | ObjectFlags.Instantiated, type.symbol)); result.declaration = type.declaration; result.mapper = type.mapper ? this.combineTypeMappers(type.mapper, mapper) : mapper; result.aliasSymbol = type.aliasSymbol; result.aliasTypeArguments = this.instantiateTypes(type.aliasTypeArguments, mapper); return result; } public isSymbolInScopeOfMappedTypeParameter(symbol: Symbol, mapper: TypeMapper) { const __conv_self__ = this; if (!(symbol.declarations && symbol.declarations.length)) { return false; } const mappedTypes = mapper.mappedTypes; // Starting with the parent of the symbol's declaration, check if the mapper maps any of // the type parameters introduced by enclosing declarations. We just pick the first // declaration since multiple declarations will all have the same parent anyway. return !!findAncestor(symbol.declarations[0], node => { if (node.kind === SyntaxKind.ModuleDeclaration || node.kind === SyntaxKind.SourceFile) { return "quit"; } switch (node.kind) { case SyntaxKind.FunctionType: case SyntaxKind.ConstructorType: case SyntaxKind.FunctionDeclaration: case SyntaxKind.MethodDeclaration: case SyntaxKind.MethodSignature: case SyntaxKind.Constructor: case SyntaxKind.CallSignature: case SyntaxKind.ConstructSignature: case SyntaxKind.IndexSignature: case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: case SyntaxKind.ClassDeclaration: case SyntaxKind.ClassExpression: case SyntaxKind.InterfaceDeclaration: case SyntaxKind.TypeAliasDeclaration: const typeParameters = getEffectiveTypeParameterDeclarations(node as DeclarationWithTypeParameters); if (typeParameters) { for (const d of typeParameters) { if (contains(mappedTypes, __conv_self__.getDeclaredTypeOfTypeParameter(__conv_self__.getSymbolOfNode(d)))) { return true; } } } if (isClassLike(node) || node.kind === SyntaxKind.InterfaceDeclaration) { const thisType = __conv_self__.getDeclaredTypeOfClassOrInterface(__conv_self__.getSymbolOfNode(node)).thisType; if (thisType && contains(mappedTypes, thisType)) { return true; } } break; case SyntaxKind.MappedType: if (contains(mappedTypes, __conv_self__.getDeclaredTypeOfTypeParameter(__conv_self__.getSymbolOfNode((node).typeParameter)))) { return true; } break; case SyntaxKind.JSDocFunctionType: const func = node as JSDocFunctionType; for (const p of func.parameters) { if (contains(mappedTypes, __conv_self__.getTypeOfNode(p))) { return true; } } break; } }); } public isTopLevelTypeAlias(symbol: Symbol) { if (symbol.declarations && symbol.declarations.length) { const parentKind = symbol.declarations[0].parent.kind; return parentKind === SyntaxKind.SourceFile || parentKind === SyntaxKind.ModuleBlock; } return false; } public instantiateType(type: Type, mapper: TypeMapper): Type { if (type && mapper !== this.identityMapper) { // If we are instantiating a type that has a top-level type alias, obtain the instantiation through // the type alias instead in order to share instantiations for the same type arguments. This can // dramatically reduce the number of structurally identical types we generate. Note that we can only // perform this optimization for top-level type aliases. Consider: // // function f1(x: T) { // type Foo = { x: X, t: T }; // let obj: Foo = { x: x }; // return obj; // } // function f2(x: U) { return f1(x); } // let z = f2(42); // // Above, the declaration of f2 has an inferred return type that is an instantiation of f1's Foo // equivalent to { x: U, t: U }. When instantiating this return type, we can't go back to Foo's // cache because all cached instantiations are of the form { x: ???, t: T }, i.e. they have not been // instantiated for T. Instead, we need to further instantiate the { x: U, t: U } form. if (type.aliasSymbol && this.isTopLevelTypeAlias(type.aliasSymbol)) { if (type.aliasTypeArguments) { return this.getTypeAliasInstantiation(type.aliasSymbol, this.instantiateTypes(type.aliasTypeArguments, mapper)); } return type; } return this.instantiateTypeNoAlias(type, mapper); } return type; } public instantiateTypeNoAlias(type: Type, mapper: TypeMapper): Type { if (type.flags & TypeFlags.TypeParameter) { return mapper(type); } if (type.flags & TypeFlags.Object) { if ((type).objectFlags & ObjectFlags.Anonymous) { // If the anonymous type originates in a declaration of a function, method, class, or // interface, in an object type literal, or in an object literal expression, we may need // to instantiate the type because it might reference a type parameter. We skip instantiation // if none of the type parameters that are in scope in the type's declaration are mapped by // the given mapper, however we can only do that analysis if the type isn't itself an // instantiation. return type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) && ((type).objectFlags & ObjectFlags.Instantiated || this.isSymbolInScopeOfMappedTypeParameter(type.symbol, mapper)) ? this.instantiateCached(type, mapper, this.instantiateAnonymousType) : type; } if ((type).objectFlags & ObjectFlags.Mapped) { return this.instantiateCached(type, mapper, this.instantiateMappedType); } if ((type).objectFlags & ObjectFlags.Reference) { return this.createTypeReference((type).target, this.instantiateTypes((type).typeArguments, mapper)); } } if (type.flags & TypeFlags.Union && !(type.flags & TypeFlags.Primitive)) { return this.getUnionType(this.instantiateTypes((type).types, mapper), /*subtypeReduction*/ false, type.aliasSymbol, this.instantiateTypes(type.aliasTypeArguments, mapper)); } if (type.flags & TypeFlags.Intersection) { return this.getIntersectionType(this.instantiateTypes((type).types, mapper), type.aliasSymbol, this.instantiateTypes(type.aliasTypeArguments, mapper)); } if (type.flags & TypeFlags.Index) { return this.getIndexType(this.instantiateType((type).type, mapper)); } if (type.flags & TypeFlags.IndexedAccess) { return this.getIndexedAccessType(this.instantiateType((type).objectType, mapper), this.instantiateType((type).indexType, mapper)); } return type; } public instantiateIndexInfo(info: IndexInfo, mapper: TypeMapper): IndexInfo { return info && this.createIndexInfo(this.instantiateType(info.type, mapper), info.isReadonly, info.declaration); } // Returns true if the given expression contains (at any level of nesting) a function or arrow expression // that is subject to contextual typing. public isContextSensitive(node: Expression | MethodDeclaration | ObjectLiteralElementLike | JsxAttributeLike): boolean { Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isObjectLiteralMethod(node)); switch (node.kind) { case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: return this.isContextSensitiveFunctionLikeDeclaration((node)); case SyntaxKind.ObjectLiteralExpression: return forEach((node).properties, this.isContextSensitive); case SyntaxKind.ArrayLiteralExpression: return forEach((node).elements, this.isContextSensitive); case SyntaxKind.ConditionalExpression: return this.isContextSensitive((node).whenTrue) || this.isContextSensitive((node).whenFalse); case SyntaxKind.BinaryExpression: return (node).operatorToken.kind === SyntaxKind.BarBarToken && (this.isContextSensitive((node).left) || this.isContextSensitive((node).right)); case SyntaxKind.PropertyAssignment: return this.isContextSensitive((node).initializer); case SyntaxKind.MethodDeclaration: case SyntaxKind.MethodSignature: return this.isContextSensitiveFunctionLikeDeclaration((node)); case SyntaxKind.ParenthesizedExpression: return this.isContextSensitive((node).expression); case SyntaxKind.JsxAttributes: return forEach((node).properties, this.isContextSensitive); case SyntaxKind.JsxAttribute: // If there is no initializer, JSX attribute has a boolean value of true which is not context sensitive. return (node).initializer && this.isContextSensitive((node).initializer); case SyntaxKind.JsxExpression: // It is possible to that node.expression is undefined (e.g
) return (node).expression && this.isContextSensitive((node).expression); } return false; } public isContextSensitiveFunctionLikeDeclaration(node: FunctionLikeDeclaration) { // Functions with type parameters are not context sensitive. if (node.typeParameters) { return false; } // Functions with any parameters that lack type annotations are context sensitive. if (forEach(node.parameters, p => !getEffectiveTypeAnnotationNode(p))) { return true; } if (node.kind !== SyntaxKind.ArrowFunction) { // If the first parameter is not an explicit 'this' parameter, then the function has // an implicit 'this' parameter which is subject to contextual typing. const parameter = firstOrUndefined(node.parameters); if (!(parameter && parameterIsThisKeyword(parameter))) { return true; } } // TODO(anhans): A block should be context-sensitive if it has a context-sensitive return value. return node.body.kind === SyntaxKind.Block ? false : this.isContextSensitive(node.body); } public isContextSensitiveFunctionOrObjectLiteralMethod(func: Node): func is FunctionExpression | ArrowFunction | MethodDeclaration { return (this.isFunctionExpressionOrArrowFunction(func) || isObjectLiteralMethod(func)) && this.isContextSensitiveFunctionLikeDeclaration(func); } public getTypeWithoutSignatures(type: Type): Type { if (type.flags & TypeFlags.Object) { const resolved = this.resolveStructuredTypeMembers((type)); if (resolved.constructSignatures.length) { const result = (this.createObjectType(ObjectFlags.Anonymous, type.symbol)); result.members = resolved.members; result.properties = resolved.properties; result.callSignatures = emptyArray; result.constructSignatures = emptyArray; return result; } } else if (type.flags & TypeFlags.Intersection) { return this.getIntersectionType(map((type).types, this.getTypeWithoutSignatures)); } return type; } // TYPE CHECKING public isTypeIdenticalTo(source: Type, target: Type): boolean { return this.isTypeRelatedTo(source, target, this.identityRelation); } public compareTypesIdentical(source: Type, target: Type): Ternary { return this.isTypeRelatedTo(source, target, this.identityRelation) ? Ternary.True : Ternary.False; } public compareTypesAssignable(source: Type, target: Type): Ternary { return this.isTypeRelatedTo(source, target, this.assignableRelation) ? Ternary.True : Ternary.False; } public isTypeSubtypeOf(source: Type, target: Type): boolean { return this.isTypeRelatedTo(source, target, this.subtypeRelation); } public isTypeAssignableTo(source: Type, target: Type): boolean { return this.isTypeRelatedTo(source, target, this.assignableRelation); } // A type S is considered to be an instance of a type T if S and T are the same type or if S is a // subtype of T but not structurally identical to T. This specifically means that two distinct but // structurally identical types (such as two classes) are not considered instances of each other. public isTypeInstanceOf(source: Type, target: Type): boolean { return this.getTargetType(source) === this.getTargetType(target) || this.isTypeSubtypeOf(source, target) && !this.isTypeIdenticalTo(source, target); } /** * This is *not* a bi-directional relationship. * If one needs to check both directions for comparability, use a second call to this function or 'checkTypeComparableTo'. * * A type S is comparable to a type T if some (but not necessarily all) of the possible values of S are also possible values of T. * It is used to check following cases: * - the types of the left and right sides of equality/inequality operators (`===`, `!==`, `==`, `!=`). * - the types of `case` clause expressions and their respective `switch` expressions. * - the type of an expression in a type assertion with the type being asserted. */ public isTypeComparableTo(source: Type, target: Type): boolean { return this.isTypeRelatedTo(source, target, this.comparableRelation); } public areTypesComparable(type1: Type, type2: Type): boolean { return this.isTypeComparableTo(type1, type2) || this.isTypeComparableTo(type2, type1); } public checkTypeAssignableTo(source: Type, target: Type, errorNode: Node, headMessage?: DiagnosticMessage, containingMessageChain?: DiagnosticMessageChain): boolean { return this.checkTypeRelatedTo(source, target, this.assignableRelation, errorNode, headMessage, containingMessageChain); } /** * This is *not* a bi-directional relationship. * If one needs to check both directions for comparability, use a second call to this function or 'isTypeComparableTo'. */ public checkTypeComparableTo(source: Type, target: Type, errorNode: Node, headMessage?: DiagnosticMessage, containingMessageChain?: DiagnosticMessageChain): boolean { return this.checkTypeRelatedTo(source, target, this.comparableRelation, errorNode, headMessage, containingMessageChain); } public isSignatureAssignableTo(source: Signature, target: Signature, ignoreReturnTypes: boolean): boolean { return this.compareSignaturesRelated(source, target, /*checkAsCallback*/ false, ignoreReturnTypes, /*reportErrors*/ false, /*errorReporter*/ undefined, this.compareTypesAssignable) !== Ternary.False; } /** * See signatureRelatedTo, compareSignaturesIdentical */ public compareSignaturesRelated(source: Signature, target: Signature, checkAsCallback: boolean, ignoreReturnTypes: boolean, reportErrors: boolean, errorReporter: ErrorReporter, compareTypes: TypeComparer): Ternary { // TODO (drosen): De-duplicate code between related functions. if (source === target) { return Ternary.True; } if (!target.hasRestParameter && source.minArgumentCount > target.parameters.length) { return Ternary.False; } if (source.typeParameters) { source = this.instantiateSignatureInContextOf(source, target, /*contextualMapper*/ undefined, compareTypes); } let result = Ternary.True; const sourceThisType = this.getThisTypeOfSignature(source); if (sourceThisType && sourceThisType !== this.voidType) { const targetThisType = this.getThisTypeOfSignature(target); if (targetThisType) { // void sources are assignable to anything. const related = compareTypes(sourceThisType, targetThisType, /*reportErrors*/ false) || compareTypes(targetThisType, sourceThisType, reportErrors); if (!related) { if (reportErrors) { errorReporter(Diagnostics.The_this_types_of_each_signature_are_incompatible); } return Ternary.False; } result &= related; } } const sourceMax = this.getNumNonRestParameters(source); const targetMax = this.getNumNonRestParameters(target); const checkCount = this.getNumParametersToCheckForSignatureRelatability(source, sourceMax, target, targetMax); const sourceParams = source.parameters; const targetParams = target.parameters; for (let i = 0; i < checkCount; i++) { const sourceType = i < sourceMax ? this.getTypeOfParameter(sourceParams[i]) : this.getRestTypeOfSignature(source); const targetType = i < targetMax ? this.getTypeOfParameter(targetParams[i]) : this.getRestTypeOfSignature(target); const sourceSig = this.getSingleCallSignature(this.getNonNullableType(sourceType)); const targetSig = this.getSingleCallSignature(this.getNonNullableType(targetType)); // In order to ensure that any generic type Foo is at least co-variant with respect to T no matter // how Foo uses T, we need to relate parameters bi-variantly (given that parameters are input positions, // they naturally relate only contra-variantly). However, if the source and target parameters both have // function types with a single call signature, we known we are relating two callback parameters. In // that case it is sufficient to only relate the parameters of the signatures co-variantly because, // similar to return values, callback parameters are output positions. This means that a Promise, // where T is used only in callback parameter positions, will be co-variant (as opposed to bi-variant) // with respect to T. const callbacks = sourceSig && targetSig && !sourceSig.typePredicate && !targetSig.typePredicate && (this.getFalsyFlags(sourceType) & TypeFlags.Nullable) === (this.getFalsyFlags(targetType) & TypeFlags.Nullable); const related = callbacks ? this.compareSignaturesRelated(targetSig, sourceSig, /*checkAsCallback*/ true, /*ignoreReturnTypes*/ false, reportErrors, errorReporter, compareTypes) : !checkAsCallback && compareTypes(sourceType, targetType, /*reportErrors*/ false) || compareTypes(targetType, sourceType, reportErrors); if (!related) { if (reportErrors) { errorReporter(Diagnostics.Types_of_parameters_0_and_1_are_incompatible, unescapeLeadingUnderscores(sourceParams[i < sourceMax ? i : sourceMax].escapedName), unescapeLeadingUnderscores(targetParams[i < targetMax ? i : targetMax].escapedName)); } return Ternary.False; } result &= related; } if (!ignoreReturnTypes) { const targetReturnType = this.getReturnTypeOfSignature(target); if (targetReturnType === this.voidType) { return result; } const sourceReturnType = this.getReturnTypeOfSignature(source); // The following block preserves behavior forbidding boolean returning functions from being assignable to type guard returning functions if (target.typePredicate) { if (source.typePredicate) { result &= this.compareTypePredicateRelatedTo(source.typePredicate, target.typePredicate, source.declaration, target.declaration, reportErrors, errorReporter, compareTypes); } else if (isIdentifierTypePredicate(target.typePredicate)) { if (reportErrors) { errorReporter(Diagnostics.Signature_0_must_be_a_type_predicate, this.signatureToString(source)); } return Ternary.False; } } else { // When relating callback signatures, we still need to relate return types bi-variantly as otherwise // the containing type wouldn't be co-variant. For example, interface Foo { add(cb: () => T): void } // wouldn't be co-variant for T without this rule. result &= checkAsCallback && compareTypes(targetReturnType, sourceReturnType, /*reportErrors*/ false) || compareTypes(sourceReturnType, targetReturnType, reportErrors); } } return result; } public compareTypePredicateRelatedTo(source: TypePredicate, target: TypePredicate, sourceDeclaration: SignatureDeclaration, targetDeclaration: SignatureDeclaration, reportErrors: boolean, errorReporter: ErrorReporter, compareTypes: (s: Type, t: Type, reportErrors?: boolean) => Ternary): Ternary { if (source.kind !== target.kind) { if (reportErrors) { errorReporter(Diagnostics.A_this_based_type_guard_is_not_compatible_with_a_parameter_based_type_guard); errorReporter(Diagnostics.Type_predicate_0_is_not_assignable_to_1, this.typePredicateToString(source), this.typePredicateToString(target)); } return Ternary.False; } if (source.kind === TypePredicateKind.Identifier) { const sourcePredicate = source as IdentifierTypePredicate; const targetPredicate = target as IdentifierTypePredicate; const sourceIndex = sourcePredicate.parameterIndex - (getThisParameter(sourceDeclaration) ? 1 : 0); const targetIndex = targetPredicate.parameterIndex - (getThisParameter(targetDeclaration) ? 1 : 0); if (sourceIndex !== targetIndex) { if (reportErrors) { errorReporter(Diagnostics.Parameter_0_is_not_in_the_same_position_as_parameter_1, sourcePredicate.parameterName, targetPredicate.parameterName); errorReporter(Diagnostics.Type_predicate_0_is_not_assignable_to_1, this.typePredicateToString(source), this.typePredicateToString(target)); } return Ternary.False; } } const related = compareTypes(source.type, target.type, reportErrors); if (related === Ternary.False && reportErrors) { errorReporter(Diagnostics.Type_predicate_0_is_not_assignable_to_1, this.typePredicateToString(source), this.typePredicateToString(target)); } return related; } public isImplementationCompatibleWithOverload(implementation: Signature, overload: Signature): boolean { const erasedSource = this.getErasedSignature(implementation); const erasedTarget = this.getErasedSignature(overload); // First see if the return types are compatible in either direction. const sourceReturnType = this.getReturnTypeOfSignature(erasedSource); const targetReturnType = this.getReturnTypeOfSignature(erasedTarget); if (targetReturnType === this.voidType || this.isTypeRelatedTo(targetReturnType, sourceReturnType, this.assignableRelation) || this.isTypeRelatedTo(sourceReturnType, targetReturnType, this.assignableRelation)) { return this.isSignatureAssignableTo(erasedSource, erasedTarget, /*ignoreReturnTypes*/ true); } return false; } public getNumNonRestParameters(signature: Signature) { const numParams = signature.parameters.length; return signature.hasRestParameter ? numParams - 1 : numParams; } public getNumParametersToCheckForSignatureRelatability(source: Signature, sourceNonRestParamCount: number, target: Signature, targetNonRestParamCount: number) { if (source.hasRestParameter === target.hasRestParameter) { if (source.hasRestParameter) { // If both have rest parameters, get the max and add 1 to // compensate for the rest parameter. return Math.max(sourceNonRestParamCount, targetNonRestParamCount) + 1; } else { return Math.min(sourceNonRestParamCount, targetNonRestParamCount); } } else { // Return the count for whichever signature doesn't have rest parameters. return source.hasRestParameter ? targetNonRestParamCount : sourceNonRestParamCount; } } public isEmptyResolvedType(t: ResolvedType) { return t.properties.length === 0 && t.callSignatures.length === 0 && t.constructSignatures.length === 0 && !t.stringIndexInfo && !t.numberIndexInfo; } public isEmptyObjectType(type: Type): boolean { const __conv_self__ = this; return type.flags & TypeFlags.Object ? this.isEmptyResolvedType(this.resolveStructuredTypeMembers((type))) : type.flags & TypeFlags.NonPrimitive ? true : type.flags & TypeFlags.Union ? forEach((type).types, this.isEmptyObjectType) : type.flags & TypeFlags.Intersection ? !forEach((type).types, t => !__conv_self__.isEmptyObjectType(t)) : false; } public isEnumTypeRelatedTo(sourceSymbol: Symbol, targetSymbol: Symbol, errorReporter?: ErrorReporter) { if (sourceSymbol === targetSymbol) { return true; } const id = getSymbolId(sourceSymbol) + "," + getSymbolId(targetSymbol); const relation = this.enumRelation.get(id); if (relation !== undefined) { return relation; } if (sourceSymbol.escapedName !== targetSymbol.escapedName || !(sourceSymbol.flags & SymbolFlags.RegularEnum) || !(targetSymbol.flags & SymbolFlags.RegularEnum)) { this.enumRelation.set(id, false); return false; } const targetEnumType = this.getTypeOfSymbol(targetSymbol); for (const property of this.getPropertiesOfType(this.getTypeOfSymbol(sourceSymbol))) { if (property.flags & SymbolFlags.EnumMember) { const targetProperty = this.getPropertyOfType(targetEnumType, property.escapedName); if (!targetProperty || !(targetProperty.flags & SymbolFlags.EnumMember)) { if (errorReporter) { errorReporter(Diagnostics.Property_0_is_missing_in_type_1, unescapeLeadingUnderscores(property.escapedName), this.typeToString(this.getDeclaredTypeOfSymbol(targetSymbol), /*enclosingDeclaration*/ undefined, TypeFormatFlags.UseFullyQualifiedType)); } this.enumRelation.set(id, false); return false; } } } this.enumRelation.set(id, true); return true; } public isSimpleTypeRelatedTo(source: Type, target: Type, relation: Map, errorReporter?: ErrorReporter) { const s = source.flags; const t = target.flags; if (t & TypeFlags.Never) return false; if (t & TypeFlags.Any || s & TypeFlags.Never) return true; if (s & TypeFlags.StringLike && t & TypeFlags.String) return true; if (s & TypeFlags.StringLiteral && s & TypeFlags.EnumLiteral && t & TypeFlags.StringLiteral && !(t & TypeFlags.EnumLiteral) && (source).value === (target).value) return true; if (s & TypeFlags.NumberLike && t & TypeFlags.Number) return true; if (s & TypeFlags.NumberLiteral && s & TypeFlags.EnumLiteral && t & TypeFlags.NumberLiteral && !(t & TypeFlags.EnumLiteral) && (source).value === (target).value) return true; if (s & TypeFlags.BooleanLike && t & TypeFlags.Boolean) return true; if (s & TypeFlags.Enum && t & TypeFlags.Enum && this.isEnumTypeRelatedTo(source.symbol, target.symbol, errorReporter)) return true; if (s & TypeFlags.EnumLiteral && t & TypeFlags.EnumLiteral) { if (s & TypeFlags.Union && t & TypeFlags.Union && this.isEnumTypeRelatedTo(source.symbol, target.symbol, errorReporter)) return true; if (s & TypeFlags.Literal && t & TypeFlags.Literal && (source).value === (target).value && this.isEnumTypeRelatedTo(this.getParentOfSymbol(source.symbol), this.getParentOfSymbol(target.symbol), errorReporter)) return true; } if (s & TypeFlags.Undefined && (!this.strictNullChecks || t & (TypeFlags.Undefined | TypeFlags.Void))) return true; if (s & TypeFlags.Null && (!this.strictNullChecks || t & TypeFlags.Null)) return true; if (s & TypeFlags.Object && t & TypeFlags.NonPrimitive) return true; if (relation === this.assignableRelation || relation === this.comparableRelation) { if (s & TypeFlags.Any) return true; // Type number or any numeric literal type is assignable to any numeric enum type or any // numeric enum literal type. This rule exists for backwards compatibility reasons because // bit-flag enum types sometimes look like literal enum types with numeric literal values. if (s & (TypeFlags.Number | TypeFlags.NumberLiteral) && !(s & TypeFlags.EnumLiteral) && (t & TypeFlags.Enum || t & TypeFlags.NumberLiteral && t & TypeFlags.EnumLiteral)) return true; } return false; } public isTypeRelatedTo(source: Type, target: Type, relation: Map) { if (source.flags & TypeFlags.StringOrNumberLiteral && source.flags & TypeFlags.FreshLiteral) { source = (source).regularType; } if (target.flags & TypeFlags.StringOrNumberLiteral && target.flags & TypeFlags.FreshLiteral) { target = (target).regularType; } if (source === target || relation !== this.identityRelation && this.isSimpleTypeRelatedTo(source, target, relation)) { return true; } if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Object) { const id = relation !== this.identityRelation || source.id < target.id ? source.id + "," + target.id : target.id + "," + source.id; const related = relation.get(id); if (related !== undefined) { return related === RelationComparisonResult.Succeeded; } } if (source.flags & TypeFlags.StructuredOrTypeVariable || target.flags & TypeFlags.StructuredOrTypeVariable) { return this.checkTypeRelatedTo(source, target, relation, /*errorNode*/ undefined); } return false; } /** * Checks if 'source' is related to 'target' (e.g.: is a assignable to). * @param source The left-hand-side of the relation. * @param target The right-hand-side of the relation. * @param relation The relation considered. One of 'identityRelation', 'subtypeRelation', 'assignableRelation', or 'comparableRelation'. * Used as both to determine which checks are performed and as a cache of previously computed results. * @param errorNode The suggested node upon which all errors will be reported, if defined. This may or may not be the actual node used. * @param headMessage If the error chain should be prepended by a head message, then headMessage will be used. * @param containingMessageChain A chain of errors to prepend any new errors found. */ public checkTypeRelatedTo(source: Type, target: Type, relation: Map, errorNode: Node, headMessage?: DiagnosticMessage, containingMessageChain?: DiagnosticMessageChain): boolean { const __conv_self__ = this; let errorInfo: DiagnosticMessageChain; let maybeKeys: string[]; let sourceStack: Type[]; let targetStack: Type[]; let maybeCount = 0; let depth = 0; let expandingFlags = 0; let overflow = false; let isIntersectionConstituent = false; Debug.assert(relation !== this.identityRelation || !errorNode, "no error reporting in identity checking"); const result = isRelatedTo(source, target, /*reportErrors*/ !!errorNode, headMessage); if (overflow) { this.error(errorNode, Diagnostics.Excessive_stack_depth_comparing_types_0_and_1, this.typeToString(source), this.typeToString(target)); } else if (errorInfo) { if (containingMessageChain) { errorInfo = concatenateDiagnosticMessageChains(containingMessageChain, errorInfo); } this.diagnostics.add(createDiagnosticForNodeFromMessageChain(errorNode, errorInfo)); } return result !== Ternary.False; function reportError(message: DiagnosticMessage, arg0?: string, arg1?: string, arg2?: string): void { Debug.assert(!!errorNode); errorInfo = chainDiagnosticMessages(errorInfo, message, arg0, arg1, arg2); } function reportRelationError(message: DiagnosticMessage, source: Type, target: Type) { let sourceType = __conv_self__.typeToString(source); let targetType = __conv_self__.typeToString(target); if (sourceType === targetType) { sourceType = __conv_self__.typeToString(source, /*enclosingDeclaration*/ undefined, TypeFormatFlags.UseFullyQualifiedType); targetType = __conv_self__.typeToString(target, /*enclosingDeclaration*/ undefined, TypeFormatFlags.UseFullyQualifiedType); } if (!message) { if (relation === __conv_self__.comparableRelation) { message = Diagnostics.Type_0_is_not_comparable_to_type_1; } else if (sourceType === targetType) { message = Diagnostics.Type_0_is_not_assignable_to_type_1_Two_different_types_with_this_name_exist_but_they_are_unrelated; } else { message = Diagnostics.Type_0_is_not_assignable_to_type_1; } } reportError(message, sourceType, targetType); } function tryElaborateErrorsForPrimitivesAndObjects(source: Type, target: Type) { const sourceType = __conv_self__.typeToString(source); const targetType = __conv_self__.typeToString(target); if ((__conv_self__.globalStringType === source && __conv_self__.stringType === target) || (__conv_self__.globalNumberType === source && __conv_self__.numberType === target) || (__conv_self__.globalBooleanType === source && __conv_self__.booleanType === target) || (__conv_self__.getGlobalESSymbolType(/*reportErrors*/ false) === source && __conv_self__.esSymbolType === target)) { reportError(Diagnostics._0_is_a_primitive_but_1_is_a_wrapper_object_Prefer_using_0_when_possible, targetType, sourceType); } } function isUnionOrIntersectionTypeWithoutNullableConstituents(type: Type): boolean { if (!(type.flags & TypeFlags.UnionOrIntersection)) { return false; } // at this point we know that this is union or intersection type possibly with nullable constituents. // check if we still will have compound type if we ignore nullable components. let seenNonNullable = false; for (const t of (type).types) { if (t.flags & TypeFlags.Nullable) { continue; } if (seenNonNullable) { return true; } seenNonNullable = true; } return false; } /** * Compare two types and return * * Ternary.True if they are related with no assumptions, * * Ternary.Maybe if they are related with assumptions of other relationships, or * * Ternary.False if they are not related. */ function isRelatedTo(source: Type, target: Type, reportErrors?: boolean, headMessage?: DiagnosticMessage): Ternary { if (source.flags & TypeFlags.StringOrNumberLiteral && source.flags & TypeFlags.FreshLiteral) { source = (source).regularType; } if (target.flags & TypeFlags.StringOrNumberLiteral && target.flags & TypeFlags.FreshLiteral) { target = (target).regularType; } // both types are the same - covers 'they are the same primitive type or both are Any' or the same type parameter cases if (source === target) return Ternary.True; if (relation === __conv_self__.identityRelation) { return isIdenticalTo(source, target); } if (__conv_self__.isSimpleTypeRelatedTo(source, target, relation, reportErrors ? reportError : undefined)) return Ternary.True; if (__conv_self__.getObjectFlags(source) & ObjectFlags.ObjectLiteral && source.flags & TypeFlags.FreshLiteral) { if (hasExcessProperties(source, target, reportErrors)) { if (reportErrors) { reportRelationError(headMessage, source, target); } return Ternary.False; } // Above we check for excess properties with respect to the entire target type. When union // and intersection types are further deconstructed on the target side, we don't want to // make the check again (as it might fail for a partial target type). Therefore we obtain // the regular source type and proceed with that. if (isUnionOrIntersectionTypeWithoutNullableConstituents(target)) { source = __conv_self__.getRegularTypeOfObjectLiteral(source); } } if (relation !== __conv_self__.comparableRelation && !(source.flags & TypeFlags.UnionOrIntersection) && !(target.flags & TypeFlags.Union) && !isIntersectionConstituent && source !== __conv_self__.globalObjectType && (__conv_self__.getPropertiesOfType(source).length > 0 || __conv_self__.getSignaturesOfType(source, SignatureKind.Call).length > 0 || __conv_self__.getSignaturesOfType(source, SignatureKind.Construct).length > 0) && isWeakType(target) && !hasCommonProperties(source, target)) { if (reportErrors) { const calls = __conv_self__.getSignaturesOfType(source, SignatureKind.Call); const constructs = __conv_self__.getSignaturesOfType(source, SignatureKind.Construct); if (calls.length > 0 && isRelatedTo(__conv_self__.getReturnTypeOfSignature(calls[0]), target, /*reportErrors*/ false) || constructs.length > 0 && isRelatedTo(__conv_self__.getReturnTypeOfSignature(constructs[0]), target, /*reportErrors*/ false)) { reportError(Diagnostics.Value_of_type_0_has_no_properties_in_common_with_type_1_Did_you_mean_to_call_it, __conv_self__.typeToString(source), __conv_self__.typeToString(target)); } else { reportError(Diagnostics.Type_0_has_no_properties_in_common_with_type_1, __conv_self__.typeToString(source), __conv_self__.typeToString(target)); } } return Ternary.False; } let result = Ternary.False; const saveErrorInfo = errorInfo; const saveIsIntersectionConstituent = isIntersectionConstituent; isIntersectionConstituent = false; // Note that these checks are specifically ordered to produce correct results. In particular, // we need to deconstruct unions before intersections (because unions are always at the top), // and we need to handle "each" relations before "some" relations for the same kind of type. if (source.flags & TypeFlags.Union) { result = relation === __conv_self__.comparableRelation ? someTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive)) : eachTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive)); } else { if (target.flags & TypeFlags.Union) { result = typeRelatedToSomeType(source, target, reportErrors && !(source.flags & TypeFlags.Primitive) && !(target.flags & TypeFlags.Primitive)); } else if (target.flags & TypeFlags.Intersection) { isIntersectionConstituent = true; result = typeRelatedToEachType(source, target as IntersectionType, reportErrors); } else if (source.flags & TypeFlags.Intersection) { // Check to see if any constituents of the intersection are immediately related to the target. // // Don't report errors though. Checking whether a constituent is related to the source is not actually // useful and leads to some confusing error messages. Instead it is better to let the below checks // take care of this, or to not elaborate at all. For instance, // // - For an object type (such as 'C = A & B'), users are usually more interested in structural errors. // // - For a union type (such as '(A | B) = (C & D)'), it's better to hold onto the whole intersection // than to report that 'D' is not assignable to 'A' or 'B'. // // - For a primitive type or type parameter (such as 'number = A & B') there is no point in // breaking the intersection apart. result = someTypeRelatedToType(source, target, /*reportErrors*/ false); } if (!result && (source.flags & TypeFlags.StructuredOrTypeVariable || target.flags & TypeFlags.StructuredOrTypeVariable)) { if (result = recursiveTypeRelatedTo(source, target, reportErrors)) { errorInfo = saveErrorInfo; } } } isIntersectionConstituent = saveIsIntersectionConstituent; if (!result && reportErrors) { if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Primitive) { tryElaborateErrorsForPrimitivesAndObjects(source, target); } else if (source.symbol && source.flags & TypeFlags.Object && __conv_self__.globalObjectType === source) { reportError(Diagnostics.The_Object_type_is_assignable_to_very_few_other_types_Did_you_mean_to_use_the_any_type_instead); } reportRelationError(headMessage, source, target); } return result; } function isIdenticalTo(source: Type, target: Type): Ternary { let result: Ternary; if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Object) { return recursiveTypeRelatedTo(source, target, /*reportErrors*/ false); } if (source.flags & TypeFlags.Union && target.flags & TypeFlags.Union || source.flags & TypeFlags.Intersection && target.flags & TypeFlags.Intersection) { if (result = eachTypeRelatedToSomeType(source, target)) { if (result &= eachTypeRelatedToSomeType(target, source)) { return result; } } } return Ternary.False; } function hasExcessProperties(source: FreshObjectLiteralType, target: Type, reportErrors: boolean): boolean { if (__conv_self__.maybeTypeOfKind(target, TypeFlags.Object) && !(__conv_self__.getObjectFlags(target) & ObjectFlags.ObjectLiteralPatternWithComputedProperties)) { const isComparingJsxAttributes = !!(source.flags & TypeFlags.JsxAttributes); if ((relation === __conv_self__.assignableRelation || relation === __conv_self__.comparableRelation) && (__conv_self__.isTypeSubsetOf(__conv_self__.globalObjectType, target) || (!isComparingJsxAttributes && __conv_self__.isEmptyObjectType(target)))) { return false; } for (const prop of __conv_self__.getPropertiesOfObjectType(source)) { if (!__conv_self__.isKnownProperty(target, prop.escapedName, isComparingJsxAttributes)) { if (reportErrors) { // We know *exactly* where things went wrong when comparing the types. // Use this property as the error node as this will be more helpful in // reasoning about what went wrong. Debug.assert(!!errorNode); if (isJsxAttributes(errorNode) || isJsxOpeningLikeElement(errorNode)) { // JsxAttributes has an object-literal flag and undergo same type-assignablity check as normal object-literal. // However, using an object-literal error message will be very confusing to the users so we give different a message. reportError(Diagnostics.Property_0_does_not_exist_on_type_1, __conv_self__.symbolToString(prop), __conv_self__.typeToString(target)); } else { // use the property's value declaration if the property is assigned inside the literal itself const objectLiteralDeclaration = source.symbol && firstOrUndefined(source.symbol.declarations); if (prop.valueDeclaration && findAncestor(prop.valueDeclaration, d => d === objectLiteralDeclaration)) { errorNode = prop.valueDeclaration; } reportError(Diagnostics.Object_literal_may_only_specify_known_properties_and_0_does_not_exist_in_type_1, __conv_self__.symbolToString(prop), __conv_self__.typeToString(target)); } } return true; } } } return false; } function eachTypeRelatedToSomeType(source: UnionOrIntersectionType, target: UnionOrIntersectionType): Ternary { let result = Ternary.True; const sourceTypes = source.types; for (const sourceType of sourceTypes) { const related = typeRelatedToSomeType(sourceType, target, /*reportErrors*/ false); if (!related) { return Ternary.False; } result &= related; } return result; } function typeRelatedToSomeType(source: Type, target: UnionOrIntersectionType, reportErrors: boolean): Ternary { const targetTypes = target.types; if (target.flags & TypeFlags.Union && __conv_self__.containsType(targetTypes, source)) { return Ternary.True; } for (const type of targetTypes) { const related = isRelatedTo(source, type, /*reportErrors*/ false); if (related) { return related; } } if (reportErrors) { const discriminantType = findMatchingDiscriminantType(source, target); isRelatedTo(source, discriminantType || targetTypes[targetTypes.length - 1], /*reportErrors*/ true); } return Ternary.False; } function findMatchingDiscriminantType(source: Type, target: UnionOrIntersectionType) { const sourceProperties = __conv_self__.getPropertiesOfObjectType(source); if (sourceProperties) { for (const sourceProperty of sourceProperties) { if (__conv_self__.isDiscriminantProperty(target, sourceProperty.escapedName)) { const sourceType = __conv_self__.getTypeOfSymbol(sourceProperty); for (const type of target.types) { const targetType = __conv_self__.getTypeOfPropertyOfType(type, sourceProperty.escapedName); if (targetType && isRelatedTo(sourceType, targetType)) { return type; } } } } } } function typeRelatedToEachType(source: Type, target: IntersectionType, reportErrors: boolean): Ternary { let result = Ternary.True; const targetTypes = target.types; for (const targetType of targetTypes) { const related = isRelatedTo(source, targetType, reportErrors); if (!related) { return Ternary.False; } result &= related; } return result; } function someTypeRelatedToType(source: UnionOrIntersectionType, target: Type, reportErrors: boolean): Ternary { const sourceTypes = source.types; if (source.flags & TypeFlags.Union && __conv_self__.containsType(sourceTypes, target)) { return Ternary.True; } const len = sourceTypes.length; for (let i = 0; i < len; i++) { const related = isRelatedTo(sourceTypes[i], target, reportErrors && i === len - 1); if (related) { return related; } } return Ternary.False; } function eachTypeRelatedToType(source: UnionOrIntersectionType, target: Type, reportErrors: boolean): Ternary { let result = Ternary.True; const sourceTypes = source.types; for (const sourceType of sourceTypes) { const related = isRelatedTo(sourceType, target, reportErrors); if (!related) { return Ternary.False; } result &= related; } return result; } function typeArgumentsRelatedTo(source: TypeReference, target: TypeReference, reportErrors: boolean): Ternary { const sources = source.typeArguments || emptyArray; const targets = target.typeArguments || emptyArray; if (sources.length !== targets.length && relation === __conv_self__.identityRelation) { return Ternary.False; } const length = sources.length <= targets.length ? sources.length : targets.length; let result = Ternary.True; for (let i = 0; i < length; i++) { const related = isRelatedTo(sources[i], targets[i], reportErrors); if (!related) { return Ternary.False; } result &= related; } return result; } // Determine if possibly recursive types are related. First, check if the result is already available in the global cache. // Second, check if we have already started a comparison of the given two types in which case we assume the result to be true. // Third, check if both types are part of deeply nested chains of generic type instantiations and if so assume the types are // equal and infinitely expanding. Fourth, if we have reached a depth of 100 nested comparisons, assume we have runaway recursion // and issue an error. Otherwise, actually compare the structure of the two types. function recursiveTypeRelatedTo(source: Type, target: Type, reportErrors: boolean): Ternary { if (overflow) { return Ternary.False; } const id = relation !== __conv_self__.identityRelation || source.id < target.id ? source.id + "," + target.id : target.id + "," + source.id; const related = relation.get(id); if (related !== undefined) { if (reportErrors && related === RelationComparisonResult.Failed) { // We are elaborating errors and the cached result is an unreported failure. Record the result as a reported // failure and continue computing the relation such that errors get reported. relation.set(id, RelationComparisonResult.FailedAndReported); } else { return related === RelationComparisonResult.Succeeded ? Ternary.True : Ternary.False; } } if (!maybeKeys) { maybeKeys = []; sourceStack = []; targetStack = []; } else { for (let i = 0; i < maybeCount; i++) { // If source and target are already being compared, consider them related with assumptions if (id === maybeKeys[i]) { return Ternary.Maybe; } } if (depth === 100) { overflow = true; return Ternary.False; } } const maybeStart = maybeCount; maybeKeys[maybeCount] = id; maybeCount++; sourceStack[depth] = source; targetStack[depth] = target; depth++; const saveExpandingFlags = expandingFlags; if (!(expandingFlags & 1) && __conv_self__.isDeeplyNestedType(source, sourceStack, depth)) expandingFlags |= 1; if (!(expandingFlags & 2) && __conv_self__.isDeeplyNestedType(target, targetStack, depth)) expandingFlags |= 2; const result = expandingFlags !== 3 ? structuredTypeRelatedTo(source, target, reportErrors) : Ternary.Maybe; expandingFlags = saveExpandingFlags; depth--; if (result) { if (result === Ternary.True || depth === 0) { // If result is definitely true, record all maybe keys as having succeeded for (let i = maybeStart; i < maybeCount; i++) { relation.set(maybeKeys[i], RelationComparisonResult.Succeeded); } maybeCount = maybeStart; } } else { // A false result goes straight into global cache (when something is false under // assumptions it will also be false without assumptions) relation.set(id, reportErrors ? RelationComparisonResult.FailedAndReported : RelationComparisonResult.Failed); maybeCount = maybeStart; } return result; } function structuredTypeRelatedTo(source: Type, target: Type, reportErrors: boolean): Ternary { let result: Ternary; const saveErrorInfo = errorInfo; if (target.flags & TypeFlags.TypeParameter) { // A source type { [P in keyof T]: X } is related to a target type T if X is related to T[P]. if (__conv_self__.getObjectFlags(source) & ObjectFlags.Mapped && __conv_self__.getConstraintTypeFromMappedType((source)) === __conv_self__.getIndexType(target)) { if (!(source).declaration.questionToken) { const templateType = __conv_self__.getTemplateTypeFromMappedType((source)); const indexedAccessType = __conv_self__.getIndexedAccessType(target, __conv_self__.getTypeParameterFromMappedType((source))); if (result = isRelatedTo(templateType, indexedAccessType, reportErrors)) { return result; } } } } else if (target.flags & TypeFlags.Index) { // A keyof S is related to a keyof T if T is related to S. if (source.flags & TypeFlags.Index) { if (result = isRelatedTo((target).type, (source).type, /*reportErrors*/ false)) { return result; } } // A type S is assignable to keyof T if S is assignable to keyof C, where C is the // constraint of T. const constraint = __conv_self__.getConstraintOfType((target).type); if (constraint) { if (result = isRelatedTo(source, __conv_self__.getIndexType(constraint), reportErrors)) { return result; } } } else if (target.flags & TypeFlags.IndexedAccess) { // A type S is related to a type T[K] if S is related to A[K], where K is string-like and // A is the apparent type of S. const constraint = __conv_self__.getConstraintOfType((target)); if (constraint) { if (result = isRelatedTo(source, constraint, reportErrors)) { errorInfo = saveErrorInfo; return result; } } } if (source.flags & TypeFlags.TypeParameter) { // A source type T is related to a target type { [P in keyof T]: X } if T[P] is related to X. if (__conv_self__.getObjectFlags(target) & ObjectFlags.Mapped && __conv_self__.getConstraintTypeFromMappedType((target)) === __conv_self__.getIndexType(source)) { const indexedAccessType = __conv_self__.getIndexedAccessType(source, __conv_self__.getTypeParameterFromMappedType((target))); const templateType = __conv_self__.getTemplateTypeFromMappedType((target)); if (result = isRelatedTo(indexedAccessType, templateType, reportErrors)) { errorInfo = saveErrorInfo; return result; } } else { let constraint = __conv_self__.getConstraintOfTypeParameter((source)); // A type parameter with no constraint is not related to the non-primitive object type. if (constraint || !(target.flags & TypeFlags.NonPrimitive)) { if (!constraint || constraint.flags & TypeFlags.Any) { constraint = __conv_self__.emptyObjectType; } // The constraint may need to be further instantiated with its 'this' type. constraint = __conv_self__.getTypeWithThisArgument(constraint, source); // Report constraint errors only if the constraint is not the empty object type const reportConstraintErrors = reportErrors && constraint !== __conv_self__.emptyObjectType; if (result = isRelatedTo(constraint, target, reportConstraintErrors)) { errorInfo = saveErrorInfo; return result; } } } } else if (source.flags & TypeFlags.IndexedAccess) { // A type S[K] is related to a type T if A[K] is related to T, where K is string-like and // A is the apparent type of S. const constraint = __conv_self__.getConstraintOfType((source)); if (constraint) { if (result = isRelatedTo(constraint, target, reportErrors)) { errorInfo = saveErrorInfo; return result; } } else if (target.flags & TypeFlags.IndexedAccess && (source).indexType === (target).indexType) { // if we have indexed access types with identical index types, see if relationship holds for // the two object types. if (result = isRelatedTo((source).objectType, (target).objectType, reportErrors)) { return result; } } } else { if (__conv_self__.getObjectFlags(source) & ObjectFlags.Reference && __conv_self__.getObjectFlags(target) & ObjectFlags.Reference && (source).target === (target).target) { // We have type references to same target type, see if relationship holds for all type arguments if (result = typeArgumentsRelatedTo(source, target, reportErrors)) { return result; } } // Even if relationship doesn't hold for unions, intersections, or generic type references, // it may hold in a structural comparison. const sourceIsPrimitive = !!(source.flags & TypeFlags.Primitive); if (relation !== __conv_self__.identityRelation) { source = __conv_self__.getApparentType(source); } // In a check of the form X = A & B, we will have previously checked if A relates to X or B relates // to X. Failing both of those we want to check if the aggregation of A and B's members structurally // relates to X. Thus, we include intersection types on the source side here. if (source.flags & (TypeFlags.Object | TypeFlags.Intersection) && target.flags & TypeFlags.Object) { // Report structural errors only if we haven't reported any errors yet const reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo && !sourceIsPrimitive; // An empty object type is related to any mapped type that includes a '?' modifier. if (__conv_self__.isPartialMappedType(target) && !__conv_self__.isGenericMappedType(source) && __conv_self__.isEmptyObjectType(source)) { result = Ternary.True; } else if (__conv_self__.isGenericMappedType(target)) { result = __conv_self__.isGenericMappedType(source) ? mappedTypeRelatedTo(source, target, reportStructuralErrors) : Ternary.False; } else { result = propertiesRelatedTo(source, target, reportStructuralErrors); if (result) { result &= signaturesRelatedTo(source, target, SignatureKind.Call, reportStructuralErrors); if (result) { result &= signaturesRelatedTo(source, target, SignatureKind.Construct, reportStructuralErrors); if (result) { result &= indexTypesRelatedTo(source, target, IndexKind.String, sourceIsPrimitive, reportStructuralErrors); if (result) { result &= indexTypesRelatedTo(source, target, IndexKind.Number, sourceIsPrimitive, reportStructuralErrors); } } } } } if (result) { errorInfo = saveErrorInfo; return result; } } } return Ternary.False; } // A type [P in S]: X is related to a type [Q in T]: Y if T is related to S and X' is // related to Y, where X' is an instantiation of X in which P is replaced with Q. Notice // that S and T are contra-variant whereas X and Y are co-variant. function mappedTypeRelatedTo(source: MappedType, target: MappedType, reportErrors: boolean): Ternary { const sourceReadonly = !!source.declaration.readonlyToken; const sourceOptional = !!source.declaration.questionToken; const targetReadonly = !!target.declaration.readonlyToken; const targetOptional = !!target.declaration.questionToken; const modifiersRelated = relation === __conv_self__.identityRelation ? sourceReadonly === targetReadonly && sourceOptional === targetOptional : relation === __conv_self__.comparableRelation || !sourceOptional || targetOptional; if (modifiersRelated) { let result: Ternary; if (result = isRelatedTo(__conv_self__.getConstraintTypeFromMappedType((target)), __conv_self__.getConstraintTypeFromMappedType((source)), reportErrors)) { const mapper = __conv_self__.createTypeMapper([__conv_self__.getTypeParameterFromMappedType((source))], [__conv_self__.getTypeParameterFromMappedType((target))]); return result & isRelatedTo(__conv_self__.instantiateType(__conv_self__.getTemplateTypeFromMappedType((source)), mapper), __conv_self__.getTemplateTypeFromMappedType((target)), reportErrors); } } return Ternary.False; } function propertiesRelatedTo(source: Type, target: Type, reportErrors: boolean): Ternary { if (relation === __conv_self__.identityRelation) { return propertiesIdenticalTo(source, target); } let result = Ternary.True; const properties = __conv_self__.getPropertiesOfObjectType(target); const requireOptionalProperties = relation === __conv_self__.subtypeRelation && !(__conv_self__.getObjectFlags(source) & ObjectFlags.ObjectLiteral); for (const targetProp of properties) { const sourceProp = __conv_self__.getPropertyOfType(source, targetProp.escapedName); if (sourceProp !== targetProp) { if (!sourceProp) { if (!(targetProp.flags & SymbolFlags.Optional) || requireOptionalProperties) { if (reportErrors) { reportError(Diagnostics.Property_0_is_missing_in_type_1, __conv_self__.symbolToString(targetProp), __conv_self__.typeToString(source)); } return Ternary.False; } } else if (!(targetProp.flags & SymbolFlags.Prototype)) { const sourcePropFlags = getDeclarationModifierFlagsFromSymbol(sourceProp); const targetPropFlags = getDeclarationModifierFlagsFromSymbol(targetProp); if (sourcePropFlags & ModifierFlags.Private || targetPropFlags & ModifierFlags.Private) { if (getCheckFlags(sourceProp) & CheckFlags.ContainsPrivate) { if (reportErrors) { reportError(Diagnostics.Property_0_has_conflicting_declarations_and_is_inaccessible_in_type_1, __conv_self__.symbolToString(sourceProp), __conv_self__.typeToString(source)); } return Ternary.False; } if (sourceProp.valueDeclaration !== targetProp.valueDeclaration) { if (reportErrors) { if (sourcePropFlags & ModifierFlags.Private && targetPropFlags & ModifierFlags.Private) { reportError(Diagnostics.Types_have_separate_declarations_of_a_private_property_0, __conv_self__.symbolToString(targetProp)); } else { reportError(Diagnostics.Property_0_is_private_in_type_1_but_not_in_type_2, __conv_self__.symbolToString(targetProp), __conv_self__.typeToString(sourcePropFlags & ModifierFlags.Private ? source : target), __conv_self__.typeToString(sourcePropFlags & ModifierFlags.Private ? target : source)); } } return Ternary.False; } } else if (targetPropFlags & ModifierFlags.Protected) { if (!__conv_self__.isValidOverrideOf(sourceProp, targetProp)) { if (reportErrors) { reportError(Diagnostics.Property_0_is_protected_but_type_1_is_not_a_class_derived_from_2, __conv_self__.symbolToString(targetProp), __conv_self__.typeToString(__conv_self__.getDeclaringClass(sourceProp) || source), __conv_self__.typeToString(__conv_self__.getDeclaringClass(targetProp) || target)); } return Ternary.False; } } else if (sourcePropFlags & ModifierFlags.Protected) { if (reportErrors) { reportError(Diagnostics.Property_0_is_protected_in_type_1_but_public_in_type_2, __conv_self__.symbolToString(targetProp), __conv_self__.typeToString(source), __conv_self__.typeToString(target)); } return Ternary.False; } const related = isRelatedTo(__conv_self__.getTypeOfSymbol(sourceProp), __conv_self__.getTypeOfSymbol(targetProp), reportErrors); if (!related) { if (reportErrors) { reportError(Diagnostics.Types_of_property_0_are_incompatible, __conv_self__.symbolToString(targetProp)); } return Ternary.False; } result &= related; // When checking for comparability, be more lenient with optional properties. if (relation !== __conv_self__.comparableRelation && sourceProp.flags & SymbolFlags.Optional && !(targetProp.flags & SymbolFlags.Optional)) { // TypeScript 1.0 spec (April 2014): 3.8.3 // S is a subtype of a type T, and T is a supertype of S if ... // S' and T are object types and, for each member M in T.. // M is a property and S' contains a property N where // if M is a required property, N is also a required property // (M - property in T) // (N - property in S) if (reportErrors) { reportError(Diagnostics.Property_0_is_optional_in_type_1_but_required_in_type_2, __conv_self__.symbolToString(targetProp), __conv_self__.typeToString(source), __conv_self__.typeToString(target)); } return Ternary.False; } } } } return result; } /** * A type is 'weak' if it is an object type with at least one optional property * and no required properties, call/construct signatures or index signatures */ function isWeakType(type: Type): boolean { if (type.flags & TypeFlags.Object) { const resolved = __conv_self__.resolveStructuredTypeMembers((type)); return resolved.callSignatures.length === 0 && resolved.constructSignatures.length === 0 && !resolved.stringIndexInfo && !resolved.numberIndexInfo && resolved.properties.length > 0 && every(resolved.properties, p => !!(p.flags & SymbolFlags.Optional)); } if (type.flags & TypeFlags.Intersection) { return every((type).types, isWeakType); } return false; } function hasCommonProperties(source: Type, target: Type) { const isComparingJsxAttributes = !!(source.flags & TypeFlags.JsxAttributes); for (const prop of __conv_self__.getPropertiesOfType(source)) { if (__conv_self__.isKnownProperty(target, prop.escapedName, isComparingJsxAttributes)) { return true; } } return false; } function propertiesIdenticalTo(source: Type, target: Type): Ternary { if (!(source.flags & TypeFlags.Object && target.flags & TypeFlags.Object)) { return Ternary.False; } const sourceProperties = __conv_self__.getPropertiesOfObjectType(source); const targetProperties = __conv_self__.getPropertiesOfObjectType(target); if (sourceProperties.length !== targetProperties.length) { return Ternary.False; } let result = Ternary.True; for (const sourceProp of sourceProperties) { const targetProp = __conv_self__.getPropertyOfObjectType(target, sourceProp.escapedName); if (!targetProp) { return Ternary.False; } const related = __conv_self__.compareProperties(sourceProp, targetProp, isRelatedTo); if (!related) { return Ternary.False; } result &= related; } return result; } function signaturesRelatedTo(source: Type, target: Type, kind: SignatureKind, reportErrors: boolean): Ternary { if (relation === __conv_self__.identityRelation) { return signaturesIdenticalTo(source, target, kind); } if (target === __conv_self__.anyFunctionType || source === __conv_self__.anyFunctionType) { return Ternary.True; } const sourceSignatures = __conv_self__.getSignaturesOfType(source, kind); const targetSignatures = __conv_self__.getSignaturesOfType(target, kind); if (kind === SignatureKind.Construct && sourceSignatures.length && targetSignatures.length) { if (__conv_self__.isAbstractConstructorType(source) && !__conv_self__.isAbstractConstructorType(target)) { // An abstract constructor type is not assignable to a non-abstract constructor type // as it would otherwise be possible to new an abstract class. Note that the assignability // check we perform for an extends clause excludes construct signatures from the target, // so this check never proceeds. if (reportErrors) { reportError(Diagnostics.Cannot_assign_an_abstract_constructor_type_to_a_non_abstract_constructor_type); } return Ternary.False; } if (!constructorVisibilitiesAreCompatible(sourceSignatures[0], targetSignatures[0], reportErrors)) { return Ternary.False; } } let result = Ternary.True; const saveErrorInfo = errorInfo; if (__conv_self__.getObjectFlags(source) & ObjectFlags.Instantiated && __conv_self__.getObjectFlags(target) & ObjectFlags.Instantiated && source.symbol === target.symbol) { // We have instantiations of the same anonymous type (which typically will be the type of a // method). Simply do a pairwise comparison of the signatures in the two signature lists instead // of the much more expensive N * M comparison matrix we explore below. We erase type parameters // as they are known to always be the same. for (let i = 0; i < targetSignatures.length; i++) { const related = signatureRelatedTo(sourceSignatures[i], targetSignatures[i], /*erase*/ true, reportErrors); if (!related) { return Ternary.False; } result &= related; } } else if (sourceSignatures.length === 1 && targetSignatures.length === 1) { // For simple functions (functions with a single signature) we only erase type parameters for // the comparable relation. Otherwise, if the source signature is generic, we instantiate it // in the context of the target signature before checking the relationship. Ideally we'd do // this regardless of the number of signatures, but the potential costs are prohibitive due // to the quadratic nature of the logic below. const eraseGenerics = relation === __conv_self__.comparableRelation || __conv_self__.compilerOptions.noStrictGenericChecks; result = signatureRelatedTo(sourceSignatures[0], targetSignatures[0], eraseGenerics, reportErrors); } else { outer: for (const t of targetSignatures) { // Only elaborate errors from the first failure let shouldElaborateErrors = reportErrors; for (const s of sourceSignatures) { const related = signatureRelatedTo(s, t, /*erase*/ true, shouldElaborateErrors); if (related) { result &= related; errorInfo = saveErrorInfo; continue outer; } shouldElaborateErrors = false; } if (shouldElaborateErrors) { reportError(Diagnostics.Type_0_provides_no_match_for_the_signature_1, __conv_self__.typeToString(source), __conv_self__.signatureToString(t, /*enclosingDeclaration*/ undefined, /*flags*/ undefined, kind)); } return Ternary.False; } } return result; } /** * See signatureAssignableTo, compareSignaturesIdentical */ function signatureRelatedTo(source: Signature, target: Signature, erase: boolean, reportErrors: boolean): Ternary { return __conv_self__.compareSignaturesRelated(erase ? __conv_self__.getErasedSignature(source) : source, erase ? __conv_self__.getErasedSignature(target) : target, /*checkAsCallback*/ false, /*ignoreReturnTypes*/ false, reportErrors, reportError, isRelatedTo); } function signaturesIdenticalTo(source: Type, target: Type, kind: SignatureKind): Ternary { const sourceSignatures = __conv_self__.getSignaturesOfType(source, kind); const targetSignatures = __conv_self__.getSignaturesOfType(target, kind); if (sourceSignatures.length !== targetSignatures.length) { return Ternary.False; } let result = Ternary.True; for (let i = 0; i < sourceSignatures.length; i++) { const related = __conv_self__.compareSignaturesIdentical(sourceSignatures[i], targetSignatures[i], /*partialMatch*/ false, /*ignoreThisTypes*/ false, /*ignoreReturnTypes*/ false, isRelatedTo); if (!related) { return Ternary.False; } result &= related; } return result; } function eachPropertyRelatedTo(source: Type, target: Type, kind: IndexKind, reportErrors: boolean): Ternary { let result = Ternary.True; for (const prop of __conv_self__.getPropertiesOfObjectType(source)) { if (kind === IndexKind.String || __conv_self__.isNumericLiteralName(prop.escapedName)) { const related = isRelatedTo(__conv_self__.getTypeOfSymbol(prop), target, reportErrors); if (!related) { if (reportErrors) { reportError(Diagnostics.Property_0_is_incompatible_with_index_signature, __conv_self__.symbolToString(prop)); } return Ternary.False; } result &= related; } } return result; } function indexInfoRelatedTo(sourceInfo: IndexInfo, targetInfo: IndexInfo, reportErrors: boolean) { const related = isRelatedTo(sourceInfo.type, targetInfo.type, reportErrors); if (!related && reportErrors) { reportError(Diagnostics.Index_signatures_are_incompatible); } return related; } function indexTypesRelatedTo(source: Type, target: Type, kind: IndexKind, sourceIsPrimitive: boolean, reportErrors: boolean) { if (relation === __conv_self__.identityRelation) { return indexTypesIdenticalTo(source, target, kind); } const targetInfo = __conv_self__.getIndexInfoOfType(target, kind); if (!targetInfo || targetInfo.type.flags & TypeFlags.Any && !sourceIsPrimitive) { // Index signature of type any permits assignment from everything but primitives return Ternary.True; } const sourceInfo = __conv_self__.getIndexInfoOfType(source, kind) || kind === IndexKind.Number && __conv_self__.getIndexInfoOfType(source, IndexKind.String); if (sourceInfo) { return indexInfoRelatedTo(sourceInfo, targetInfo, reportErrors); } if (__conv_self__.isGenericMappedType(source)) { // A generic mapped type { [P in K]: T } is related to an index signature { [x: string]: U } // if T is related to U. return kind === IndexKind.String && isRelatedTo(__conv_self__.getTemplateTypeFromMappedType((source)), targetInfo.type, reportErrors); } if (__conv_self__.isObjectLiteralType(source)) { let related = Ternary.True; if (kind === IndexKind.String) { const sourceNumberInfo = __conv_self__.getIndexInfoOfType(source, IndexKind.Number); if (sourceNumberInfo) { related = indexInfoRelatedTo(sourceNumberInfo, targetInfo, reportErrors); } } if (related) { related &= eachPropertyRelatedTo(source, targetInfo.type, kind, reportErrors); } return related; } if (reportErrors) { reportError(Diagnostics.Index_signature_is_missing_in_type_0, __conv_self__.typeToString(source)); } return Ternary.False; } function indexTypesIdenticalTo(source: Type, target: Type, indexKind: IndexKind): Ternary { const targetInfo = __conv_self__.getIndexInfoOfType(target, indexKind); const sourceInfo = __conv_self__.getIndexInfoOfType(source, indexKind); if (!sourceInfo && !targetInfo) { return Ternary.True; } if (sourceInfo && targetInfo && sourceInfo.isReadonly === targetInfo.isReadonly) { return isRelatedTo(sourceInfo.type, targetInfo.type); } return Ternary.False; } function constructorVisibilitiesAreCompatible(sourceSignature: Signature, targetSignature: Signature, reportErrors: boolean) { if (!sourceSignature.declaration || !targetSignature.declaration) { return true; } const sourceAccessibility = getSelectedModifierFlags(sourceSignature.declaration, ModifierFlags.NonPublicAccessibilityModifier); const targetAccessibility = getSelectedModifierFlags(targetSignature.declaration, ModifierFlags.NonPublicAccessibilityModifier); // A public, protected and private signature is assignable to a private signature. if (targetAccessibility === ModifierFlags.Private) { return true; } // A public and protected signature is assignable to a protected signature. if (targetAccessibility === ModifierFlags.Protected && sourceAccessibility !== ModifierFlags.Private) { return true; } // Only a public signature is assignable to public signature. if (targetAccessibility !== ModifierFlags.Protected && !sourceAccessibility) { return true; } if (reportErrors) { reportError(Diagnostics.Cannot_assign_a_0_constructor_type_to_a_1_constructor_type, __conv_self__.visibilityToString(sourceAccessibility), __conv_self__.visibilityToString(targetAccessibility)); } return false; } } // Invoke the callback for each underlying property symbol of the given symbol and return the first // value that isn't undefined. public forEachProperty(prop: Symbol, callback: (p: Symbol) => T): T { if (getCheckFlags(prop) & CheckFlags.Synthetic) { for (const t of (prop).containingType.types) { const p = this.getPropertyOfType(t, prop.escapedName); const result = p && this.forEachProperty(p, callback); if (result) { return result; } } return undefined; } return callback(prop); } // Return the declaring class type of a property or undefined if property not declared in class public getDeclaringClass(prop: Symbol) { return prop.parent && prop.parent.flags & SymbolFlags.Class ? this.getDeclaredTypeOfSymbol(this.getParentOfSymbol(prop)) : undefined; } // Return true if some underlying source property is declared in a class that derives // from the given base class. public isPropertyInClassDerivedFrom(prop: Symbol, baseClass: Type) { const __conv_self__ = this; return this.forEachProperty(prop, sp => { const sourceClass = __conv_self__.getDeclaringClass(sp); return sourceClass ? __conv_self__.hasBaseType(sourceClass, baseClass) : false; }); } // Return true if source property is a valid override of protected parts of target property. public isValidOverrideOf(sourceProp: Symbol, targetProp: Symbol) { const __conv_self__ = this; return !this.forEachProperty(targetProp, tp => getDeclarationModifierFlagsFromSymbol(tp) & ModifierFlags.Protected ? !__conv_self__.isPropertyInClassDerivedFrom(sourceProp, __conv_self__.getDeclaringClass(tp)) : false); } // Return true if the given class derives from each of the declaring classes of the protected // constituents of the given property. public isClassDerivedFromDeclaringClasses(checkClass: Type, prop: Symbol) { const __conv_self__ = this; return this.forEachProperty(prop, p => getDeclarationModifierFlagsFromSymbol(p) & ModifierFlags.Protected ? !__conv_self__.hasBaseType(checkClass, __conv_self__.getDeclaringClass(p)) : false) ? undefined : checkClass; } // Return true if the given type is the constructor type for an abstract class public isAbstractConstructorType(type: Type) { if (this.getObjectFlags(type) & ObjectFlags.Anonymous) { const symbol = type.symbol; if (symbol && symbol.flags & SymbolFlags.Class) { const declaration = this.getClassLikeDeclarationOfSymbol(symbol); if (declaration && hasModifier(declaration, ModifierFlags.Abstract)) { return true; } } } return false; } // Return true if the given type is deeply nested. We consider this to be the case when structural type comparisons // for 5 or more occurrences or instantiations of the type have been recorded on the given stack. It is possible, // though highly unlikely, for this test to be true in a situation where a chain of instantiations is not infinitely // expanding. Effectively, we will generate a false positive when two types are structurally equal to at least 5 // levels, but unequal at some level beyond that. public isDeeplyNestedType(type: Type, stack: Type[], depth: number): boolean { // We track all object types that have an associated symbol (representing the origin of the type) if (depth >= 5 && type.flags & TypeFlags.Object) { const symbol = type.symbol; if (symbol) { let count = 0; for (let i = 0; i < depth; i++) { const t = stack[i]; if (t.flags & TypeFlags.Object && t.symbol === symbol) { count++; if (count >= 5) return true; } } } } return false; } public isPropertyIdenticalTo(sourceProp: Symbol, targetProp: Symbol): boolean { return this.compareProperties(sourceProp, targetProp, this.compareTypesIdentical) !== Ternary.False; } public compareProperties(sourceProp: Symbol, targetProp: Symbol, compareTypes: (source: Type, target: Type) => Ternary): Ternary { // Two members are considered identical when // - they are public properties with identical names, optionality, and types, // - they are private or protected properties originating in the same declaration and having identical types if (sourceProp === targetProp) { return Ternary.True; } const sourcePropAccessibility = getDeclarationModifierFlagsFromSymbol(sourceProp) & ModifierFlags.NonPublicAccessibilityModifier; const targetPropAccessibility = getDeclarationModifierFlagsFromSymbol(targetProp) & ModifierFlags.NonPublicAccessibilityModifier; if (sourcePropAccessibility !== targetPropAccessibility) { return Ternary.False; } if (sourcePropAccessibility) { if (this.getTargetSymbol(sourceProp) !== this.getTargetSymbol(targetProp)) { return Ternary.False; } } else { if ((sourceProp.flags & SymbolFlags.Optional) !== (targetProp.flags & SymbolFlags.Optional)) { return Ternary.False; } } if (this.isReadonlySymbol(sourceProp) !== this.isReadonlySymbol(targetProp)) { return Ternary.False; } return compareTypes(this.getTypeOfSymbol(sourceProp), this.getTypeOfSymbol(targetProp)); } public isMatchingSignature(source: Signature, target: Signature, partialMatch: boolean) { // A source signature matches a target signature if the two signatures have the same number of required, // optional, and rest parameters. if (source.parameters.length === target.parameters.length && source.minArgumentCount === target.minArgumentCount && source.hasRestParameter === target.hasRestParameter) { return true; } // A source signature partially matches a target signature if the target signature has no fewer required // parameters and no more overall parameters than the source signature (where a signature with a rest // parameter is always considered to have more overall parameters than one without). const sourceRestCount = source.hasRestParameter ? 1 : 0; const targetRestCount = target.hasRestParameter ? 1 : 0; if (partialMatch && source.minArgumentCount <= target.minArgumentCount && (sourceRestCount > targetRestCount || sourceRestCount === targetRestCount && source.parameters.length >= target.parameters.length)) { return true; } return false; } /** * See signatureRelatedTo, compareSignaturesIdentical */ public compareSignaturesIdentical(source: Signature, target: Signature, partialMatch: boolean, ignoreThisTypes: boolean, ignoreReturnTypes: boolean, compareTypes: (s: Type, t: Type) => Ternary): Ternary { // TODO (drosen): De-duplicate code between related functions. if (source === target) { return Ternary.True; } if (!(this.isMatchingSignature(source, target, partialMatch))) { return Ternary.False; } // Check that the two signatures have the same number of type parameters. We might consider // also checking that any type parameter constraints match, but that would require instantiating // the constraints with a common set of type arguments to get relatable entities in places where // type parameters occur in the constraints. The complexity of doing that doesn't seem worthwhile, // particularly as we're comparing erased versions of the signatures below. if (length(source.typeParameters) !== length(target.typeParameters)) { return Ternary.False; } // Spec 1.0 Section 3.8.3 & 3.8.4: // M and N (the signatures) are instantiated using type Any as the type argument for all type parameters declared by M and N source = this.getErasedSignature(source); target = this.getErasedSignature(target); let result = Ternary.True; if (!ignoreThisTypes) { const sourceThisType = this.getThisTypeOfSignature(source); if (sourceThisType) { const targetThisType = this.getThisTypeOfSignature(target); if (targetThisType) { const related = compareTypes(sourceThisType, targetThisType); if (!related) { return Ternary.False; } result &= related; } } } const targetLen = target.parameters.length; for (let i = 0; i < targetLen; i++) { const s = this.isRestParameterIndex(source, i) ? this.getRestTypeOfSignature(source) : this.getTypeOfParameter(source.parameters[i]); const t = this.isRestParameterIndex(target, i) ? this.getRestTypeOfSignature(target) : this.getTypeOfParameter(target.parameters[i]); const related = compareTypes(s, t); if (!related) { return Ternary.False; } result &= related; } if (!ignoreReturnTypes) { result &= compareTypes(this.getReturnTypeOfSignature(source), this.getReturnTypeOfSignature(target)); } return result; } public isRestParameterIndex(signature: Signature, parameterIndex: number) { return signature.hasRestParameter && parameterIndex >= signature.parameters.length - 1; } public literalTypesWithSameBaseType(types: Type[]): boolean { let commonBaseType: Type; for (const t of types) { const baseType = this.getBaseTypeOfLiteralType(t); if (!commonBaseType) { commonBaseType = baseType; } if (baseType === t || baseType !== commonBaseType) { return false; } } return true; } // When the candidate types are all literal types with the same base type, return a union // of those literal types. Otherwise, return the leftmost type for which no type to the // right is a supertype. public getSupertypeOrUnion(types: Type[]): Type { const __conv_self__ = this; return this.literalTypesWithSameBaseType(types) ? this.getUnionType(types) : reduceLeft(types, (s, t) => __conv_self__.isTypeSubtypeOf(s, t) ? t : s); } public getCommonSupertype(types: Type[]): Type { if (!this.strictNullChecks) { return this.getSupertypeOrUnion(types); } const primaryTypes = filter(types, t => !(t.flags & TypeFlags.Nullable)); return primaryTypes.length ? this.getNullableType(this.getSupertypeOrUnion(primaryTypes), this.getFalsyFlagsOfTypes(types) & TypeFlags.Nullable) : this.getUnionType(types, /*subtypeReduction*/ true); } public isArrayType(type: Type): boolean { return this.getObjectFlags(type) & ObjectFlags.Reference && (type).target === this.globalArrayType; } public isArrayLikeType(type: Type): boolean { // A type is array-like if it is a reference to the global Array or global ReadonlyArray type, // or if it is not the undefined or null type and if it is assignable to ReadonlyArray return this.getObjectFlags(type) & ObjectFlags.Reference && ((type).target === this.globalArrayType || (type).target === this.globalReadonlyArrayType) || !(type.flags & TypeFlags.Nullable) && this.isTypeAssignableTo(type, this.anyReadonlyArrayType); } public isTupleLikeType(type: Type): boolean { return !!this.getPropertyOfType(type, ("0" as __String)); } public isUnitType(type: Type): boolean { return (type.flags & (TypeFlags.Literal | TypeFlags.Undefined | TypeFlags.Null)) !== 0; } public isLiteralType(type: Type): boolean { const __conv_self__ = this; return type.flags & TypeFlags.Boolean ? true : type.flags & TypeFlags.Union ? type.flags & TypeFlags.EnumLiteral ? true : !forEach((type).types, t => !__conv_self__.isUnitType(t)) : this.isUnitType(type); } public getBaseTypeOfLiteralType(type: Type): Type { return type.flags & TypeFlags.EnumLiteral ? this.getBaseTypeOfEnumLiteralType((type)) : type.flags & TypeFlags.StringLiteral ? this.stringType : type.flags & TypeFlags.NumberLiteral ? this.numberType : type.flags & TypeFlags.BooleanLiteral ? this.booleanType : type.flags & TypeFlags.Union ? this.getUnionType(sameMap((type).types, this.getBaseTypeOfLiteralType)) : type; } public getWidenedLiteralType(type: Type): Type { return type.flags & TypeFlags.EnumLiteral ? this.getBaseTypeOfEnumLiteralType((type)) : type.flags & TypeFlags.StringLiteral && type.flags & TypeFlags.FreshLiteral ? this.stringType : type.flags & TypeFlags.NumberLiteral && type.flags & TypeFlags.FreshLiteral ? this.numberType : type.flags & TypeFlags.BooleanLiteral ? this.booleanType : type.flags & TypeFlags.Union ? this.getUnionType(sameMap((type).types, this.getWidenedLiteralType)) : type; } /** * Check if a Type was written as a tuple type literal. * Prefer using isTupleLikeType() unless the use of `elementTypes` is required. */ public isTupleType(type: Type): boolean { return !!(this.getObjectFlags(type) & ObjectFlags.Reference && (type).target.objectFlags & ObjectFlags.Tuple); } public getFalsyFlagsOfTypes(types: Type[]): TypeFlags { let result: TypeFlags = 0; for (const t of types) { result |= this.getFalsyFlags(t); } return result; } // Returns the String, Number, Boolean, StringLiteral, NumberLiteral, BooleanLiteral, Void, Undefined, or Null // flags for the string, number, boolean, "", 0, false, void, undefined, or null types respectively. Returns // no flags for all other types (including non-falsy literal types). public getFalsyFlags(type: Type): TypeFlags { return type.flags & TypeFlags.Union ? this.getFalsyFlagsOfTypes((type).types) : type.flags & TypeFlags.StringLiteral ? (type).value === "" ? TypeFlags.StringLiteral : 0 : type.flags & TypeFlags.NumberLiteral ? (type).value === 0 ? TypeFlags.NumberLiteral : 0 : type.flags & TypeFlags.BooleanLiteral ? type === this.falseType ? TypeFlags.BooleanLiteral : 0 : type.flags & TypeFlags.PossiblyFalsy; } public removeDefinitelyFalsyTypes(type: Type): Type { const __conv_self__ = this; return this.getFalsyFlags(type) & TypeFlags.DefinitelyFalsy ? this.filterType(type, t => !(__conv_self__.getFalsyFlags(t) & TypeFlags.DefinitelyFalsy)) : type; } public extractDefinitelyFalsyTypes(type: Type): Type { return this.mapType(type, this.getDefinitelyFalsyPartOfType); } public getDefinitelyFalsyPartOfType(type: Type): Type { return type.flags & TypeFlags.String ? this.emptyStringType : type.flags & TypeFlags.Number ? this.zeroType : type.flags & TypeFlags.Boolean || type === this.falseType ? this.falseType : type.flags & (TypeFlags.Void | TypeFlags.Undefined | TypeFlags.Null) || type.flags & TypeFlags.StringLiteral && (type).value === "" || type.flags & TypeFlags.NumberLiteral && (type).value === 0 ? type : this.neverType; } /** * Add undefined or null or both to a type if they are missing. * @param type - type to add undefined and/or null to if not present * @param flags - Either TypeFlags.Undefined or TypeFlags.Null, or both */ public getNullableType(type: Type, flags: TypeFlags): Type { const missing = (flags & ~type.flags) & (TypeFlags.Undefined | TypeFlags.Null); return missing === 0 ? type : missing === TypeFlags.Undefined ? this.getUnionType([type, this.undefinedType]) : missing === TypeFlags.Null ? this.getUnionType([type, this.nullType]) : this.getUnionType([type, this.undefinedType, this.nullType]); } public getNonNullableType(type: Type): Type { return this.strictNullChecks ? this.getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull) : type; } /** * Return true if type was inferred from an object literal or written as an object type literal * with no call or construct signatures. */ public isObjectLiteralType(type: Type) { return type.symbol && (type.symbol.flags & (SymbolFlags.ObjectLiteral | SymbolFlags.TypeLiteral)) !== 0 && this.getSignaturesOfType(type, SignatureKind.Call).length === 0 && this.getSignaturesOfType(type, SignatureKind.Construct).length === 0; } public createSymbolWithType(source: Symbol, type: Type) { const symbol = this.createSymbol(source.flags, source.escapedName); symbol.declarations = source.declarations; symbol.parent = source.parent; symbol.type = type; symbol.target = source; if (source.valueDeclaration) { symbol.valueDeclaration = source.valueDeclaration; } return symbol; } public transformTypeOfMembers(type: Type, f: (propertyType: Type) => Type) { const members = createSymbolTable(); for (const property of this.getPropertiesOfObjectType(type)) { const original = this.getTypeOfSymbol(property); const updated = f(original); members.set(property.escapedName, updated === original ? property : this.createSymbolWithType(property, updated)); } return members; } /** * If the the provided object literal is subject to the excess properties check, * create a new that is exempt. Recursively mark object literal members as exempt. * Leave signatures alone since they are not subject to the check. */ public getRegularTypeOfObjectLiteral(type: Type): Type { if (!(this.getObjectFlags(type) & ObjectFlags.ObjectLiteral && type.flags & TypeFlags.FreshLiteral)) { return type; } const regularType = (type).regularType; if (regularType) { return regularType; } const resolved = type; const members = this.transformTypeOfMembers(type, this.getRegularTypeOfObjectLiteral); const regularNew = this.createAnonymousType(resolved.symbol, members, resolved.callSignatures, resolved.constructSignatures, resolved.stringIndexInfo, resolved.numberIndexInfo); regularNew.flags = resolved.flags & ~TypeFlags.FreshLiteral; regularNew.objectFlags |= ObjectFlags.ObjectLiteral; (type).regularType = regularNew; return regularNew; } public getWidenedProperty(prop: Symbol): Symbol { const original = this.getTypeOfSymbol(prop); const widened = this.getWidenedType(original); return widened === original ? prop : this.createSymbolWithType(prop, widened); } public getWidenedTypeOfObjectLiteral(type: Type): Type { const members = createSymbolTable(); for (const prop of this.getPropertiesOfObjectType(type)) { // Since get accessors already widen their return value there is no need to // widen accessor based properties here. members.set(prop.escapedName, prop.flags & SymbolFlags.Property ? this.getWidenedProperty(prop) : prop); } const stringIndexInfo = this.getIndexInfoOfType(type, IndexKind.String); const numberIndexInfo = this.getIndexInfoOfType(type, IndexKind.Number); return this.createAnonymousType(type.symbol, members, emptyArray, emptyArray, stringIndexInfo && this.createIndexInfo(this.getWidenedType(stringIndexInfo.type), stringIndexInfo.isReadonly), numberIndexInfo && this.createIndexInfo(this.getWidenedType(numberIndexInfo.type), numberIndexInfo.isReadonly)); } public getWidenedConstituentType(type: Type): Type { return type.flags & TypeFlags.Nullable ? type : this.getWidenedType(type); } public getWidenedType(type: Type): Type { if (type.flags & TypeFlags.RequiresWidening) { if (type.flags & TypeFlags.Nullable) { return this.anyType; } if (this.getObjectFlags(type) & ObjectFlags.ObjectLiteral) { return this.getWidenedTypeOfObjectLiteral(type); } if (type.flags & TypeFlags.Union) { return this.getUnionType(sameMap((type).types, this.getWidenedConstituentType)); } if (this.isArrayType(type) || this.isTupleType(type)) { return this.createTypeReference((type).target, sameMap((type).typeArguments, this.getWidenedType)); } } return type; } /** * Reports implicit any errors that occur as a result of widening 'null' and 'undefined' * to 'any'. A call to reportWideningErrorsInType is normally accompanied by a call to * getWidenedType. But in some cases getWidenedType is called without reporting errors * (type argument inference is an example). * * The return value indicates whether an error was in fact reported. The particular circumstances * are on a best effort basis. Currently, if the null or undefined that causes widening is inside * an object literal property (arbitrarily deeply), this function reports an error. If no error is * reported, reportImplicitAnyError is a suitable fallback to report a general error. */ public reportWideningErrorsInType(type: Type): boolean { let errorReported = false; if (type.flags & TypeFlags.Union) { for (const t of (type).types) { if (this.reportWideningErrorsInType(t)) { errorReported = true; } } } if (this.isArrayType(type) || this.isTupleType(type)) { for (const t of (type).typeArguments) { if (this.reportWideningErrorsInType(t)) { errorReported = true; } } } if (this.getObjectFlags(type) & ObjectFlags.ObjectLiteral) { for (const p of this.getPropertiesOfObjectType(type)) { const t = this.getTypeOfSymbol(p); if (t.flags & TypeFlags.ContainsWideningType) { if (!this.reportWideningErrorsInType(t)) { this.error(p.valueDeclaration, Diagnostics.Object_literal_s_property_0_implicitly_has_an_1_type, unescapeLeadingUnderscores(p.escapedName), this.typeToString(this.getWidenedType(t))); } errorReported = true; } } } return errorReported; } public reportImplicitAnyError(declaration: Declaration, type: Type) { const typeAsString = this.typeToString(this.getWidenedType(type)); let diagnostic: DiagnosticMessage; switch (declaration.kind) { case SyntaxKind.PropertyDeclaration: case SyntaxKind.PropertySignature: diagnostic = Diagnostics.Member_0_implicitly_has_an_1_type; break; case SyntaxKind.Parameter: diagnostic = (declaration).dotDotDotToken ? Diagnostics.Rest_parameter_0_implicitly_has_an_any_type : Diagnostics.Parameter_0_implicitly_has_an_1_type; break; case SyntaxKind.BindingElement: diagnostic = Diagnostics.Binding_element_0_implicitly_has_an_1_type; break; case SyntaxKind.FunctionDeclaration: case SyntaxKind.MethodDeclaration: case SyntaxKind.MethodSignature: case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: if (!(declaration as NamedDeclaration).name) { this.error(declaration, Diagnostics.Function_expression_which_lacks_return_type_annotation_implicitly_has_an_0_return_type, typeAsString); return; } diagnostic = Diagnostics._0_which_lacks_return_type_annotation_implicitly_has_an_1_return_type; break; default: diagnostic = Diagnostics.Variable_0_implicitly_has_an_1_type; } this.error(declaration, diagnostic, declarationNameToString(getNameOfDeclaration(declaration)), typeAsString); } public reportErrorsFromWidening(declaration: Declaration, type: Type) { if (this.produceDiagnostics && this.noImplicitAny && type.flags & TypeFlags.ContainsWideningType) { // Report implicit any error within type if possible, otherwise report error on declaration if (!this.reportWideningErrorsInType(type)) { this.reportImplicitAnyError(declaration, type); } } } public forEachMatchingParameterType(source: Signature, target: Signature, callback: (s: Type, t: Type) => void) { const sourceMax = source.parameters.length; const targetMax = target.parameters.length; let count: number; if (source.hasRestParameter && target.hasRestParameter) { count = Math.max(sourceMax, targetMax); } else if (source.hasRestParameter) { count = targetMax; } else if (target.hasRestParameter) { count = sourceMax; } else { count = Math.min(sourceMax, targetMax); } for (let i = 0; i < count; i++) { callback(this.getTypeAtPosition(source, i), this.getTypeAtPosition(target, i)); } } public createInferenceContext(signature: Signature, flags: InferenceFlags, compareTypes?: TypeComparer, baseInferences?: InferenceInfo[]): InferenceContext { const __conv_self__ = this; const inferences = baseInferences ? map(baseInferences, this.cloneInferenceInfo) : map(signature.typeParameters, this.createInferenceInfo); const context = mapper as InferenceContext; context.mappedTypes = signature.typeParameters; context.signature = signature; context.inferences = inferences; context.flags = flags; context.compareTypes = compareTypes || this.compareTypesAssignable; return context; function mapper(t: Type): Type { for (let i = 0; i < inferences.length; i++) { if (t === inferences[i].typeParameter) { inferences[i].isFixed = true; return __conv_self__.getInferredType(context, i); } } return t; } } public createInferenceInfo(typeParameter: TypeParameter): InferenceInfo { return { typeParameter, candidates: undefined, inferredType: undefined, priority: undefined, topLevel: true, isFixed: false }; } public cloneInferenceInfo(inference: InferenceInfo): InferenceInfo { return { typeParameter: inference.typeParameter, candidates: inference.candidates && inference.candidates.slice(), inferredType: inference.inferredType, priority: inference.priority, topLevel: inference.topLevel, isFixed: inference.isFixed }; } // Return true if the given type could possibly reference a type parameter for which // we perform type inference (i.e. a type parameter of a generic function). We cache // results for union and intersection types for performance reasons. public couldContainTypeVariables(type: Type): boolean { const objectFlags = this.getObjectFlags(type); return !!(type.flags & TypeFlags.TypeVariable || objectFlags & ObjectFlags.Reference && forEach((type).typeArguments, this.couldContainTypeVariables) || objectFlags & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.TypeLiteral | SymbolFlags.Class) || objectFlags & ObjectFlags.Mapped || type.flags & TypeFlags.UnionOrIntersection && this.couldUnionOrIntersectionContainTypeVariables((type))); } public couldUnionOrIntersectionContainTypeVariables(type: UnionOrIntersectionType): boolean { if (type.couldContainTypeVariables === undefined) { type.couldContainTypeVariables = forEach(type.types, this.couldContainTypeVariables); } return type.couldContainTypeVariables; } public isTypeParameterAtTopLevel(type: Type, typeParameter: TypeParameter): boolean { const __conv_self__ = this; return type === typeParameter || type.flags & TypeFlags.UnionOrIntersection && forEach((type).types, t => __conv_self__.isTypeParameterAtTopLevel(t, typeParameter)); } // Infer a suitable input type for a homomorphic mapped type { [P in keyof T]: X }. We construct // an object type with the same set of properties as the source type, where the type of each // property is computed by inferring from the source property type to X for the type // variable T[P] (i.e. we treat the type T[P] as the type variable we're inferring for). public inferTypeForHomomorphicMappedType(source: Type, target: MappedType): Type { const __conv_self__ = this; const properties = this.getPropertiesOfType(source); let indexInfo = this.getIndexInfoOfType(source, IndexKind.String); if (properties.length === 0 && !indexInfo) { return undefined; } const typeParameter = (this.getIndexedAccessType((this.getConstraintTypeFromMappedType(target)).type, this.getTypeParameterFromMappedType(target))); const inference = this.createInferenceInfo(typeParameter); const inferences = [inference]; const templateType = this.getTemplateTypeFromMappedType(target); const readonlyMask = target.declaration.readonlyToken ? false : true; const optionalMask = target.declaration.questionToken ? 0 : SymbolFlags.Optional; const members = createSymbolTable(); for (const prop of properties) { const inferredPropType = inferTargetType(this.getTypeOfSymbol(prop)); if (!inferredPropType) { return undefined; } const inferredProp = this.createSymbol(SymbolFlags.Property | prop.flags & optionalMask, prop.escapedName); inferredProp.checkFlags = readonlyMask && this.isReadonlySymbol(prop) ? CheckFlags.Readonly : 0; inferredProp.declarations = prop.declarations; inferredProp.type = inferredPropType; members.set(prop.escapedName, inferredProp); } if (indexInfo) { const inferredIndexType = inferTargetType(indexInfo.type); if (!inferredIndexType) { return undefined; } indexInfo = this.createIndexInfo(inferredIndexType, readonlyMask && indexInfo.isReadonly); } return this.createAnonymousType(undefined, members, emptyArray, emptyArray, indexInfo, undefined); function inferTargetType(sourceType: Type): Type { inference.candidates = undefined; __conv_self__.inferTypes(inferences, sourceType, templateType); return inference.candidates && __conv_self__.getUnionType(inference.candidates, /*subtypeReduction*/ true); } } public isPossiblyAssignableTo(source: Type, target: Type) { const properties = this.getPropertiesOfObjectType(target); for (const targetProp of properties) { if (!(targetProp.flags & (SymbolFlags.Optional | SymbolFlags.Prototype))) { const sourceProp = this.getPropertyOfObjectType(source, targetProp.escapedName); if (!sourceProp) { return false; } } } return true; } public inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0) { const __conv_self__ = this; let symbolStack: Symbol[]; let visited: Map; inferFromTypes(originalSource, originalTarget); function inferFromTypes(source: Type, target: Type) { if (!__conv_self__.couldContainTypeVariables(target)) { return; } if (source.aliasSymbol && source.aliasTypeArguments && source.aliasSymbol === target.aliasSymbol) { // Source and target are types originating in the same generic type alias declaration. // Simply infer from source type arguments to target type arguments. const sourceTypes = source.aliasTypeArguments; const targetTypes = target.aliasTypeArguments; for (let i = 0; i < sourceTypes.length; i++) { inferFromTypes(sourceTypes[i], targetTypes[i]); } return; } if (source.flags & TypeFlags.Union && target.flags & TypeFlags.Union && !(source.flags & TypeFlags.EnumLiteral && target.flags & TypeFlags.EnumLiteral) || source.flags & TypeFlags.Intersection && target.flags & TypeFlags.Intersection) { // Source and target are both unions or both intersections. If source and target // are the same type, just relate each constituent type to itself. if (source === target) { for (const t of (source).types) { inferFromTypes(t, t); } return; } // Find each source constituent type that has an identically matching target constituent // type, and for each such type infer from the type to itself. When inferring from a // type to itself we effectively find all type parameter occurrences within that type // and infer themselves as their type arguments. We have special handling for numeric // and string literals because the number and string types are not represented as unions // of all their possible values. let matchingTypes: Type[]; for (const t of (source).types) { if (__conv_self__.typeIdenticalToSomeType(t, (target).types)) { (matchingTypes || (matchingTypes = [])).push(t); inferFromTypes(t, t); } else if (t.flags & (TypeFlags.NumberLiteral | TypeFlags.StringLiteral)) { const b = __conv_self__.getBaseTypeOfLiteralType(t); if (__conv_self__.typeIdenticalToSomeType(b, (target).types)) { (matchingTypes || (matchingTypes = [])).push(t, b); } } } // Next, to improve the quality of inferences, reduce the source and target types by // removing the identically matched constituents. For example, when inferring from // 'string | string[]' to 'string | T' we reduce the types to 'string[]' and 'T'. if (matchingTypes) { source = __conv_self__.removeTypesFromUnionOrIntersection((source), matchingTypes); target = __conv_self__.removeTypesFromUnionOrIntersection((target), matchingTypes); } } if (target.flags & TypeFlags.TypeVariable) { // If target is a type parameter, make an inference, unless the source type contains // the anyFunctionType (the wildcard type that's used to avoid contextually typing functions). // Because the anyFunctionType is internal, it should not be exposed to the user by adding // it as an inference candidate. Hopefully, a better candidate will come along that does // not contain anyFunctionType when we come back to this argument for its second round // of inference. Also, we exclude inferences for silentNeverType which is used as a wildcard // when constructing types from type parameters that had no inference candidates. if (source.flags & TypeFlags.ContainsAnyFunctionType || source === __conv_self__.silentNeverType) { return; } const inference = getInferenceInfoForType(target); if (inference) { if (!inference.isFixed) { if (!inference.candidates || priority < inference.priority) { inference.candidates = [source]; inference.priority = priority; } else if (priority === inference.priority) { inference.candidates.push(source); } if (!(priority & InferencePriority.ReturnType) && target.flags & TypeFlags.TypeParameter && !__conv_self__.isTypeParameterAtTopLevel(originalTarget, (target))) { inference.topLevel = false; } } return; } } else if (__conv_self__.getObjectFlags(source) & ObjectFlags.Reference && __conv_self__.getObjectFlags(target) & ObjectFlags.Reference && (source).target === (target).target) { // If source and target are references to the same generic type, infer from type arguments const sourceTypes = (source).typeArguments || emptyArray; const targetTypes = (target).typeArguments || emptyArray; const count = sourceTypes.length < targetTypes.length ? sourceTypes.length : targetTypes.length; for (let i = 0; i < count; i++) { inferFromTypes(sourceTypes[i], targetTypes[i]); } } else if (target.flags & TypeFlags.UnionOrIntersection) { const targetTypes = (target).types; let typeVariableCount = 0; let typeVariable: TypeVariable; // First infer to each type in union or intersection that isn't a type variable for (const t of targetTypes) { if (getInferenceInfoForType(t)) { typeVariable = t; typeVariableCount++; } else { inferFromTypes(source, t); } } // Next, if target containings a single naked type variable, make a secondary inference to that type // variable. This gives meaningful results for union types in co-variant positions and intersection // types in contra-variant positions (such as callback parameters). if (typeVariableCount === 1) { const savePriority = priority; priority |= InferencePriority.NakedTypeVariable; inferFromTypes(source, typeVariable); priority = savePriority; } } else if (source.flags & TypeFlags.UnionOrIntersection) { // Source is a union or intersection type, infer from each constituent type const sourceTypes = (source).types; for (const sourceType of sourceTypes) { inferFromTypes(sourceType, target); } } else { source = __conv_self__.getApparentType(source); if (source.flags & TypeFlags.Object) { const key = source.id + "," + target.id; if (visited && visited.get(key)) { return; } (visited || (visited = createMap())).set(key, true); // If we are already processing another target type with the same associated symbol (such as // an instantiation of the same generic type), we do not explore this target as it would yield // no further inferences. We exclude the static side of classes from this check since it shares // its symbol with the instance side which would lead to false positives. const isNonConstructorObject = target.flags & TypeFlags.Object && !(__conv_self__.getObjectFlags(target) & ObjectFlags.Anonymous && target.symbol && target.symbol.flags & SymbolFlags.Class); const symbol = isNonConstructorObject ? target.symbol : undefined; if (symbol) { if (contains(symbolStack, symbol)) { return; } (symbolStack || (symbolStack = [])).push(symbol); inferFromObjectTypes(source, target); symbolStack.pop(); } else { inferFromObjectTypes(source, target); } } } } function getInferenceInfoForType(type: Type) { if (type.flags & TypeFlags.TypeVariable) { for (const inference of inferences) { if (type === inference.typeParameter) { return inference; } } } return undefined; } function inferFromObjectTypes(source: Type, target: Type) { if (__conv_self__.getObjectFlags(target) & ObjectFlags.Mapped) { const constraintType = __conv_self__.getConstraintTypeFromMappedType((target)); if (constraintType.flags & TypeFlags.Index) { // We're inferring from some source type S to a homomorphic mapped type { [P in keyof T]: X }, // where T is a type variable. Use inferTypeForHomomorphicMappedType to infer a suitable source // type and then make a secondary inference from that type to T. We make a secondary inference // such that direct inferences to T get priority over inferences to Partial, for example. const inference = getInferenceInfoForType((constraintType).type); if (inference && !inference.isFixed) { const inferredType = __conv_self__.inferTypeForHomomorphicMappedType(source, (target)); if (inferredType) { const savePriority = priority; priority |= InferencePriority.MappedType; inferFromTypes(inferredType, inference.typeParameter); priority = savePriority; } } return; } if (constraintType.flags & TypeFlags.TypeParameter) { // We're inferring from some source type S to a mapped type { [P in T]: X }, where T is a type // parameter. Infer from 'keyof S' to T and infer from a union of each property type in S to X. inferFromTypes(__conv_self__.getIndexType(source), constraintType); inferFromTypes(__conv_self__.getUnionType(map(__conv_self__.getPropertiesOfType(source), __conv_self__.getTypeOfSymbol)), __conv_self__.getTemplateTypeFromMappedType((target))); return; } } // Infer from the members of source and target only if the two types are possibly related. We check // in both directions because we may be inferring for a co-variant or a contra-variant position. if (__conv_self__.isPossiblyAssignableTo(source, target) || __conv_self__.isPossiblyAssignableTo(target, source)) { inferFromProperties(source, target); inferFromSignatures(source, target, SignatureKind.Call); inferFromSignatures(source, target, SignatureKind.Construct); inferFromIndexTypes(source, target); } } function inferFromProperties(source: Type, target: Type) { const properties = __conv_self__.getPropertiesOfObjectType(target); for (const targetProp of properties) { const sourceProp = __conv_self__.getPropertyOfObjectType(source, targetProp.escapedName); if (sourceProp) { inferFromTypes(__conv_self__.getTypeOfSymbol(sourceProp), __conv_self__.getTypeOfSymbol(targetProp)); } } } function inferFromSignatures(source: Type, target: Type, kind: SignatureKind) { const sourceSignatures = __conv_self__.getSignaturesOfType(source, kind); const targetSignatures = __conv_self__.getSignaturesOfType(target, kind); const sourceLen = sourceSignatures.length; const targetLen = targetSignatures.length; const len = sourceLen < targetLen ? sourceLen : targetLen; for (let i = 0; i < len; i++) { inferFromSignature(__conv_self__.getErasedSignature(sourceSignatures[sourceLen - len + i]), __conv_self__.getErasedSignature(targetSignatures[targetLen - len + i])); } } function inferFromSignature(source: Signature, target: Signature) { __conv_self__.forEachMatchingParameterType(source, target, inferFromTypes); if (source.typePredicate && target.typePredicate && source.typePredicate.kind === target.typePredicate.kind) { inferFromTypes(source.typePredicate.type, target.typePredicate.type); } else { inferFromTypes(__conv_self__.getReturnTypeOfSignature(source), __conv_self__.getReturnTypeOfSignature(target)); } } function inferFromIndexTypes(source: Type, target: Type) { const targetStringIndexType = __conv_self__.getIndexTypeOfType(target, IndexKind.String); if (targetStringIndexType) { const sourceIndexType = __conv_self__.getIndexTypeOfType(source, IndexKind.String) || __conv_self__.getImplicitIndexTypeOfType(source, IndexKind.String); if (sourceIndexType) { inferFromTypes(sourceIndexType, targetStringIndexType); } } const targetNumberIndexType = __conv_self__.getIndexTypeOfType(target, IndexKind.Number); if (targetNumberIndexType) { const sourceIndexType = __conv_self__.getIndexTypeOfType(source, IndexKind.Number) || __conv_self__.getIndexTypeOfType(source, IndexKind.String) || __conv_self__.getImplicitIndexTypeOfType(source, IndexKind.Number); if (sourceIndexType) { inferFromTypes(sourceIndexType, targetNumberIndexType); } } } } public typeIdenticalToSomeType(type: Type, types: Type[]): boolean { for (const t of types) { if (this.isTypeIdenticalTo(t, type)) { return true; } } return false; } /** * Return a new union or intersection type computed by removing a given set of types * from a given union or intersection type. */ public removeTypesFromUnionOrIntersection(type: UnionOrIntersectionType, typesToRemove: Type[]) { const reducedTypes: Type[] = []; for (const t of type.types) { if (!this.typeIdenticalToSomeType(t, typesToRemove)) { reducedTypes.push(t); } } return type.flags & TypeFlags.Union ? this.getUnionType(reducedTypes) : this.getIntersectionType(reducedTypes); } public hasPrimitiveConstraint(type: TypeParameter): boolean { const constraint = this.getConstraintOfTypeParameter(type); return constraint && this.maybeTypeOfKind(constraint, TypeFlags.Primitive | TypeFlags.Index); } public getInferredType(context: InferenceContext, index: number): Type { const inference = context.inferences[index]; let inferredType = inference.inferredType; if (!inferredType) { if (inference.candidates) { // We widen inferred literal types if // all inferences were made to top-level ocurrences of the type parameter, and // the type parameter has no constraint or its constraint includes no primitive or literal types, and // the type parameter was fixed during inference or does not occur at top-level in the return type. const signature = context.signature; const widenLiteralTypes = inference.topLevel && !this.hasPrimitiveConstraint(inference.typeParameter) && (inference.isFixed || !this.isTypeParameterAtTopLevel(this.getReturnTypeOfSignature(signature), inference.typeParameter)); const baseCandidates = widenLiteralTypes ? sameMap(inference.candidates, this.getWidenedLiteralType) : inference.candidates; // Infer widened union or supertype, or the unknown type for no common supertype. We infer union types // for inferences coming from return types in order to avoid common supertype failures. const unionOrSuperType = context.flags & InferenceFlags.InferUnionTypes || inference.priority & InferencePriority.ReturnType ? this.getUnionType(baseCandidates, /*subtypeReduction*/ true) : this.getCommonSupertype(baseCandidates); inferredType = this.getWidenedType(unionOrSuperType); } else if (context.flags & InferenceFlags.NoDefault) { // We use silentNeverType as the wildcard that signals no inferences. inferredType = this.silentNeverType; } else { // Infer either the default or the empty object type when no inferences were // made. It is important to remember that in this case, inference still // succeeds, meaning there is no error for not having inference candidates. An // inference error only occurs when there are *conflicting* candidates, i.e. // candidates with no common supertype. const defaultType = this.getDefaultFromTypeParameter(inference.typeParameter); if (defaultType) { // Instantiate the default type. Any forward reference to a type // parameter should be instantiated to the empty object type. inferredType = this.instantiateType(defaultType, this.combineTypeMappers(this.createBackreferenceMapper(context.signature.typeParameters, index), context)); } else { inferredType = this.getDefaultTypeArgumentType(!!(context.flags & InferenceFlags.AnyDefault)); } } inference.inferredType = inferredType; const constraint = this.getConstraintOfTypeParameter(context.signature.typeParameters[index]); if (constraint) { const instantiatedConstraint = this.instantiateType(constraint, context); if (!context.compareTypes(inferredType, this.getTypeWithThisArgument(instantiatedConstraint, inferredType))) { inference.inferredType = inferredType = instantiatedConstraint; } } } return inferredType; } public getDefaultTypeArgumentType(isInJavaScriptFile: boolean): Type { return isInJavaScriptFile ? this.anyType : this.emptyObjectType; } public getInferredTypes(context: InferenceContext): Type[] { const result: Type[] = []; for (let i = 0; i < context.inferences.length; i++) { result.push(this.getInferredType(context, i)); } return result; } // EXPRESSION TYPE CHECKING public getResolvedSymbol(node: Identifier): Symbol { const links = this.getNodeLinks(node); if (!links.resolvedSymbol) { links.resolvedSymbol = !nodeIsMissing(node) && this.resolveName(node, node.escapedText, SymbolFlags.Value | SymbolFlags.ExportValue, Diagnostics.Cannot_find_name_0, node, Diagnostics.Cannot_find_name_0_Did_you_mean_1) || this.unknownSymbol; } return links.resolvedSymbol; } public isInTypeQuery(node: Node): boolean { // TypeScript 1.0 spec (April 2014): 3.6.3 // A type query consists of the keyword typeof followed by an expression. // The expression is restricted to a single identifier or a sequence of identifiers separated by periods return !!findAncestor(node, n => n.kind === SyntaxKind.TypeQuery ? true : n.kind === SyntaxKind.Identifier || n.kind === SyntaxKind.QualifiedName ? false : "quit"); } // Return the flow cache key for a "dotted name" (i.e. a sequence of identifiers // separated by dots). The key consists of the id of the symbol referenced by the // leftmost identifier followed by zero or more property names separated by dots. // The result is undefined if the reference isn't a dotted name. We prefix nodes // occurring in an apparent type position with '@' because the control flow type // of such nodes may be based on the apparent type instead of the declared type. public getFlowCacheKey(node: Node): string | undefined { if (node.kind === SyntaxKind.Identifier) { const symbol = this.getResolvedSymbol((node)); return symbol !== this.unknownSymbol ? (this.isApparentTypePosition(node) ? "@" : "") + getSymbolId(symbol) : undefined; } if (node.kind === SyntaxKind.ThisKeyword) { return "0"; } if (node.kind === SyntaxKind.PropertyAccessExpression) { const key = this.getFlowCacheKey((node).expression); return key && key + "." + unescapeLeadingUnderscores((node).name.escapedText); } if (node.kind === SyntaxKind.BindingElement) { const container = (node as BindingElement).parent.parent; const key = container.kind === SyntaxKind.BindingElement ? this.getFlowCacheKey(container) : (container.initializer && this.getFlowCacheKey(container.initializer)); const text = this.getBindingElementNameText((node as BindingElement)); const result = key && text && (key + "." + text); return result; } return undefined; } public getBindingElementNameText(element: BindingElement): string | undefined { if (element.parent.kind === SyntaxKind.ObjectBindingPattern) { const name = element.propertyName || element.name; switch (name.kind) { case SyntaxKind.Identifier: return unescapeLeadingUnderscores(name.escapedText); case SyntaxKind.ComputedPropertyName: return isStringOrNumericLiteral(name.expression) ? name.expression.text : undefined; case SyntaxKind.StringLiteral: case SyntaxKind.NumericLiteral: return name.text; default: // Per types, array and object binding patterns remain, however they should never be present if propertyName is not defined Debug.fail("Unexpected name kind for binding element name"); } } else { return "" + element.parent.elements.indexOf(element); } } public isMatchingReference(source: Node, target: Node): boolean { switch (source.kind) { case SyntaxKind.Identifier: return target.kind === SyntaxKind.Identifier && this.getResolvedSymbol((source)) === this.getResolvedSymbol((target)) || (target.kind === SyntaxKind.VariableDeclaration || target.kind === SyntaxKind.BindingElement) && this.getExportSymbolOfValueSymbolIfExported(this.getResolvedSymbol((source))) === this.getSymbolOfNode(target); case SyntaxKind.ThisKeyword: return target.kind === SyntaxKind.ThisKeyword; case SyntaxKind.SuperKeyword: return target.kind === SyntaxKind.SuperKeyword; case SyntaxKind.PropertyAccessExpression: return target.kind === SyntaxKind.PropertyAccessExpression && (source).name.escapedText === (target).name.escapedText && this.isMatchingReference((source).expression, (target).expression); case SyntaxKind.BindingElement: if (target.kind !== SyntaxKind.PropertyAccessExpression) return false; const t = target as PropertyAccessExpression; if (t.name.escapedText !== this.getBindingElementNameText((source as BindingElement))) return false; if (source.parent.parent.kind === SyntaxKind.BindingElement && this.isMatchingReference(source.parent.parent, t.expression)) { return true; } if (source.parent.parent.kind === SyntaxKind.VariableDeclaration) { const maybeId = (source.parent.parent as VariableDeclaration).initializer; return maybeId && this.isMatchingReference(maybeId, t.expression); } } return false; } public containsMatchingReference(source: Node, target: Node) { while (source.kind === SyntaxKind.PropertyAccessExpression) { source = (source).expression; if (this.isMatchingReference(source, target)) { return true; } } return false; } // Return true if target is a property access xxx.yyy, source is a property access xxx.zzz, the declared // type of xxx is a union type, and yyy is a property that is possibly a discriminant. We consider a property // a possible discriminant if its type differs in the constituents of containing union type, and if every // choice is a unit type or a union of unit types. public containsMatchingReferenceDiscriminant(source: Node, target: Node) { return target.kind === SyntaxKind.PropertyAccessExpression && this.containsMatchingReference(source, (target).expression) && this.isDiscriminantProperty(this.getDeclaredTypeOfReference((target).expression), (target).name.escapedText); } public getDeclaredTypeOfReference(expr: Node): Type { if (expr.kind === SyntaxKind.Identifier) { return this.getTypeOfSymbol(this.getResolvedSymbol((expr))); } if (expr.kind === SyntaxKind.PropertyAccessExpression) { const type = this.getDeclaredTypeOfReference((expr).expression); return type && this.getTypeOfPropertyOfType(type, (expr).name.escapedText); } return undefined; } public isDiscriminantProperty(type: Type, name: __String) { if (type && type.flags & TypeFlags.Union) { const prop = this.getUnionOrIntersectionProperty((type), name); if (prop && getCheckFlags(prop) & CheckFlags.SyntheticProperty) { if ((prop).isDiscriminantProperty === undefined) { (prop).isDiscriminantProperty = (prop).checkFlags & CheckFlags.HasNonUniformType && this.isLiteralType(this.getTypeOfSymbol(prop)); } return (prop).isDiscriminantProperty; } } return false; } public isOrContainsMatchingReference(source: Node, target: Node) { return this.isMatchingReference(source, target) || this.containsMatchingReference(source, target); } public hasMatchingArgument(callExpression: CallExpression, reference: Node) { if (callExpression.arguments) { for (const argument of callExpression.arguments) { if (this.isOrContainsMatchingReference(reference, argument)) { return true; } } } if (callExpression.expression.kind === SyntaxKind.PropertyAccessExpression && this.isOrContainsMatchingReference(reference, (callExpression.expression).expression)) { return true; } return false; } public getFlowNodeId(flow: FlowNode): number { if (!flow.id) { flow.id = nextFlowId; nextFlowId++; } return flow.id; } public typeMaybeAssignableTo(source: Type, target: Type) { if (!(source.flags & TypeFlags.Union)) { return this.isTypeAssignableTo(source, target); } for (const t of (source).types) { if (this.isTypeAssignableTo(t, target)) { return true; } } return false; } // Remove those constituent types of declaredType to which no constituent type of assignedType is assignable. // For example, when a variable of type number | string | boolean is assigned a value of type number | boolean, // we remove type string. public getAssignmentReducedType(declaredType: UnionType, assignedType: Type) { const __conv_self__ = this; if (declaredType !== assignedType) { if (assignedType.flags & TypeFlags.Never) { return assignedType; } const reducedType = this.filterType(declaredType, t => __conv_self__.typeMaybeAssignableTo(assignedType, t)); if (!(reducedType.flags & TypeFlags.Never)) { return reducedType; } } return declaredType; } public getTypeFactsOfTypes(types: Type[]): TypeFacts { let result: TypeFacts = TypeFacts.None; for (const t of types) { result |= this.getTypeFacts(t); } return result; } public isFunctionObjectType(type: ObjectType): boolean { // We do a quick check for a "bind" property before performing the more expensive subtype // check. This gives us a quicker out in the common case where an object type is not a function. const resolved = this.resolveStructuredTypeMembers(type); return !!(resolved.callSignatures.length || resolved.constructSignatures.length || resolved.members.get("bind" as __String) && this.isTypeSubtypeOf(type, this.globalFunctionType)); } public getTypeFacts(type: Type): TypeFacts { const flags = type.flags; if (flags & TypeFlags.String) { return this.strictNullChecks ? TypeFacts.StringStrictFacts : TypeFacts.StringFacts; } if (flags & TypeFlags.StringLiteral) { const isEmpty = (type).value === ""; return this.strictNullChecks ? isEmpty ? TypeFacts.EmptyStringStrictFacts : TypeFacts.NonEmptyStringStrictFacts : isEmpty ? TypeFacts.EmptyStringFacts : TypeFacts.NonEmptyStringFacts; } if (flags & (TypeFlags.Number | TypeFlags.Enum)) { return this.strictNullChecks ? TypeFacts.NumberStrictFacts : TypeFacts.NumberFacts; } if (flags & TypeFlags.NumberLiteral) { const isZero = (type).value === 0; return this.strictNullChecks ? isZero ? TypeFacts.ZeroStrictFacts : TypeFacts.NonZeroStrictFacts : isZero ? TypeFacts.ZeroFacts : TypeFacts.NonZeroFacts; } if (flags & TypeFlags.Boolean) { return this.strictNullChecks ? TypeFacts.BooleanStrictFacts : TypeFacts.BooleanFacts; } if (flags & TypeFlags.BooleanLike) { return this.strictNullChecks ? type === this.falseType ? TypeFacts.FalseStrictFacts : TypeFacts.TrueStrictFacts : type === this.falseType ? TypeFacts.FalseFacts : TypeFacts.TrueFacts; } if (flags & TypeFlags.Object) { return this.isFunctionObjectType((type)) ? this.strictNullChecks ? TypeFacts.FunctionStrictFacts : TypeFacts.FunctionFacts : this.strictNullChecks ? TypeFacts.ObjectStrictFacts : TypeFacts.ObjectFacts; } if (flags & (TypeFlags.Void | TypeFlags.Undefined)) { return TypeFacts.UndefinedFacts; } if (flags & TypeFlags.Null) { return TypeFacts.NullFacts; } if (flags & TypeFlags.ESSymbol) { return this.strictNullChecks ? TypeFacts.SymbolStrictFacts : TypeFacts.SymbolFacts; } if (flags & TypeFlags.NonPrimitive) { return this.strictNullChecks ? TypeFacts.ObjectStrictFacts : TypeFacts.ObjectFacts; } if (flags & TypeFlags.TypeVariable) { return this.getTypeFacts(this.getBaseConstraintOfType(type) || this.emptyObjectType); } if (flags & TypeFlags.UnionOrIntersection) { return this.getTypeFactsOfTypes((type).types); } return TypeFacts.All; } public getTypeWithFacts(type: Type, include: TypeFacts) { const __conv_self__ = this; return this.filterType(type, t => (__conv_self__.getTypeFacts(t) & include) !== 0); } public getTypeWithDefault(type: Type, defaultExpression: Expression) { if (defaultExpression) { const defaultType = this.getTypeOfExpression(defaultExpression); return this.getUnionType([this.getTypeWithFacts(type, TypeFacts.NEUndefined), defaultType]); } return type; } public getTypeOfDestructuredProperty(type: Type, name: PropertyName) { const text = getTextOfPropertyName(name); return this.getTypeOfPropertyOfType(type, text) || this.isNumericLiteralName(text) && this.getIndexTypeOfType(type, IndexKind.Number) || this.getIndexTypeOfType(type, IndexKind.String) || this.unknownType; } public getTypeOfDestructuredArrayElement(type: Type, index: number) { return this.isTupleLikeType(type) && this.getTypeOfPropertyOfType(type, ("" + index as __String)) || this.checkIteratedTypeOrElementType(type, /*errorNode*/ undefined, /*allowStringInput*/ false, /*allowAsyncIterables*/ false) || this.unknownType; } public getTypeOfDestructuredSpreadExpression(type: Type) { return this.createArrayType(this.checkIteratedTypeOrElementType(type, /*errorNode*/ undefined, /*allowStringInput*/ false, /*allowAsyncIterables*/ false) || this.unknownType); } public getAssignedTypeOfBinaryExpression(node: BinaryExpression): Type { const isDestructuringDefaultAssignment = node.parent.kind === SyntaxKind.ArrayLiteralExpression && this.isDestructuringAssignmentTarget(node.parent) || node.parent.kind === SyntaxKind.PropertyAssignment && this.isDestructuringAssignmentTarget(node.parent.parent); return isDestructuringDefaultAssignment ? this.getTypeWithDefault(this.getAssignedType(node), node.right) : this.getTypeOfExpression(node.right); } public isDestructuringAssignmentTarget(parent: Node) { return parent.parent.kind === SyntaxKind.BinaryExpression && (parent.parent as BinaryExpression).left === parent || parent.parent.kind === SyntaxKind.ForOfStatement && (parent.parent as ForOfStatement).initializer === parent; } public getAssignedTypeOfArrayLiteralElement(node: ArrayLiteralExpression, element: Expression): Type { return this.getTypeOfDestructuredArrayElement(this.getAssignedType(node), indexOf(node.elements, element)); } public getAssignedTypeOfSpreadExpression(node: SpreadElement): Type { return this.getTypeOfDestructuredSpreadExpression(this.getAssignedType((node.parent))); } public getAssignedTypeOfPropertyAssignment(node: PropertyAssignment | ShorthandPropertyAssignment): Type { return this.getTypeOfDestructuredProperty(this.getAssignedType((node.parent)), node.name); } public getAssignedTypeOfShorthandPropertyAssignment(node: ShorthandPropertyAssignment): Type { return this.getTypeWithDefault(this.getAssignedTypeOfPropertyAssignment(node), node.objectAssignmentInitializer); } public getAssignedType(node: Expression): Type { const parent = node.parent; switch (parent.kind) { case SyntaxKind.ForInStatement: return this.stringType; case SyntaxKind.ForOfStatement: return this.checkRightHandSideOfForOf((parent).expression, (parent).awaitModifier) || this.unknownType; case SyntaxKind.BinaryExpression: return this.getAssignedTypeOfBinaryExpression((parent)); case SyntaxKind.DeleteExpression: return this.undefinedType; case SyntaxKind.ArrayLiteralExpression: return this.getAssignedTypeOfArrayLiteralElement((parent), node); case SyntaxKind.SpreadElement: return this.getAssignedTypeOfSpreadExpression((parent)); case SyntaxKind.PropertyAssignment: return this.getAssignedTypeOfPropertyAssignment((parent)); case SyntaxKind.ShorthandPropertyAssignment: return this.getAssignedTypeOfShorthandPropertyAssignment((parent)); } return this.unknownType; } public getInitialTypeOfBindingElement(node: BindingElement): Type { const pattern = node.parent; const parentType = this.getInitialType((pattern.parent)); const type = pattern.kind === SyntaxKind.ObjectBindingPattern ? this.getTypeOfDestructuredProperty(parentType, node.propertyName || node.name) : !node.dotDotDotToken ? this.getTypeOfDestructuredArrayElement(parentType, indexOf(pattern.elements, node)) : this.getTypeOfDestructuredSpreadExpression(parentType); return this.getTypeWithDefault(type, node.initializer); } public getTypeOfInitializer(node: Expression) { // Return the cached type if one is available. If the type of the variable was inferred // from its initializer, we'll already have cached the type. Otherwise we compute it now // without caching such that transient types are reflected. const links = this.getNodeLinks(node); return links.resolvedType || this.getTypeOfExpression(node); } public getInitialTypeOfVariableDeclaration(node: VariableDeclaration) { if (node.initializer) { return this.getTypeOfInitializer(node.initializer); } if (node.parent.parent.kind === SyntaxKind.ForInStatement) { return this.stringType; } if (node.parent.parent.kind === SyntaxKind.ForOfStatement) { return this.checkRightHandSideOfForOf((node.parent.parent).expression, (node.parent.parent).awaitModifier) || this.unknownType; } return this.unknownType; } public getInitialType(node: VariableDeclaration | BindingElement) { return node.kind === SyntaxKind.VariableDeclaration ? this.getInitialTypeOfVariableDeclaration((node)) : this.getInitialTypeOfBindingElement((node)); } public getInitialOrAssignedType(node: VariableDeclaration | BindingElement | Expression) { return node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.BindingElement ? this.getInitialType((node)) : this.getAssignedType((node)); } public isEmptyArrayAssignment(node: VariableDeclaration | BindingElement | Expression) { return node.kind === SyntaxKind.VariableDeclaration && (node).initializer && this.isEmptyArrayLiteral((node).initializer) || node.kind !== SyntaxKind.BindingElement && node.parent.kind === SyntaxKind.BinaryExpression && this.isEmptyArrayLiteral((node.parent).right); } public getReferenceCandidate(node: Expression): Expression { switch (node.kind) { case SyntaxKind.ParenthesizedExpression: return this.getReferenceCandidate((node).expression); case SyntaxKind.BinaryExpression: switch ((node).operatorToken.kind) { case SyntaxKind.EqualsToken: return this.getReferenceCandidate((node).left); case SyntaxKind.CommaToken: return this.getReferenceCandidate((node).right); } } return node; } public getReferenceRoot(node: Node): Node { const parent = node.parent; return parent.kind === SyntaxKind.ParenthesizedExpression || parent.kind === SyntaxKind.BinaryExpression && (parent).operatorToken.kind === SyntaxKind.EqualsToken && (parent).left === node || parent.kind === SyntaxKind.BinaryExpression && (parent).operatorToken.kind === SyntaxKind.CommaToken && (parent).right === node ? this.getReferenceRoot(parent) : node; } public getTypeOfSwitchClause(clause: CaseClause | DefaultClause) { if (clause.kind === SyntaxKind.CaseClause) { const caseType = this.getRegularTypeOfLiteralType(this.getTypeOfExpression((clause).expression)); return this.isUnitType(caseType) ? caseType : undefined; } return this.neverType; } public getSwitchClauseTypes(switchStatement: SwitchStatement): Type[] { const links = this.getNodeLinks(switchStatement); if (!links.switchTypes) { // If all case clauses specify expressions that have unit types, we return an array // of those unit types. Otherwise we return an empty array. links.switchTypes = []; for (const clause of switchStatement.caseBlock.clauses) { const type = this.getTypeOfSwitchClause(clause); if (type === undefined) { return links.switchTypes = emptyArray; } links.switchTypes.push(type); } } return links.switchTypes; } public eachTypeContainedIn(source: Type, types: Type[]) { return source.flags & TypeFlags.Union ? !forEach((source).types, t => !contains(types, t)) : contains(types, source); } public isTypeSubsetOf(source: Type, target: Type) { return source === target || target.flags & TypeFlags.Union && this.isTypeSubsetOfUnion(source, (target)); } public isTypeSubsetOfUnion(source: Type, target: UnionType) { if (source.flags & TypeFlags.Union) { for (const t of (source).types) { if (!this.containsType(target.types, t)) { return false; } } return true; } if (source.flags & TypeFlags.EnumLiteral && this.getBaseTypeOfEnumLiteralType((source)) === target) { return true; } return this.containsType(target.types, source); } public forEachType(type: Type, f: (t: Type) => T): T { return type.flags & TypeFlags.Union ? forEach((type).types, f) : f(type); } public filterType(type: Type, f: (t: Type) => boolean): Type { if (type.flags & TypeFlags.Union) { const types = (type).types; const filtered = filter(types, f); return filtered === types ? type : this.getUnionTypeFromSortedList(filtered); } return f(type) ? type : this.neverType; } // Apply a mapping function to a type and return the resulting type. If the source type // is a union type, the mapping function is applied to each constituent type and a union // of the resulting types is returned. public mapType(type: Type, mapper: (t: Type) => Type): Type { if (!(type.flags & TypeFlags.Union)) { return mapper(type); } const types = (type).types; let mappedType: Type; let mappedTypes: Type[]; for (const current of types) { const t = mapper(current); if (t) { if (!mappedType) { mappedType = t; } else if (!mappedTypes) { mappedTypes = [mappedType, t]; } else { mappedTypes.push(t); } } } return mappedTypes ? this.getUnionType(mappedTypes) : mappedType; } public extractTypesOfKind(type: Type, kind: TypeFlags) { return this.filterType(type, t => (t.flags & kind) !== 0); } // Return a new type in which occurrences of the string and number primitive types in // typeWithPrimitives have been replaced with occurrences of string literals and numeric // literals in typeWithLiterals, respectively. public replacePrimitivesWithLiterals(typeWithPrimitives: Type, typeWithLiterals: Type) { const __conv_self__ = this; if (this.isTypeSubsetOf(this.stringType, typeWithPrimitives) && this.maybeTypeOfKind(typeWithLiterals, TypeFlags.StringLiteral) || this.isTypeSubsetOf(this.numberType, typeWithPrimitives) && this.maybeTypeOfKind(typeWithLiterals, TypeFlags.NumberLiteral)) { return this.mapType(typeWithPrimitives, t => t.flags & TypeFlags.String ? __conv_self__.extractTypesOfKind(typeWithLiterals, TypeFlags.String | TypeFlags.StringLiteral) : t.flags & TypeFlags.Number ? __conv_self__.extractTypesOfKind(typeWithLiterals, TypeFlags.Number | TypeFlags.NumberLiteral) : t); } return typeWithPrimitives; } public isIncomplete(flowType: FlowType) { return flowType.flags === 0; } public getTypeFromFlowType(flowType: FlowType) { return flowType.flags === 0 ? (flowType).type : flowType; } public createFlowType(type: Type, incomplete: boolean): FlowType { return incomplete ? { flags: 0, type } : type; } // An evolving array type tracks the element types that have so far been seen in an // 'x.push(value)' or 'x[n] = value' operation along the control flow graph. Evolving // array types are ultimately converted into manifest array types (using getFinalArrayType) // and never escape the getFlowTypeOfReference function. public createEvolvingArrayType(elementType: Type): EvolvingArrayType { const result = (this.createObjectType(ObjectFlags.EvolvingArray)); result.elementType = elementType; return result; } public getEvolvingArrayType(elementType: Type): EvolvingArrayType { return this.evolvingArrayTypes[elementType.id] || (this.evolvingArrayTypes[elementType.id] = this.createEvolvingArrayType(elementType)); } // When adding evolving array element types we do not perform subtype reduction. Instead, // we defer subtype reduction until the evolving array type is finalized into a manifest // array type. public addEvolvingArrayElementType(evolvingArrayType: EvolvingArrayType, node: Expression): EvolvingArrayType { const elementType = this.getBaseTypeOfLiteralType(this.getContextFreeTypeOfExpression(node)); return this.isTypeSubsetOf(elementType, evolvingArrayType.elementType) ? evolvingArrayType : this.getEvolvingArrayType(this.getUnionType([evolvingArrayType.elementType, elementType])); } public createFinalArrayType(elementType: Type) { return elementType.flags & TypeFlags.Never ? this.autoArrayType : this.createArrayType(elementType.flags & TypeFlags.Union ? this.getUnionType((elementType).types, /*subtypeReduction*/ true) : elementType); } // We perform subtype reduction upon obtaining the final array type from an evolving array type. public getFinalArrayType(evolvingArrayType: EvolvingArrayType): Type { return evolvingArrayType.finalArrayType || (evolvingArrayType.finalArrayType = this.createFinalArrayType(evolvingArrayType.elementType)); } public finalizeEvolvingArrayType(type: Type): Type { return this.getObjectFlags(type) & ObjectFlags.EvolvingArray ? this.getFinalArrayType((type)) : type; } public getElementTypeOfEvolvingArrayType(type: Type) { return this.getObjectFlags(type) & ObjectFlags.EvolvingArray ? (type).elementType : this.neverType; } public isEvolvingArrayTypeList(types: Type[]) { let hasEvolvingArrayType = false; for (const t of types) { if (!(t.flags & TypeFlags.Never)) { if (!(this.getObjectFlags(t) & ObjectFlags.EvolvingArray)) { return false; } hasEvolvingArrayType = true; } } return hasEvolvingArrayType; } // At flow control branch or loop junctions, if the type along every antecedent code path // is an evolving array type, we construct a combined evolving array type. Otherwise we // finalize all evolving array types. public getUnionOrEvolvingArrayType(types: Type[], subtypeReduction: boolean) { return this.isEvolvingArrayTypeList(types) ? this.getEvolvingArrayType(this.getUnionType(map(types, this.getElementTypeOfEvolvingArrayType))) : this.getUnionType(sameMap(types, this.finalizeEvolvingArrayType), subtypeReduction); } // Return true if the given node is 'x' in an 'x.length', x.push(value)', 'x.unshift(value)' or // 'x[n] = value' operation, where 'n' is an expression of type any, undefined, or a number-like type. public isEvolvingArrayOperationTarget(node: Node) { const root = this.getReferenceRoot(node); const parent = root.parent; const isLengthPushOrUnshift = parent.kind === SyntaxKind.PropertyAccessExpression && ((parent).name.escapedText === "length" || parent.parent.kind === SyntaxKind.CallExpression && isPushOrUnshiftIdentifier((parent).name)); const isElementAssignment = parent.kind === SyntaxKind.ElementAccessExpression && (parent).expression === root && parent.parent.kind === SyntaxKind.BinaryExpression && (parent.parent).operatorToken.kind === SyntaxKind.EqualsToken && (parent.parent).left === parent && !isAssignmentTarget(parent.parent) && this.isTypeAssignableToKind(this.getTypeOfExpression((parent).argumentExpression), TypeFlags.NumberLike); return isLengthPushOrUnshift || isElementAssignment; } public maybeTypePredicateCall(node: CallExpression) { const links = this.getNodeLinks(node); if (links.maybeTypePredicate === undefined) { links.maybeTypePredicate = this.getMaybeTypePredicate(node); } return links.maybeTypePredicate; } public getMaybeTypePredicate(node: CallExpression) { if (node.expression.kind !== SyntaxKind.SuperKeyword) { const funcType = this.checkNonNullExpression(node.expression); if (funcType !== this.silentNeverType) { const apparentType = this.getApparentType(funcType); if (apparentType !== this.unknownType) { const callSignatures = this.getSignaturesOfType(apparentType, SignatureKind.Call); return !!forEach(callSignatures, sig => sig.typePredicate); } } } return false; } public getFlowTypeOfReference(reference: Node, declaredType: Type, initialType = declaredType, flowContainer?: Node, couldBeUninitialized?: boolean) { const __conv_self__ = this; let key: string; if (!reference.flowNode || !couldBeUninitialized && !(declaredType.flags & TypeFlags.Narrowable)) { return declaredType; } const visitedFlowStart = this.visitedFlowCount; const evolvedType = this.getTypeFromFlowType(getTypeAtFlowNode(reference.flowNode)); this.visitedFlowCount = visitedFlowStart; // When the reference is 'x' in an 'x.length', 'x.push(value)', 'x.unshift(value)' or x[n] = value' operation, // we give type 'any[]' to 'x' instead of using the type determined by control flow analysis such that operations // on empty arrays are possible without implicit any errors and new element types can be inferred without // type mismatch errors. const resultType = this.getObjectFlags(evolvedType) & ObjectFlags.EvolvingArray && this.isEvolvingArrayOperationTarget(reference) ? this.anyArrayType : this.finalizeEvolvingArrayType(evolvedType); if (reference.parent.kind === SyntaxKind.NonNullExpression && this.getTypeWithFacts(resultType, TypeFacts.NEUndefinedOrNull).flags & TypeFlags.Never) { return declaredType; } return resultType; function getTypeAtFlowNode(flow: FlowNode): FlowType { while (true) { if (flow.flags & FlowFlags.Shared) { // We cache results of flow type resolution for shared nodes that were previously visited in // the same getFlowTypeOfReference invocation. A node is considered shared when it is the // antecedent of more than one node. for (let i = visitedFlowStart; i < __conv_self__.visitedFlowCount; i++) { if (__conv_self__.visitedFlowNodes[i] === flow) { return __conv_self__.visitedFlowTypes[i]; } } } let type: FlowType; if (flow.flags & FlowFlags.AfterFinally) { // block flow edge: finally -> pre-try (for larger explanation check comment in binder.ts - bindTryStatement (flow).locked = true; type = getTypeAtFlowNode((flow).antecedent); (flow).locked = false; } else if (flow.flags & FlowFlags.PreFinally) { // locked pre-finally flows are filtered out in getTypeAtFlowBranchLabel // so here just redirect to antecedent flow = (flow).antecedent; continue; } else if (flow.flags & FlowFlags.Assignment) { type = getTypeAtFlowAssignment(flow); if (!type) { flow = (flow).antecedent; continue; } } else if (flow.flags & FlowFlags.Condition) { type = getTypeAtFlowCondition(flow); } else if (flow.flags & FlowFlags.SwitchClause) { type = getTypeAtSwitchClause(flow); } else if (flow.flags & FlowFlags.Label) { if ((flow).antecedents.length === 1) { flow = (flow).antecedents[0]; continue; } type = flow.flags & FlowFlags.BranchLabel ? getTypeAtFlowBranchLabel(flow) : getTypeAtFlowLoopLabel(flow); } else if (flow.flags & FlowFlags.ArrayMutation) { type = getTypeAtFlowArrayMutation(flow); if (!type) { flow = (flow).antecedent; continue; } } else if (flow.flags & FlowFlags.Start) { // Check if we should continue with the control flow of the containing function. const container = (flow).container; if (container && container !== flowContainer && reference.kind !== SyntaxKind.PropertyAccessExpression && reference.kind !== SyntaxKind.ThisKeyword) { flow = container.flowNode; continue; } // At the top of the flow we have the initial type. type = initialType; } else { // Unreachable code errors are reported in the binding phase. Here we // simply return the non-auto declared type to reduce follow-on errors. type = __conv_self__.convertAutoToAny(declaredType); } if (flow.flags & FlowFlags.Shared) { // Record visited node and the associated type in the cache. __conv_self__.visitedFlowNodes[__conv_self__.visitedFlowCount] = flow; __conv_self__.visitedFlowTypes[__conv_self__.visitedFlowCount] = type; __conv_self__.visitedFlowCount++; } return type; } } function getTypeAtFlowAssignment(flow: FlowAssignment) { const node = flow.node; // Assignments only narrow the computed type if the declared type is a union type. Thus, we // only need to evaluate the assigned type if the declared type is a union type. if (__conv_self__.isMatchingReference(reference, node)) { if (getAssignmentTargetKind(node) === AssignmentKind.Compound) { const flowType = getTypeAtFlowNode(flow.antecedent); return __conv_self__.createFlowType(__conv_self__.getBaseTypeOfLiteralType(__conv_self__.getTypeFromFlowType(flowType)), __conv_self__.isIncomplete(flowType)); } if (declaredType === __conv_self__.autoType || declaredType === __conv_self__.autoArrayType) { if (__conv_self__.isEmptyArrayAssignment(node)) { return __conv_self__.getEvolvingArrayType(__conv_self__.neverType); } const assignedType = __conv_self__.getBaseTypeOfLiteralType(__conv_self__.getInitialOrAssignedType(node)); return __conv_self__.isTypeAssignableTo(assignedType, declaredType) ? assignedType : __conv_self__.anyArrayType; } if (declaredType.flags & TypeFlags.Union) { return __conv_self__.getAssignmentReducedType((declaredType), __conv_self__.getInitialOrAssignedType(node)); } return declaredType; } // We didn't have a direct match. However, if the reference is a dotted name, this // may be an assignment to a left hand part of the reference. For example, for a // reference 'x.y.z', we may be at an assignment to 'x.y' or 'x'. In that case, // return the declared type. if (__conv_self__.containsMatchingReference(reference, node)) { return declaredType; } // Assignment doesn't affect reference return undefined; } function getTypeAtFlowArrayMutation(flow: FlowArrayMutation): FlowType { const node = flow.node; const expr = node.kind === SyntaxKind.CallExpression ? ((node).expression).expression : ((node).left).expression; if (__conv_self__.isMatchingReference(reference, __conv_self__.getReferenceCandidate(expr))) { const flowType = getTypeAtFlowNode(flow.antecedent); const type = __conv_self__.getTypeFromFlowType(flowType); if (__conv_self__.getObjectFlags(type) & ObjectFlags.EvolvingArray) { let evolvedType = type; if (node.kind === SyntaxKind.CallExpression) { for (const arg of (node).arguments) { evolvedType = __conv_self__.addEvolvingArrayElementType(evolvedType, arg); } } else { const indexType = __conv_self__.getTypeOfExpression(((node).left).argumentExpression); if (__conv_self__.isTypeAssignableToKind(indexType, TypeFlags.NumberLike)) { evolvedType = __conv_self__.addEvolvingArrayElementType(evolvedType, (node).right); } } return evolvedType === type ? flowType : __conv_self__.createFlowType(evolvedType, __conv_self__.isIncomplete(flowType)); } return flowType; } return undefined; } function getTypeAtFlowCondition(flow: FlowCondition): FlowType { const flowType = getTypeAtFlowNode(flow.antecedent); const type = __conv_self__.getTypeFromFlowType(flowType); if (type.flags & TypeFlags.Never) { return flowType; } // If we have an antecedent type (meaning we're reachable in some way), we first // attempt to narrow the antecedent type. If that produces the never type, and if // the antecedent type is incomplete (i.e. a transient type in a loop), then we // take the type guard as an indication that control *could* reach here once we // have the complete type. We proceed by switching to the silent never type which // doesn't report errors when operators are applied to it. Note that this is the // *only* place a silent never type is ever generated. const assumeTrue = (flow.flags & FlowFlags.TrueCondition) !== 0; const nonEvolvingType = __conv_self__.finalizeEvolvingArrayType(type); const narrowedType = narrowType(nonEvolvingType, flow.expression, assumeTrue); if (narrowedType === nonEvolvingType) { return flowType; } const incomplete = __conv_self__.isIncomplete(flowType); const resultType = incomplete && narrowedType.flags & TypeFlags.Never ? __conv_self__.silentNeverType : narrowedType; return __conv_self__.createFlowType(resultType, incomplete); } function getTypeAtSwitchClause(flow: FlowSwitchClause): FlowType { const flowType = getTypeAtFlowNode(flow.antecedent); let type = __conv_self__.getTypeFromFlowType(flowType); const expr = flow.switchStatement.expression; if (__conv_self__.isMatchingReference(reference, expr)) { type = narrowTypeBySwitchOnDiscriminant(type, flow.switchStatement, flow.clauseStart, flow.clauseEnd); } else if (isMatchingReferenceDiscriminant(expr, type)) { type = narrowTypeByDiscriminant(type, expr, t => narrowTypeBySwitchOnDiscriminant(t, flow.switchStatement, flow.clauseStart, flow.clauseEnd)); } return __conv_self__.createFlowType(type, __conv_self__.isIncomplete(flowType)); } function getTypeAtFlowBranchLabel(flow: FlowLabel): FlowType { const antecedentTypes: Type[] = []; let subtypeReduction = false; let seenIncomplete = false; for (const antecedent of flow.antecedents) { if (antecedent.flags & FlowFlags.PreFinally && (antecedent).lock.locked) { // if flow correspond to branch from pre-try to finally and this branch is locked - this means that // we initially have started following the flow outside the finally block. // in this case we should ignore this branch. continue; } const flowType = getTypeAtFlowNode(antecedent); const type = __conv_self__.getTypeFromFlowType(flowType); // If the type at a particular antecedent path is the declared type and the // reference is known to always be assigned (i.e. when declared and initial types // are the same), there is no reason to process more antecedents since the only // possible outcome is subtypes that will be removed in the final union type anyway. if (type === declaredType && declaredType === initialType) { return type; } if (!contains(antecedentTypes, type)) { antecedentTypes.push(type); } // If an antecedent type is not a subset of the declared type, we need to perform // subtype reduction. This happens when a "foreign" type is injected into the control // flow using the instanceof operator or a user defined type predicate. if (!__conv_self__.isTypeSubsetOf(type, declaredType)) { subtypeReduction = true; } if (__conv_self__.isIncomplete(flowType)) { seenIncomplete = true; } } return __conv_self__.createFlowType(__conv_self__.getUnionOrEvolvingArrayType(antecedentTypes, subtypeReduction), seenIncomplete); } function getTypeAtFlowLoopLabel(flow: FlowLabel): FlowType { // If we have previously computed the control flow type for the reference at // this flow loop junction, return the cached type. const id = __conv_self__.getFlowNodeId(flow); const cache = __conv_self__.flowLoopCaches[id] || (__conv_self__.flowLoopCaches[id] = createMap()); if (!key) { key = __conv_self__.getFlowCacheKey(reference); // No cache key is generated when binding patterns are in unnarrowable situations if (!key) { return declaredType; } } const cached = cache.get(key); if (cached) { return cached; } // If this flow loop junction and reference are already being processed, return // the union of the types computed for each branch so far, marked as incomplete. // It is possible to see an empty array in cases where loops are nested and the // back edge of the outer loop reaches an inner loop that is already being analyzed. // In such cases we restart the analysis of the inner loop, which will then see // a non-empty in-process array for the outer loop and eventually terminate because // the first antecedent of a loop junction is always the non-looping control flow // path that leads to the top. for (let i = __conv_self__.flowLoopStart; i < __conv_self__.flowLoopCount; i++) { if (__conv_self__.flowLoopNodes[i] === flow && __conv_self__.flowLoopKeys[i] === key && __conv_self__.flowLoopTypes[i].length) { return __conv_self__.createFlowType(__conv_self__.getUnionOrEvolvingArrayType(__conv_self__.flowLoopTypes[i], /*subtypeReduction*/ false), /*incomplete*/ true); } } // Add the flow loop junction and reference to the in-process stack and analyze // each antecedent code path. const antecedentTypes: Type[] = []; let subtypeReduction = false; let firstAntecedentType: FlowType; __conv_self__.flowLoopNodes[__conv_self__.flowLoopCount] = flow; __conv_self__.flowLoopKeys[__conv_self__.flowLoopCount] = key; __conv_self__.flowLoopTypes[__conv_self__.flowLoopCount] = antecedentTypes; for (const antecedent of flow.antecedents) { __conv_self__.flowLoopCount++; const flowType = getTypeAtFlowNode(antecedent); __conv_self__.flowLoopCount--; if (!firstAntecedentType) { firstAntecedentType = flowType; } const type = __conv_self__.getTypeFromFlowType(flowType); // If we see a value appear in the cache it is a sign that control flow analysis // was restarted and completed by checkExpressionCached. We can simply pick up // the resulting type and bail out. const cached = cache.get(key); if (cached) { return cached; } if (!contains(antecedentTypes, type)) { antecedentTypes.push(type); } // If an antecedent type is not a subset of the declared type, we need to perform // subtype reduction. This happens when a "foreign" type is injected into the control // flow using the instanceof operator or a user defined type predicate. if (!__conv_self__.isTypeSubsetOf(type, declaredType)) { subtypeReduction = true; } // If the type at a particular antecedent path is the declared type there is no // reason to process more antecedents since the only possible outcome is subtypes // that will be removed in the final union type anyway. if (type === declaredType) { break; } } // The result is incomplete if the first antecedent (the non-looping control flow path) // is incomplete. const result = __conv_self__.getUnionOrEvolvingArrayType(antecedentTypes, subtypeReduction); if (__conv_self__.isIncomplete(firstAntecedentType)) { return __conv_self__.createFlowType(result, /*incomplete*/ true); } cache.set(key, result); return result; } function isMatchingReferenceDiscriminant(expr: Expression, computedType: Type) { return expr.kind === SyntaxKind.PropertyAccessExpression && computedType.flags & TypeFlags.Union && __conv_self__.isMatchingReference(reference, (expr).expression) && __conv_self__.isDiscriminantProperty(computedType, (expr).name.escapedText); } function narrowTypeByDiscriminant(type: Type, propAccess: PropertyAccessExpression, narrowType: (t: Type) => Type): Type { const propName = propAccess.name.escapedText; const propType = __conv_self__.getTypeOfPropertyOfType(type, propName); const narrowedPropType = propType && narrowType(propType); return propType === narrowedPropType ? type : __conv_self__.filterType(type, t => __conv_self__.isTypeComparableTo(__conv_self__.getTypeOfPropertyOfType(t, propName), narrowedPropType)); } function narrowTypeByTruthiness(type: Type, expr: Expression, assumeTrue: boolean): Type { if (__conv_self__.isMatchingReference(reference, expr)) { return __conv_self__.getTypeWithFacts(type, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy); } if (isMatchingReferenceDiscriminant(expr, declaredType)) { return narrowTypeByDiscriminant(type, (expr), t => __conv_self__.getTypeWithFacts(t, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy)); } if (__conv_self__.containsMatchingReferenceDiscriminant(reference, expr)) { return declaredType; } return type; } function narrowTypeByBinaryExpression(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type { switch (expr.operatorToken.kind) { case SyntaxKind.EqualsToken: return narrowTypeByTruthiness(type, expr.left, assumeTrue); case SyntaxKind.EqualsEqualsToken: case SyntaxKind.ExclamationEqualsToken: case SyntaxKind.EqualsEqualsEqualsToken: case SyntaxKind.ExclamationEqualsEqualsToken: const operator = expr.operatorToken.kind; const left = __conv_self__.getReferenceCandidate(expr.left); const right = __conv_self__.getReferenceCandidate(expr.right); if (left.kind === SyntaxKind.TypeOfExpression && right.kind === SyntaxKind.StringLiteral) { return narrowTypeByTypeof(type, left, operator, right, assumeTrue); } if (right.kind === SyntaxKind.TypeOfExpression && left.kind === SyntaxKind.StringLiteral) { return narrowTypeByTypeof(type, right, operator, left, assumeTrue); } if (__conv_self__.isMatchingReference(reference, left)) { return narrowTypeByEquality(type, operator, right, assumeTrue); } if (__conv_self__.isMatchingReference(reference, right)) { return narrowTypeByEquality(type, operator, left, assumeTrue); } if (isMatchingReferenceDiscriminant(left, declaredType)) { return narrowTypeByDiscriminant(type, left, t => narrowTypeByEquality(t, operator, right, assumeTrue)); } if (isMatchingReferenceDiscriminant(right, declaredType)) { return narrowTypeByDiscriminant(type, right, t => narrowTypeByEquality(t, operator, left, assumeTrue)); } if (__conv_self__.containsMatchingReferenceDiscriminant(reference, left) || __conv_self__.containsMatchingReferenceDiscriminant(reference, right)) { return declaredType; } break; case SyntaxKind.InstanceOfKeyword: return narrowTypeByInstanceof(type, expr, assumeTrue); case SyntaxKind.CommaToken: return narrowType(type, expr.right, assumeTrue); } return type; } function narrowTypeByEquality(type: Type, operator: SyntaxKind, value: Expression, assumeTrue: boolean): Type { if (type.flags & TypeFlags.Any) { return type; } if (operator === SyntaxKind.ExclamationEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken) { assumeTrue = !assumeTrue; } const valueType = __conv_self__.getTypeOfExpression(value); if (valueType.flags & TypeFlags.Nullable) { if (!__conv_self__.strictNullChecks) { return type; } const doubleEquals = operator === SyntaxKind.EqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsToken; const facts = doubleEquals ? assumeTrue ? TypeFacts.EQUndefinedOrNull : TypeFacts.NEUndefinedOrNull : value.kind === SyntaxKind.NullKeyword ? assumeTrue ? TypeFacts.EQNull : TypeFacts.NENull : assumeTrue ? TypeFacts.EQUndefined : TypeFacts.NEUndefined; return __conv_self__.getTypeWithFacts(type, facts); } if (type.flags & TypeFlags.NotUnionOrUnit) { return type; } if (assumeTrue) { const narrowedType = __conv_self__.filterType(type, t => __conv_self__.areTypesComparable(t, valueType)); return narrowedType.flags & TypeFlags.Never ? type : __conv_self__.replacePrimitivesWithLiterals(narrowedType, valueType); } if (__conv_self__.isUnitType(valueType)) { const regularType = __conv_self__.getRegularTypeOfLiteralType(valueType); return __conv_self__.filterType(type, t => __conv_self__.getRegularTypeOfLiteralType(t) !== regularType); } return type; } function narrowTypeByTypeof(type: Type, typeOfExpr: TypeOfExpression, operator: SyntaxKind, literal: LiteralExpression, assumeTrue: boolean): Type { // We have '==', '!=', '====', or !==' operator with 'typeof xxx' and string literal operands const target = __conv_self__.getReferenceCandidate(typeOfExpr.expression); if (!__conv_self__.isMatchingReference(reference, target)) { // For a reference of the form 'x.y', a 'typeof x === ...' type guard resets the // narrowed type of 'y' to its declared type. if (__conv_self__.containsMatchingReference(reference, target)) { return declaredType; } return type; } if (operator === SyntaxKind.ExclamationEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken) { assumeTrue = !assumeTrue; } if (assumeTrue && !(type.flags & TypeFlags.Union)) { // We narrow a non-union type to an exact primitive type if the non-union type // is a supertype of that primitive type. For example, type 'any' can be narrowed // to one of the primitive types. const targetType = __conv_self__.typeofTypesByName.get(literal.text); if (targetType) { if (__conv_self__.isTypeSubtypeOf(targetType, type)) { return targetType; } if (type.flags & TypeFlags.TypeVariable) { const constraint = __conv_self__.getBaseConstraintOfType(type) || __conv_self__.anyType; if (__conv_self__.isTypeSubtypeOf(targetType, constraint)) { return __conv_self__.getIntersectionType([type, targetType]); } } } } const facts = assumeTrue ? __conv_self__.typeofEQFacts.get(literal.text) || TypeFacts.TypeofEQHostObject : __conv_self__.typeofNEFacts.get(literal.text) || TypeFacts.TypeofNEHostObject; return __conv_self__.getTypeWithFacts(type, facts); } function narrowTypeBySwitchOnDiscriminant(type: Type, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number) { // We only narrow if all case expressions specify values with unit types const switchTypes = __conv_self__.getSwitchClauseTypes(switchStatement); if (!switchTypes.length) { return type; } const clauseTypes = switchTypes.slice(clauseStart, clauseEnd); const hasDefaultClause = clauseStart === clauseEnd || contains(clauseTypes, __conv_self__.neverType); const discriminantType = __conv_self__.getUnionType(clauseTypes); const caseType = discriminantType.flags & TypeFlags.Never ? __conv_self__.neverType : __conv_self__.replacePrimitivesWithLiterals(__conv_self__.filterType(type, t => __conv_self__.isTypeComparableTo(discriminantType, t)), discriminantType); if (!hasDefaultClause) { return caseType; } const defaultType = __conv_self__.filterType(type, t => !(__conv_self__.isUnitType(t) && contains(switchTypes, __conv_self__.getRegularTypeOfLiteralType(t)))); return caseType.flags & TypeFlags.Never ? defaultType : __conv_self__.getUnionType([caseType, defaultType]); } function narrowTypeByInstanceof(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type { const left = __conv_self__.getReferenceCandidate(expr.left); if (!__conv_self__.isMatchingReference(reference, left)) { // For a reference of the form 'x.y', an 'x instanceof T' type guard resets the // narrowed type of 'y' to its declared type. if (__conv_self__.containsMatchingReference(reference, left)) { return declaredType; } return type; } // Check that right operand is a function type with a prototype property const rightType = __conv_self__.getTypeOfExpression(expr.right); if (!__conv_self__.isTypeSubtypeOf(rightType, __conv_self__.globalFunctionType)) { return type; } let targetType: Type; const prototypeProperty = __conv_self__.getPropertyOfType(rightType, ("prototype" as __String)); if (prototypeProperty) { // Target type is type of the prototype property const prototypePropertyType = __conv_self__.getTypeOfSymbol(prototypeProperty); if (!__conv_self__.isTypeAny(prototypePropertyType)) { targetType = prototypePropertyType; } } // Don't narrow from 'any' if the target type is exactly 'Object' or 'Function' if (__conv_self__.isTypeAny(type) && (targetType === __conv_self__.globalObjectType || targetType === __conv_self__.globalFunctionType)) { return type; } if (!targetType) { // Target type is type of construct signature let constructSignatures: Signature[]; if (__conv_self__.getObjectFlags(rightType) & ObjectFlags.Interface) { constructSignatures = __conv_self__.resolveDeclaredMembers((rightType)).declaredConstructSignatures; } else if (__conv_self__.getObjectFlags(rightType) & ObjectFlags.Anonymous) { constructSignatures = __conv_self__.getSignaturesOfType(rightType, SignatureKind.Construct); } if (constructSignatures && constructSignatures.length) { targetType = __conv_self__.getUnionType(map(constructSignatures, signature => __conv_self__.getReturnTypeOfSignature(__conv_self__.getErasedSignature(signature)))); } } if (targetType) { return getNarrowedType(type, targetType, assumeTrue, __conv_self__.isTypeInstanceOf); } return type; } function getNarrowedType(type: Type, candidate: Type, assumeTrue: boolean, isRelated: (source: Type, target: Type) => boolean) { if (!assumeTrue) { return __conv_self__.filterType(type, t => !isRelated(t, candidate)); } // If the current type is a union type, remove all constituents that couldn't be instances of // the candidate type. If one or more constituents remain, return a union of those. if (type.flags & TypeFlags.Union) { const assignableType = __conv_self__.filterType(type, t => isRelated(t, candidate)); if (!(assignableType.flags & TypeFlags.Never)) { return assignableType; } } // If the candidate type is a subtype of the target type, narrow to the candidate type. // Otherwise, if the target type is assignable to the candidate type, keep the target type. // Otherwise, if the candidate type is assignable to the target type, narrow to the candidate // type. Otherwise, the types are completely unrelated, so narrow to an intersection of the // two types. return __conv_self__.isTypeSubtypeOf(candidate, type) ? candidate : __conv_self__.isTypeAssignableTo(type, candidate) ? type : __conv_self__.isTypeAssignableTo(candidate, type) ? candidate : __conv_self__.getIntersectionType([type, candidate]); } function narrowTypeByTypePredicate(type: Type, callExpression: CallExpression, assumeTrue: boolean): Type { if (!__conv_self__.hasMatchingArgument(callExpression, reference) || !__conv_self__.maybeTypePredicateCall(callExpression)) { return type; } const signature = __conv_self__.getResolvedSignature(callExpression); const predicate = signature.typePredicate; if (!predicate) { return type; } // Don't narrow from 'any' if the predicate type is exactly 'Object' or 'Function' if (__conv_self__.isTypeAny(type) && (predicate.type === __conv_self__.globalObjectType || predicate.type === __conv_self__.globalFunctionType)) { return type; } if (isIdentifierTypePredicate(predicate)) { const predicateArgument = callExpression.arguments[predicate.parameterIndex - (signature.thisParameter ? 1 : 0)]; if (predicateArgument) { if (__conv_self__.isMatchingReference(reference, predicateArgument)) { return getNarrowedType(type, predicate.type, assumeTrue, __conv_self__.isTypeSubtypeOf); } if (__conv_self__.containsMatchingReference(reference, predicateArgument)) { return declaredType; } } } else { const invokedExpression = skipParentheses(callExpression.expression); if (invokedExpression.kind === SyntaxKind.ElementAccessExpression || invokedExpression.kind === SyntaxKind.PropertyAccessExpression) { const accessExpression = invokedExpression as ElementAccessExpression | PropertyAccessExpression; const possibleReference = skipParentheses(accessExpression.expression); if (__conv_self__.isMatchingReference(reference, possibleReference)) { return getNarrowedType(type, predicate.type, assumeTrue, __conv_self__.isTypeSubtypeOf); } if (__conv_self__.containsMatchingReference(reference, possibleReference)) { return declaredType; } } } return type; } // Narrow the given type based on the given expression having the assumed boolean value. The returned type // will be a subtype or the same type as the argument. function narrowType(type: Type, expr: Expression, assumeTrue: boolean): Type { switch (expr.kind) { case SyntaxKind.Identifier: case SyntaxKind.ThisKeyword: case SyntaxKind.SuperKeyword: case SyntaxKind.PropertyAccessExpression: return narrowTypeByTruthiness(type, expr, assumeTrue); case SyntaxKind.CallExpression: return narrowTypeByTypePredicate(type, expr, assumeTrue); case SyntaxKind.ParenthesizedExpression: return narrowType(type, (expr).expression, assumeTrue); case SyntaxKind.BinaryExpression: return narrowTypeByBinaryExpression(type, expr, assumeTrue); case SyntaxKind.PrefixUnaryExpression: if ((expr).operator === SyntaxKind.ExclamationToken) { return narrowType(type, (expr).operand, !assumeTrue); } break; } return type; } } public getTypeOfSymbolAtLocation(symbol: Symbol, location: Node) { symbol = symbol.exportSymbol || symbol; // If we have an identifier or a property access at the given location, if the location is // an dotted name expression, and if the location is not an assignment target, obtain the type // of the expression (which will reflect control flow analysis). If the expression indeed // resolved to the given symbol, return the narrowed type. if (location.kind === SyntaxKind.Identifier) { if (isRightSideOfQualifiedNameOrPropertyAccess(location)) { location = location.parent; } if (isPartOfExpression(location) && !isAssignmentTarget(location)) { const type = this.getTypeOfExpression((location)); if (this.getExportSymbolOfValueSymbolIfExported(this.getNodeLinks(location).resolvedSymbol) === symbol) { return type; } } } // The location isn't a reference to the given symbol, meaning we're being asked // a hypothetical question of what type the symbol would have if there was a reference // to it at the given location. Since we have no control flow information for the // hypothetical reference (control flow information is created and attached by the // binder), we simply return the declared type of the symbol. return this.getTypeOfSymbol(symbol); } public getControlFlowContainer(node: Node): Node { return findAncestor(node.parent, node => isFunctionLike(node) && !getImmediatelyInvokedFunctionExpression(node) || node.kind === SyntaxKind.ModuleBlock || node.kind === SyntaxKind.SourceFile || node.kind === SyntaxKind.PropertyDeclaration); } // Check if a parameter is assigned anywhere within its declaring function. public isParameterAssigned(symbol: Symbol) { const func = getRootDeclaration(symbol.valueDeclaration).parent; const links = this.getNodeLinks(func); if (!(links.flags & NodeCheckFlags.AssignmentsMarked)) { links.flags |= NodeCheckFlags.AssignmentsMarked; if (!this.hasParentWithAssignmentsMarked(func)) { this.markParameterAssignments(func); } } return symbol.isAssigned || false; } public hasParentWithAssignmentsMarked(node: Node) { const __conv_self__ = this; return !!findAncestor(node.parent, node => isFunctionLike(node) && !!(__conv_self__.getNodeLinks(node).flags & NodeCheckFlags.AssignmentsMarked)); } public markParameterAssignments(node: Node) { if (node.kind === SyntaxKind.Identifier) { if (isAssignmentTarget(node)) { const symbol = this.getResolvedSymbol((node)); if (symbol.valueDeclaration && getRootDeclaration(symbol.valueDeclaration).kind === SyntaxKind.Parameter) { symbol.isAssigned = true; } } } else { forEachChild(node, this.markParameterAssignments); } } public isConstVariable(symbol: Symbol) { return symbol.flags & SymbolFlags.Variable && (this.getDeclarationNodeFlagsFromSymbol(symbol) & NodeFlags.Const) !== 0 && this.getTypeOfSymbol(symbol) !== this.autoArrayType; } /** remove undefined from the annotated type of a parameter when there is an initializer (that doesn't include undefined) */ public removeOptionalityFromDeclaredType(declaredType: Type, declaration: VariableLikeDeclaration): Type { const annotationIncludesUndefined = this.strictNullChecks && declaration.kind === SyntaxKind.Parameter && declaration.initializer && this.getFalsyFlags(declaredType) & TypeFlags.Undefined && !(this.getFalsyFlags(this.checkExpression(declaration.initializer)) & TypeFlags.Undefined); return annotationIncludesUndefined ? this.getTypeWithFacts(declaredType, TypeFacts.NEUndefined) : declaredType; } public isApparentTypePosition(node: Node) { const parent = node.parent; return parent.kind === SyntaxKind.PropertyAccessExpression || parent.kind === SyntaxKind.CallExpression && (parent).expression === node || parent.kind === SyntaxKind.ElementAccessExpression && (parent).expression === node; } public typeHasNullableConstraint(type: Type) { return type.flags & TypeFlags.TypeVariable && this.maybeTypeOfKind(this.getBaseConstraintOfType(type) || this.emptyObjectType, TypeFlags.Nullable); } public getDeclaredOrApparentType(symbol: Symbol, node: Node) { // When a node is the left hand expression of a property access, element access, or call expression, // and the type of the node includes type variables with constraints that are nullable, we fetch the // apparent type of the node *before* performing control flow analysis such that narrowings apply to // the constraint type. const type = this.getTypeOfSymbol(symbol); if (this.isApparentTypePosition(node) && this.forEachType(type, this.typeHasNullableConstraint)) { return this.mapType(this.getWidenedType(type), this.getApparentType); } return type; } public checkIdentifier(node: Identifier): Type { const symbol = this.getResolvedSymbol(node); if (symbol === this.unknownSymbol) { return this.unknownType; } // As noted in ECMAScript 6 language spec, arrow functions never have an arguments objects. // Although in down-level emit of arrow function, we emit it using function expression which means that // arguments objects will be bound to the inner object; emitting arrow function natively in ES6, arguments objects // will be bound to non-arrow function that contain this arrow function. This results in inconsistent behavior. // To avoid that we will give an error to users if they use arguments objects in arrow function so that they // can explicitly bound arguments objects if (symbol === this.argumentsSymbol) { const container = getContainingFunction(node); if (this.languageVersion < ScriptTarget.ES2015) { if (container.kind === SyntaxKind.ArrowFunction) { this.error(node, Diagnostics.The_arguments_object_cannot_be_referenced_in_an_arrow_function_in_ES3_and_ES5_Consider_using_a_standard_function_expression); } else if (hasModifier(container, ModifierFlags.Async)) { this.error(node, Diagnostics.The_arguments_object_cannot_be_referenced_in_an_async_function_or_method_in_ES3_and_ES5_Consider_using_a_standard_function_or_method); } } this.getNodeLinks(container).flags |= NodeCheckFlags.CaptureArguments; return this.getTypeOfSymbol(symbol); } // We should only mark aliases as referenced if there isn't a local value declaration // for the symbol. if (this.isNonLocalAlias(symbol, /*excludes*/ SymbolFlags.Value) && !this.isInTypeQuery(node) && !this.isConstEnumOrConstEnumOnlyModule(this.resolveAlias(symbol))) { this.markAliasSymbolAsReferenced(symbol); } const localOrExportSymbol = this.getExportSymbolOfValueSymbolIfExported(symbol); let declaration = localOrExportSymbol.valueDeclaration; if (localOrExportSymbol.flags & SymbolFlags.Class) { // Due to the emit for class decorators, any reference to the class from inside of the class body // must instead be rewritten to point to a temporary variable to avoid issues with the double-bind // behavior of class names in ES6. if (declaration.kind === SyntaxKind.ClassDeclaration && nodeIsDecorated(declaration)) { let container = getContainingClass(node); while (container !== undefined) { if (container === declaration && container.name !== node) { this.getNodeLinks(declaration).flags |= NodeCheckFlags.ClassWithConstructorReference; this.getNodeLinks(node).flags |= NodeCheckFlags.ConstructorReferenceInClass; break; } container = getContainingClass(container); } } else if (declaration.kind === SyntaxKind.ClassExpression) { // When we emit a class expression with static members that contain a reference // to the constructor in the initializer, we will need to substitute that // binding with an alias as the class name is not in scope. let container = getThisContainer(node, /*includeArrowFunctions*/ false); while (container !== undefined) { if (container.parent === declaration) { if (container.kind === SyntaxKind.PropertyDeclaration && hasModifier(container, ModifierFlags.Static)) { this.getNodeLinks(declaration).flags |= NodeCheckFlags.ClassWithConstructorReference; this.getNodeLinks(node).flags |= NodeCheckFlags.ConstructorReferenceInClass; } break; } container = getThisContainer(container, /*includeArrowFunctions*/ false); } } } this.checkCollisionWithCapturedSuperVariable(node, node); this.checkCollisionWithCapturedThisVariable(node, node); this.checkCollisionWithCapturedNewTargetVariable(node, node); this.checkNestedBlockScopedBinding(node, symbol); const type = this.getDeclaredOrApparentType(localOrExportSymbol, node); const assignmentKind = getAssignmentTargetKind(node); if (assignmentKind) { if (!(localOrExportSymbol.flags & SymbolFlags.Variable)) { this.error(node, Diagnostics.Cannot_assign_to_0_because_it_is_not_a_variable, this.symbolToString(symbol)); return this.unknownType; } if (this.isReadonlySymbol(localOrExportSymbol)) { this.error(node, Diagnostics.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property, this.symbolToString(symbol)); return this.unknownType; } } const isAlias = localOrExportSymbol.flags & SymbolFlags.Alias; // We only narrow variables and parameters occurring in a non-assignment position. For all other // entities we simply return the declared type. if (localOrExportSymbol.flags & SymbolFlags.Variable) { if (assignmentKind === AssignmentKind.Definite) { return type; } } else if (isAlias) { declaration = find(symbol.declarations, isSomeImportDeclaration); } else { return type; } if (!declaration) { return type; } // The declaration container is the innermost function that encloses the declaration of the variable // or parameter. The flow container is the innermost function starting with which we analyze the control // flow graph to determine the control flow based type. const isParameter = getRootDeclaration(declaration).kind === SyntaxKind.Parameter; const declarationContainer = this.getControlFlowContainer(declaration); let flowContainer = this.getControlFlowContainer(node); const isOuterVariable = flowContainer !== declarationContainer; // When the control flow originates in a function expression or arrow function and we are referencing // a const variable or parameter from an outer function, we extend the origin of the control flow // analysis to include the immediately enclosing function. while (flowContainer !== declarationContainer && (flowContainer.kind === SyntaxKind.FunctionExpression || flowContainer.kind === SyntaxKind.ArrowFunction || isObjectLiteralOrClassExpressionMethod(flowContainer)) && (this.isConstVariable(localOrExportSymbol) || isParameter && !this.isParameterAssigned(localOrExportSymbol))) { flowContainer = this.getControlFlowContainer(flowContainer); } // We only look for uninitialized variables in strict null checking mode, and only when we can analyze // the entire control flow graph from the variable's declaration (i.e. when the flow container and // declaration container are the same). const assumeInitialized = isParameter || isAlias || isOuterVariable || type !== this.autoType && type !== this.autoArrayType && (!this.strictNullChecks || (type.flags & TypeFlags.Any) !== 0 || this.isInTypeQuery(node) || node.parent.kind === SyntaxKind.ExportSpecifier) || node.parent.kind === SyntaxKind.NonNullExpression || isInAmbientContext(declaration); const initialType = assumeInitialized ? (isParameter ? this.removeOptionalityFromDeclaredType(type, (getRootDeclaration(declaration) as VariableLikeDeclaration)) : type) : type === this.autoType || type === this.autoArrayType ? this.undefinedType : this.getNullableType(type, TypeFlags.Undefined); const flowType = this.getFlowTypeOfReference(node, type, initialType, flowContainer, !assumeInitialized); // A variable is considered uninitialized when it is possible to analyze the entire control flow graph // from declaration to use, and when the variable's declared type doesn't include undefined but the // control flow based type does include undefined. if (type === this.autoType || type === this.autoArrayType) { if (flowType === this.autoType || flowType === this.autoArrayType) { if (this.noImplicitAny) { this.error(getNameOfDeclaration(declaration), Diagnostics.Variable_0_implicitly_has_type_1_in_some_locations_where_its_type_cannot_be_determined, this.symbolToString(symbol), this.typeToString(flowType)); this.error(node, Diagnostics.Variable_0_implicitly_has_an_1_type, this.symbolToString(symbol), this.typeToString(flowType)); } return this.convertAutoToAny(flowType); } } else if (!assumeInitialized && !(this.getFalsyFlags(type) & TypeFlags.Undefined) && this.getFalsyFlags(flowType) & TypeFlags.Undefined) { this.error(node, Diagnostics.Variable_0_is_used_before_being_assigned, this.symbolToString(symbol)); // Return the declared type to reduce follow-on errors return type; } return assignmentKind ? this.getBaseTypeOfLiteralType(flowType) : flowType; } public isInsideFunction(node: Node, threshold: Node): boolean { return !!findAncestor(node, n => n === threshold ? "quit" : isFunctionLike(n)); } public checkNestedBlockScopedBinding(node: Identifier, symbol: Symbol): void { if (this.languageVersion >= ScriptTarget.ES2015 || (symbol.flags & (SymbolFlags.BlockScopedVariable | SymbolFlags.Class)) === 0 || symbol.valueDeclaration.parent.kind === SyntaxKind.CatchClause) { return; } // 1. walk from the use site up to the declaration and check // if there is anything function like between declaration and use-site (is binding/class is captured in function). // 2. walk from the declaration up to the boundary of lexical environment and check // if there is an iteration statement in between declaration and boundary (is binding/class declared inside iteration statement) const container = getEnclosingBlockScopeContainer(symbol.valueDeclaration); const usedInFunction = this.isInsideFunction(node.parent, container); let current = container; let containedInIterationStatement = false; while (current && !nodeStartsNewLexicalEnvironment(current)) { if (isIterationStatement(current, /*lookInLabeledStatements*/ false)) { containedInIterationStatement = true; break; } current = current.parent; } if (containedInIterationStatement) { if (usedInFunction) { // mark iteration statement as containing block-scoped binding captured in some function this.getNodeLinks(current).flags |= NodeCheckFlags.LoopWithCapturedBlockScopedBinding; } // mark variables that are declared in loop initializer and reassigned inside the body of ForStatement. // if body of ForStatement will be converted to function then we'll need a extra machinery to propagate reassigned values back. if (container.kind === SyntaxKind.ForStatement && getAncestor(symbol.valueDeclaration, SyntaxKind.VariableDeclarationList).parent === container && this.isAssignedInBodyOfForStatement(node, (container))) { this.getNodeLinks(symbol.valueDeclaration).flags |= NodeCheckFlags.NeedsLoopOutParameter; } // set 'declared inside loop' bit on the block-scoped binding this.getNodeLinks(symbol.valueDeclaration).flags |= NodeCheckFlags.BlockScopedBindingInLoop; } if (usedInFunction) { this.getNodeLinks(symbol.valueDeclaration).flags |= NodeCheckFlags.CapturedBlockScopedBinding; } } public isAssignedInBodyOfForStatement(node: Identifier, container: ForStatement): boolean { // skip parenthesized nodes let current: Node = node; while (current.parent.kind === SyntaxKind.ParenthesizedExpression) { current = current.parent; } // check if node is used as LHS in some assignment expression let isAssigned = false; if (isAssignmentTarget(current)) { isAssigned = true; } else if ((current.parent.kind === SyntaxKind.PrefixUnaryExpression || current.parent.kind === SyntaxKind.PostfixUnaryExpression)) { const expr = current.parent; isAssigned = expr.operator === SyntaxKind.PlusPlusToken || expr.operator === SyntaxKind.MinusMinusToken; } if (!isAssigned) { return false; } // at this point we know that node is the target of assignment // now check that modification happens inside the statement part of the ForStatement return !!findAncestor(current, n => n === container ? "quit" : n === container.statement); } public captureLexicalThis(node: Node, container: Node): void { this.getNodeLinks(node).flags |= NodeCheckFlags.LexicalThis; if (container.kind === SyntaxKind.PropertyDeclaration || container.kind === SyntaxKind.Constructor) { const classNode = container.parent; this.getNodeLinks(classNode).flags |= NodeCheckFlags.CaptureThis; } else { this.getNodeLinks(container).flags |= NodeCheckFlags.CaptureThis; } } public findFirstSuperCall(n: Node): Node { if (isSuperCall(n)) { return n; } else if (isFunctionLike(n)) { return undefined; } return forEachChild(n, this.findFirstSuperCall); } /** * Return a cached result if super-statement is already found. * Otherwise, find a super statement in a given constructor function and cache the result in the node-links of the constructor * * @param constructor constructor-function to look for super statement */ public getSuperCallInConstructor(constructor: ConstructorDeclaration): ExpressionStatement { const links = this.getNodeLinks(constructor); // Only trying to find super-call if we haven't yet tried to find one. Once we try, we will record the result if (links.hasSuperCall === undefined) { links.superCall = (this.findFirstSuperCall(constructor.body)); links.hasSuperCall = links.superCall ? true : false; } return links.superCall; } /** * Check if the given class-declaration extends null then return true. * Otherwise, return false * @param classDecl a class declaration to check if it extends null */ public classDeclarationExtendsNull(classDecl: ClassDeclaration): boolean { const classSymbol = this.getSymbolOfNode(classDecl); const classInstanceType = (this.getDeclaredTypeOfSymbol(classSymbol)); const baseConstructorType = this.getBaseConstructorTypeOfClass(classInstanceType); return baseConstructorType === this.nullWideningType; } public checkThisBeforeSuper(node: Node, container: Node, diagnosticMessage: DiagnosticMessage) { const containingClassDecl = container.parent; const baseTypeNode = getClassExtendsHeritageClauseElement(containingClassDecl); // If a containing class does not have extends clause or the class extends null // skip checking whether super statement is called before "this" accessing. if (baseTypeNode && !this.classDeclarationExtendsNull(containingClassDecl)) { const superCall = this.getSuperCallInConstructor((container)); // We should give an error in the following cases: // - No super-call // - "this" is accessing before super-call. // i.e super(this) // this.x; super(); // We want to make sure that super-call is done before accessing "this" so that // "this" is not accessed as a parameter of the super-call. if (!superCall || superCall.end > node.pos) { // In ES6, super inside constructor of class-declaration has to precede "this" accessing this.error(node, diagnosticMessage); } } } public checkThisExpression(node: Node): Type { // Stop at the first arrow function so that we can // tell whether 'this' needs to be captured. let container = getThisContainer(node, /* includeArrowFunctions */ true); let needToCaptureLexicalThis = false; if (container.kind === SyntaxKind.Constructor) { this.checkThisBeforeSuper(node, container, Diagnostics.super_must_be_called_before_accessing_this_in_the_constructor_of_a_derived_class); } // Now skip arrow functions to get the "real" owner of 'this'. if (container.kind === SyntaxKind.ArrowFunction) { container = getThisContainer(container, /* includeArrowFunctions */ false); // When targeting es6, arrow function lexically bind "this" so we do not need to do the work of binding "this" in emitted code needToCaptureLexicalThis = (this.languageVersion < ScriptTarget.ES2015); } switch (container.kind) { case SyntaxKind.ModuleDeclaration: this.error(node, Diagnostics.this_cannot_be_referenced_in_a_module_or_namespace_body); // do not return here so in case if lexical this is captured - it will be reflected in flags on NodeLinks break; case SyntaxKind.EnumDeclaration: this.error(node, Diagnostics.this_cannot_be_referenced_in_current_location); // do not return here so in case if lexical this is captured - it will be reflected in flags on NodeLinks break; case SyntaxKind.Constructor: if (this.isInConstructorArgumentInitializer(node, container)) { this.error(node, Diagnostics.this_cannot_be_referenced_in_constructor_arguments); // do not return here so in case if lexical this is captured - it will be reflected in flags on NodeLinks } break; case SyntaxKind.PropertyDeclaration: case SyntaxKind.PropertySignature: if (hasModifier(container, ModifierFlags.Static)) { this.error(node, Diagnostics.this_cannot_be_referenced_in_a_static_property_initializer); // do not return here so in case if lexical this is captured - it will be reflected in flags on NodeLinks } break; case SyntaxKind.ComputedPropertyName: this.error(node, Diagnostics.this_cannot_be_referenced_in_a_computed_property_name); break; } if (needToCaptureLexicalThis) { this.captureLexicalThis(node, container); } if (isFunctionLike(container) && (!this.isInParameterInitializerBeforeContainingFunction(node) || getThisParameter(container))) { // Note: a parameter initializer should refer to class-this unless function-this is explicitly annotated. // If this is a function in a JS file, it might be a class method. Check if it's the RHS // of a x.prototype.y = function [name]() { .... } if (container.kind === SyntaxKind.FunctionExpression && container.parent.kind === SyntaxKind.BinaryExpression && getSpecialPropertyAssignmentKind(container.parent as BinaryExpression) === SpecialPropertyAssignmentKind.PrototypeProperty) { // Get the 'x' of 'x.prototype.y = f' (here, 'f' is 'container') const className = (((container.parent as BinaryExpression) // x.prototype.y = f .left as PropertyAccessExpression) // x.prototype.y .expression as PropertyAccessExpression) // x.prototype .expression; // x const classSymbol = this.checkExpression(className).symbol; if (classSymbol && classSymbol.members && (classSymbol.flags & SymbolFlags.Function)) { return this.getInferredClassType(classSymbol); } } const thisType = this.getThisTypeOfDeclaration(container) || this.getContextualThisParameterType(container); if (thisType) { return thisType; } } if (isClassLike(container.parent)) { const symbol = this.getSymbolOfNode(container.parent); const type = hasModifier(container, ModifierFlags.Static) ? this.getTypeOfSymbol(symbol) : (this.getDeclaredTypeOfSymbol(symbol)).thisType; return this.getFlowTypeOfReference(node, type); } if (isInJavaScriptFile(node)) { const type = this.getTypeForThisExpressionFromJSDoc(container); if (type && type !== this.unknownType) { return type; } } if (this.noImplicitThis) { // With noImplicitThis, functions may not reference 'this' if it has type 'any' this.error(node, Diagnostics.this_implicitly_has_type_any_because_it_does_not_have_a_type_annotation); } return this.anyType; } public getTypeForThisExpressionFromJSDoc(node: Node) { const jsdocType = getJSDocType(node); if (jsdocType && jsdocType.kind === SyntaxKind.JSDocFunctionType) { const jsDocFunctionType = jsdocType; if (jsDocFunctionType.parameters.length > 0 && jsDocFunctionType.parameters[0].name && (jsDocFunctionType.parameters[0].name as Identifier).escapedText === "this") { return this.getTypeFromTypeNode(jsDocFunctionType.parameters[0].type); } } } public isInConstructorArgumentInitializer(node: Node, constructorDecl: Node): boolean { return !!findAncestor(node, n => n === constructorDecl ? "quit" : n.kind === SyntaxKind.Parameter); } public checkSuperExpression(node: Node): Type { const isCallExpression = node.parent.kind === SyntaxKind.CallExpression && (node.parent).expression === node; let container = getSuperContainer(node, /*stopOnFunctions*/ true); let needToCaptureLexicalThis = false; // adjust the container reference in case if super is used inside arrow functions with arbitrarily deep nesting if (!isCallExpression) { while (container && container.kind === SyntaxKind.ArrowFunction) { container = getSuperContainer(container, /*stopOnFunctions*/ true); needToCaptureLexicalThis = this.languageVersion < ScriptTarget.ES2015; } } const canUseSuperExpression = isLegalUsageOfSuperExpression(container); let nodeCheckFlag: NodeCheckFlags = 0; if (!canUseSuperExpression) { // issue more specific error if super is used in computed property name // class A { foo() { return "1" }} // class B { // [super.foo()]() {} // } const current = findAncestor(node, n => n === container ? "quit" : n.kind === SyntaxKind.ComputedPropertyName); if (current && current.kind === SyntaxKind.ComputedPropertyName) { this.error(node, Diagnostics.super_cannot_be_referenced_in_a_computed_property_name); } else if (isCallExpression) { this.error(node, Diagnostics.Super_calls_are_not_permitted_outside_constructors_or_in_nested_functions_inside_constructors); } else if (!container || !container.parent || !(isClassLike(container.parent) || container.parent.kind === SyntaxKind.ObjectLiteralExpression)) { this.error(node, Diagnostics.super_can_only_be_referenced_in_members_of_derived_classes_or_object_literal_expressions); } else { this.error(node, Diagnostics.super_property_access_is_permitted_only_in_a_constructor_member_function_or_member_accessor_of_a_derived_class); } return this.unknownType; } if (!isCallExpression && container.kind === SyntaxKind.Constructor) { this.checkThisBeforeSuper(node, container, Diagnostics.super_must_be_called_before_accessing_a_property_of_super_in_the_constructor_of_a_derived_class); } if (hasModifier(container, ModifierFlags.Static) || isCallExpression) { nodeCheckFlag = NodeCheckFlags.SuperStatic; } else { nodeCheckFlag = NodeCheckFlags.SuperInstance; } this.getNodeLinks(node).flags |= nodeCheckFlag; // Due to how we emit async functions, we need to specialize the emit for an async method that contains a `super` reference. // This is due to the fact that we emit the body of an async function inside of a generator function. As generator // functions cannot reference `super`, we emit a helper inside of the method body, but outside of the generator. This helper // uses an arrow function, which is permitted to reference `super`. // // There are two primary ways we can access `super` from within an async method. The first is getting the value of a property // or indexed access on super, either as part of a right-hand-side expression or call expression. The second is when setting the value // of a property or indexed access, either as part of an assignment expression or destructuring assignment. // // The simplest case is reading a value, in which case we will emit something like the following: // // // ts // ... // async asyncMethod() { // let x = await super.asyncMethod(); // return x; // } // ... // // // js // ... // asyncMethod() { // const _super = name => super[name]; // return __awaiter(this, arguments, Promise, function *() { // let x = yield _super("asyncMethod").call(this); // return x; // }); // } // ... // // The more complex case is when we wish to assign a value, especially as part of a destructuring assignment. As both cases // are legal in ES6, but also likely less frequent, we emit the same more complex helper for both scenarios: // // // ts // ... // async asyncMethod(ar: Promise) { // [super.a, super.b] = await ar; // } // ... // // // js // ... // asyncMethod(ar) { // const _super = (function (geti, seti) { // const cache = Object.create(null); // return name => cache[name] || (cache[name] = { get value() { return geti(name); }, set value(v) { seti(name, v); } }); // })(name => super[name], (name, value) => super[name] = value); // return __awaiter(this, arguments, Promise, function *() { // [_super("a").value, _super("b").value] = yield ar; // }); // } // ... // // This helper creates an object with a "value" property that wraps the `super` property or indexed access for both get and set. // This is required for destructuring assignments, as a call expression cannot be used as the target of a destructuring assignment // while a property access can. if (container.kind === SyntaxKind.MethodDeclaration && hasModifier(container, ModifierFlags.Async)) { if (isSuperProperty(node.parent) && isAssignmentTarget(node.parent)) { this.getNodeLinks(container).flags |= NodeCheckFlags.AsyncMethodWithSuperBinding; } else { this.getNodeLinks(container).flags |= NodeCheckFlags.AsyncMethodWithSuper; } } if (needToCaptureLexicalThis) { // call expressions are allowed only in constructors so they should always capture correct 'this' // super property access expressions can also appear in arrow functions - // in this case they should also use correct lexical this this.captureLexicalThis(node.parent, container); } if (container.parent.kind === SyntaxKind.ObjectLiteralExpression) { if (this.languageVersion < ScriptTarget.ES2015) { this.error(node, Diagnostics.super_is_only_allowed_in_members_of_object_literal_expressions_when_option_target_is_ES2015_or_higher); return this.unknownType; } else { // for object literal assume that type of 'super' is 'any' return this.anyType; } } // at this point the only legal case for parent is ClassLikeDeclaration const classLikeDeclaration = container.parent; const classType = (this.getDeclaredTypeOfSymbol(this.getSymbolOfNode(classLikeDeclaration))); const baseClassType = classType && this.getBaseTypes(classType)[0]; if (!baseClassType) { if (!getClassExtendsHeritageClauseElement(classLikeDeclaration)) { this.error(node, Diagnostics.super_can_only_be_referenced_in_a_derived_class); } return this.unknownType; } if (container.kind === SyntaxKind.Constructor && this.isInConstructorArgumentInitializer(node, container)) { // issue custom error message for super property access in constructor arguments (to be aligned with old compiler) this.error(node, Diagnostics.super_cannot_be_referenced_in_constructor_arguments); return this.unknownType; } return nodeCheckFlag === NodeCheckFlags.SuperStatic ? this.getBaseConstructorTypeOfClass(classType) : this.getTypeWithThisArgument(baseClassType, classType.thisType); function isLegalUsageOfSuperExpression(container: Node): boolean { if (!container) { return false; } if (isCallExpression) { // TS 1.0 SPEC (April 2014): 4.8.1 // Super calls are only permitted in constructors of derived classes return container.kind === SyntaxKind.Constructor; } else { // TS 1.0 SPEC (April 2014) // 'super' property access is allowed // - In a constructor, instance member function, instance member accessor, or instance member variable initializer where this references a derived class instance // - In a static member function or static member accessor // topmost container must be something that is directly nested in the class declaration\object literal expression if (isClassLike(container.parent) || container.parent.kind === SyntaxKind.ObjectLiteralExpression) { if (hasModifier(container, ModifierFlags.Static)) { return container.kind === SyntaxKind.MethodDeclaration || container.kind === SyntaxKind.MethodSignature || container.kind === SyntaxKind.GetAccessor || container.kind === SyntaxKind.SetAccessor; } else { return container.kind === SyntaxKind.MethodDeclaration || container.kind === SyntaxKind.MethodSignature || container.kind === SyntaxKind.GetAccessor || container.kind === SyntaxKind.SetAccessor || container.kind === SyntaxKind.PropertyDeclaration || container.kind === SyntaxKind.PropertySignature || container.kind === SyntaxKind.Constructor; } } } return false; } } public getContainingObjectLiteral(func: FunctionLike) { return (func.kind === SyntaxKind.MethodDeclaration || func.kind === SyntaxKind.GetAccessor || func.kind === SyntaxKind.SetAccessor) && func.parent.kind === SyntaxKind.ObjectLiteralExpression ? func.parent : func.kind === SyntaxKind.FunctionExpression && func.parent.kind === SyntaxKind.PropertyAssignment ? func.parent.parent : undefined; } public getThisTypeArgument(type: Type): Type { return this.getObjectFlags(type) & ObjectFlags.Reference && (type).target === this.globalThisType ? (type).typeArguments[0] : undefined; } public getThisTypeFromContextualType(type: Type): Type { const __conv_self__ = this; return this.mapType(type, t => { return t.flags & TypeFlags.Intersection ? forEach((t).types, __conv_self__.getThisTypeArgument) : __conv_self__.getThisTypeArgument(t); }); } public getContextualThisParameterType(func: FunctionLike): Type { if (func.kind === SyntaxKind.ArrowFunction) { return undefined; } if (this.isContextSensitiveFunctionOrObjectLiteralMethod(func)) { const contextualSignature = this.getContextualSignature(func); if (contextualSignature) { const thisParameter = contextualSignature.thisParameter; if (thisParameter) { return this.getTypeOfSymbol(thisParameter); } } } if (this.noImplicitThis || isInJavaScriptFile(func)) { const containingLiteral = this.getContainingObjectLiteral(func); if (containingLiteral) { // We have an object literal method. Check if the containing object literal has a contextual type // that includes a ThisType. If so, T is the contextual type for 'this'. We continue looking in // any directly enclosing object literals. const contextualType = this.getApparentTypeOfContextualType(containingLiteral); let literal = containingLiteral; let type = contextualType; while (type) { const thisType = this.getThisTypeFromContextualType(type); if (thisType) { return this.instantiateType(thisType, this.getContextualMapper(containingLiteral)); } if (literal.parent.kind !== SyntaxKind.PropertyAssignment) { break; } literal = literal.parent.parent; type = this.getApparentTypeOfContextualType(literal); } // There was no contextual ThisType for the containing object literal, so the contextual type // for 'this' is the non-null form of the contextual type for the containing object literal or // the type of the object literal itself. return contextualType ? this.getNonNullableType(contextualType) : this.checkExpressionCached(containingLiteral); } // In an assignment of the form 'obj.xxx = function(...)' or 'obj[xxx] = function(...)', the // contextual type for 'this' is 'obj'. if (func.parent.kind === SyntaxKind.BinaryExpression && (func.parent).operatorToken.kind === SyntaxKind.EqualsToken) { const target = (func.parent).left; if (target.kind === SyntaxKind.PropertyAccessExpression || target.kind === SyntaxKind.ElementAccessExpression) { return this.checkExpressionCached((target).expression); } } } return undefined; } // Return contextual type of parameter or undefined if no contextual type is available public getContextuallyTypedParameterType(parameter: ParameterDeclaration): Type { const func = parameter.parent; if (this.isContextSensitiveFunctionOrObjectLiteralMethod(func)) { const iife = getImmediatelyInvokedFunctionExpression(func); if (iife && iife.arguments) { const indexOfParameter = indexOf(func.parameters, parameter); if (parameter.dotDotDotToken) { const restTypes: Type[] = []; for (let i = indexOfParameter; i < iife.arguments.length; i++) { restTypes.push(this.getWidenedLiteralType(this.checkExpression(iife.arguments[i]))); } return restTypes.length ? this.createArrayType(this.getUnionType(restTypes)) : undefined; } const links = this.getNodeLinks(iife); const cached = links.resolvedSignature; links.resolvedSignature = this.anySignature; const type = indexOfParameter < iife.arguments.length ? this.getWidenedLiteralType(this.checkExpression(iife.arguments[indexOfParameter])) : parameter.initializer ? undefined : this.undefinedWideningType; links.resolvedSignature = cached; return type; } const contextualSignature = this.getContextualSignature(func); if (contextualSignature) { const funcHasRestParameters = hasRestParameter(func); const len = func.parameters.length - (funcHasRestParameters ? 1 : 0); const indexOfParameter = indexOf(func.parameters, parameter); if (indexOfParameter < len) { return this.getTypeAtPosition(contextualSignature, indexOfParameter); } // If last parameter is contextually rest parameter get its type if (funcHasRestParameters && indexOfParameter === (func.parameters.length - 1) && this.isRestParameterIndex(contextualSignature, func.parameters.length - 1)) { return this.getTypeOfSymbol(lastOrUndefined(contextualSignature.parameters)); } } } return undefined; } // In a variable, parameter or property declaration with a type annotation, // the contextual type of an initializer expression is the type of the variable, parameter or property. // Otherwise, in a parameter declaration of a contextually typed function expression, // the contextual type of an initializer expression is the contextual type of the parameter. // Otherwise, in a variable or parameter declaration with a binding pattern name, // the contextual type of an initializer expression is the type implied by the binding pattern. // Otherwise, in a binding pattern inside a variable or parameter declaration, // the contextual type of an initializer expression is the type annotation of the containing declaration, if present. public getContextualTypeForInitializerExpression(node: Expression): Type { const declaration = node.parent; if (node === declaration.initializer) { const typeNode = getEffectiveTypeAnnotationNode(declaration); if (typeNode) { return this.getTypeFromTypeNode(typeNode); } if (declaration.kind === SyntaxKind.Parameter) { const type = this.getContextuallyTypedParameterType((declaration)); if (type) { return type; } } if (isBindingPattern(declaration.name)) { return this.getTypeFromBindingPattern((declaration.name), /*includePatternInType*/ true, /*reportErrors*/ false); } if (isBindingPattern(declaration.parent)) { const parentDeclaration = declaration.parent.parent; const name = declaration.propertyName || declaration.name; if (parentDeclaration.kind !== SyntaxKind.BindingElement) { const parentTypeNode = getEffectiveTypeAnnotationNode(parentDeclaration); if (parentTypeNode && !isBindingPattern(name)) { const text = getTextOfPropertyName(name); if (text) { return this.getTypeOfPropertyOfType(this.getTypeFromTypeNode(parentTypeNode), text); } } } } } return undefined; } public getContextualTypeForReturnExpression(node: Expression): Type { const func = getContainingFunction(node); if (func) { const functionFlags = getFunctionFlags(func); if (functionFlags & FunctionFlags.Generator) { return undefined; } const contextualReturnType = this.getContextualReturnType(func); return functionFlags & FunctionFlags.Async ? contextualReturnType && this.getAwaitedTypeOfPromise(contextualReturnType) // Async function : contextualReturnType; // Regular function } return undefined; } public getContextualTypeForYieldOperand(node: YieldExpression): Type { const func = getContainingFunction(node); if (func) { const functionFlags = getFunctionFlags(func); const contextualReturnType = this.getContextualReturnType(func); if (contextualReturnType) { return node.asteriskToken ? contextualReturnType : this.getIteratedTypeOfGenerator(contextualReturnType, (functionFlags & FunctionFlags.Async) !== 0); } } return undefined; } public isInParameterInitializerBeforeContainingFunction(node: Node) { while (node.parent && !isFunctionLike(node.parent)) { if (node.parent.kind === SyntaxKind.Parameter && (node.parent).initializer === node) { return true; } node = node.parent; } return false; } public getContextualReturnType(functionDecl: FunctionLike): Type { // If the containing function has a return type annotation, is a constructor, or is a get accessor whose // corresponding set accessor has a type annotation, return statements in the function are contextually typed if (functionDecl.kind === SyntaxKind.Constructor || getEffectiveReturnTypeNode(functionDecl) || this.isGetAccessorWithAnnotatedSetAccessor(functionDecl)) { return this.getReturnTypeOfSignature(this.getSignatureFromDeclaration(functionDecl)); } // Otherwise, if the containing function is contextually typed by a function type with exactly one call signature // and that call signature is non-generic, return statements are contextually typed by the return type of the signature const signature = this.getContextualSignatureForFunctionLikeDeclaration((functionDecl)); if (signature) { return this.getReturnTypeOfSignature(signature); } return undefined; } // In a typed function call, an argument or substitution expression is contextually typed by the type of the corresponding parameter. public getContextualTypeForArgument(callTarget: CallLikeExpression, arg: Expression): Type { const args = this.getEffectiveCallArguments(callTarget); const argIndex = indexOf(args, arg); if (argIndex >= 0) { // If we're already in the process of resolving the given signature, don't resolve again as // that could cause infinite recursion. Instead, return anySignature. const signature = this.getNodeLinks(callTarget).resolvedSignature === this.resolvingSignature ? this.resolvingSignature : this.getResolvedSignature(callTarget); return this.getTypeAtPosition(signature, argIndex); } return undefined; } public getContextualTypeForSubstitutionExpression(template: TemplateExpression, substitutionExpression: Expression) { if (template.parent.kind === SyntaxKind.TaggedTemplateExpression) { return this.getContextualTypeForArgument((template.parent), substitutionExpression); } return undefined; } public getContextualTypeForBinaryOperand(node: Expression): Type { const binaryExpression = node.parent; const operator = binaryExpression.operatorToken.kind; if (isAssignmentOperator(operator)) { // Don't do this for special property assignments to avoid circularity if (getSpecialPropertyAssignmentKind(binaryExpression) !== SpecialPropertyAssignmentKind.None) { return undefined; } // In an assignment expression, the right operand is contextually typed by the type of the left operand. if (node === binaryExpression.right) { return this.getTypeOfExpression(binaryExpression.left); } } else if (operator === SyntaxKind.BarBarToken) { // When an || expression has a contextual type, the operands are contextually typed by that type. When an || // expression has no contextual type, the right operand is contextually typed by the type of the left operand. let type = this.getContextualType(binaryExpression); if (!type && node === binaryExpression.right) { type = this.getTypeOfExpression(binaryExpression.left); } return type; } else if (operator === SyntaxKind.AmpersandAmpersandToken || operator === SyntaxKind.CommaToken) { if (node === binaryExpression.right) { return this.getContextualType(binaryExpression); } } return undefined; } public getTypeOfPropertyOfContextualType(type: Type, name: __String) { const __conv_self__ = this; return this.mapType(type, t => { const prop = t.flags & TypeFlags.StructuredType ? __conv_self__.getPropertyOfType(t, name) : undefined; return prop ? __conv_self__.getTypeOfSymbol(prop) : undefined; }); } public getIndexTypeOfContextualType(type: Type, kind: IndexKind) { const __conv_self__ = this; return this.mapType(type, t => __conv_self__.getIndexTypeOfStructuredType(t, kind)); } // Return true if the given contextual type is a tuple-like type public contextualTypeIsTupleLikeType(type: Type): boolean { return !!(type.flags & TypeFlags.Union ? forEach((type).types, this.isTupleLikeType) : this.isTupleLikeType(type)); } // In an object literal contextually typed by a type T, the contextual type of a property assignment is the type of // the matching property in T, if one exists. Otherwise, it is the type of the numeric index signature in T, if one // exists. Otherwise, it is the type of the string index signature in T, if one exists. public getContextualTypeForObjectLiteralMethod(node: MethodDeclaration): Type { Debug.assert(isObjectLiteralMethod(node)); if (this.isInsideWithStatementBody(node)) { // We cannot answer semantic questions within a with block, do not proceed any further return undefined; } return this.getContextualTypeForObjectLiteralElement(node); } public getContextualTypeForObjectLiteralElement(element: ObjectLiteralElementLike) { const objectLiteral = element.parent; const type = this.getApparentTypeOfContextualType(objectLiteral); if (type) { if (!hasDynamicName(element)) { // For a (non-symbol) computed property, there is no reason to look up the name // in the type. It will just be "__computed", which does not appear in any // SymbolTable. const symbolName = this.getSymbolOfNode(element).escapedName; const propertyType = this.getTypeOfPropertyOfContextualType(type, symbolName); if (propertyType) { return propertyType; } } return this.isNumericName(element.name) && this.getIndexTypeOfContextualType(type, IndexKind.Number) || this.getIndexTypeOfContextualType(type, IndexKind.String); } return undefined; } // In an array literal contextually typed by a type T, the contextual type of an element expression at index N is // the type of the property with the numeric name N in T, if one exists. Otherwise, if T has a numeric index signature, // it is the type of the numeric index signature in T. Otherwise, in ES6 and higher, the contextual type is the iterated // type of T. public getContextualTypeForElementExpression(node: Expression): Type { const arrayLiteral = node.parent; const type = this.getApparentTypeOfContextualType(arrayLiteral); if (type) { const index = indexOf(arrayLiteral.elements, node); return this.getTypeOfPropertyOfContextualType(type, ("" + index as __String)) || this.getIndexTypeOfContextualType(type, IndexKind.Number) || this.getIteratedTypeOrElementType(type, /*errorNode*/ undefined, /*allowStringInput*/ false, /*allowAsyncIterables*/ false, /*checkAssignability*/ false); } return undefined; } // In a contextually typed conditional expression, the true/false expressions are contextually typed by the same type. public getContextualTypeForConditionalOperand(node: Expression): Type { const conditional = node.parent; return node === conditional.whenTrue || node === conditional.whenFalse ? this.getContextualType(conditional) : undefined; } public getContextualTypeForJsxExpression(node: JsxExpression): Type { // JSX expression can appear in two position : JSX Element's children or JSX attribute const jsxAttributes = isJsxAttributeLike(node.parent) ? node.parent.parent : node.parent.openingElement.attributes; // node.parent is JsxElement // When we trying to resolve JsxOpeningLikeElement as a stateless function element, we will already give its attributes a contextual type // which is a type of the parameter of the signature we are trying out. // If there is no contextual type (e.g. we are trying to resolve stateful component), get attributes type from resolving element's tagName const attributesType = this.getContextualType(jsxAttributes); if (!attributesType || this.isTypeAny(attributesType)) { return undefined; } if (isJsxAttribute(node.parent)) { // JSX expression is in JSX attribute return this.getTypeOfPropertyOfType(attributesType, node.parent.name.escapedText); } else if (node.parent.kind === SyntaxKind.JsxElement) { // JSX expression is in children of JSX Element, we will look for an "children" atttribute (we get the name from JSX.ElementAttributesProperty) const jsxChildrenPropertyName = this.getJsxElementChildrenPropertyname(); return jsxChildrenPropertyName && jsxChildrenPropertyName !== "" ? this.getTypeOfPropertyOfType(attributesType, jsxChildrenPropertyName) : this.anyType; } else { // JSX expression is in JSX spread attribute return attributesType; } } public getContextualTypeForJsxAttribute(attribute: JsxAttribute | JsxSpreadAttribute) { // When we trying to resolve JsxOpeningLikeElement as a stateless function element, we will already give its attributes a contextual type // which is a type of the parameter of the signature we are trying out. // If there is no contextual type (e.g. we are trying to resolve stateful component), get attributes type from resolving element's tagName const attributesType = this.getContextualType((attribute.parent)); if (isJsxAttribute(attribute)) { if (!attributesType || this.isTypeAny(attributesType)) { return undefined; } return this.getTypeOfPropertyOfType(attributesType, attribute.name.escapedText); } else { return attributesType; } } // Return the contextual type for a given expression node. During overload resolution, a contextual type may temporarily // be "pushed" onto a node using the contextualType property. public getApparentTypeOfContextualType(node: Expression): Type { const type = this.getContextualType(node); return type && this.getApparentType(type); } /** * Woah! Do you really want to use this function? * * Unless you're trying to get the *non-apparent* type for a * value-literal type or you're authoring relevant portions of this algorithm, * you probably meant to use 'getApparentTypeOfContextualType'. * Otherwise this may not be very useful. * * In cases where you *are* working on this function, you should understand * when it is appropriate to use 'getContextualType' and 'getApparentTypeOfContextualType'. * * - Use 'getContextualType' when you are simply going to propagate the result to the expression. * - Use 'getApparentTypeOfContextualType' when you're going to need the members of the type. * * @param node the expression whose contextual type will be returned. * @returns the contextual type of an expression. */ public getContextualType(node: Expression): Type | undefined { if (this.isInsideWithStatementBody(node)) { // We cannot answer semantic questions within a with block, do not proceed any further return undefined; } if (node.contextualType) { return node.contextualType; } const parent = node.parent; switch (parent.kind) { case SyntaxKind.VariableDeclaration: case SyntaxKind.Parameter: case SyntaxKind.PropertyDeclaration: case SyntaxKind.PropertySignature: case SyntaxKind.BindingElement: return this.getContextualTypeForInitializerExpression(node); case SyntaxKind.ArrowFunction: case SyntaxKind.ReturnStatement: return this.getContextualTypeForReturnExpression(node); case SyntaxKind.YieldExpression: return this.getContextualTypeForYieldOperand((parent)); case SyntaxKind.CallExpression: case SyntaxKind.NewExpression: return this.getContextualTypeForArgument((parent), node); case SyntaxKind.TypeAssertionExpression: case SyntaxKind.AsExpression: return this.getTypeFromTypeNode((parent).type); case SyntaxKind.BinaryExpression: return this.getContextualTypeForBinaryOperand(node); case SyntaxKind.PropertyAssignment: case SyntaxKind.ShorthandPropertyAssignment: return this.getContextualTypeForObjectLiteralElement((parent)); case SyntaxKind.SpreadAssignment: return this.getApparentTypeOfContextualType((parent.parent as ObjectLiteralExpression)); case SyntaxKind.ArrayLiteralExpression: return this.getContextualTypeForElementExpression(node); case SyntaxKind.ConditionalExpression: return this.getContextualTypeForConditionalOperand(node); case SyntaxKind.TemplateSpan: Debug.assert(parent.parent.kind === SyntaxKind.TemplateExpression); return this.getContextualTypeForSubstitutionExpression((parent.parent), node); case SyntaxKind.ParenthesizedExpression: return this.getContextualType((parent)); case SyntaxKind.JsxExpression: return this.getContextualTypeForJsxExpression((parent)); case SyntaxKind.JsxAttribute: case SyntaxKind.JsxSpreadAttribute: return this.getContextualTypeForJsxAttribute((parent)); case SyntaxKind.JsxOpeningElement: case SyntaxKind.JsxSelfClosingElement: return this.getAttributesTypeFromJsxOpeningLikeElement((parent)); } return undefined; } public getContextualMapper(node: Node) { node = findAncestor(node, n => !!n.contextualMapper); return node ? node.contextualMapper : this.identityMapper; } // If the given type is an object or union type with a single signature, and if that signature has at // least as many parameters as the given function, return the signature. Otherwise return undefined. public getContextualCallSignature(type: Type, node: FunctionExpression | ArrowFunction | MethodDeclaration): Signature { const signatures = this.getSignaturesOfStructuredType(type, SignatureKind.Call); if (signatures.length === 1) { const signature = signatures[0]; if (!this.isAritySmaller(signature, node)) { return signature; } } } /** If the contextual signature has fewer parameters than the function expression, do not use it */ public isAritySmaller(signature: Signature, target: FunctionExpression | ArrowFunction | MethodDeclaration) { let targetParameterCount = 0; for (; targetParameterCount < target.parameters.length; targetParameterCount++) { const param = target.parameters[targetParameterCount]; if (param.initializer || param.questionToken || param.dotDotDotToken || this.isJSDocOptionalParameter(param)) { break; } } if (target.parameters.length && parameterIsThisKeyword(target.parameters[0])) { targetParameterCount--; } const sourceLength = signature.hasRestParameter ? Number.MAX_VALUE : signature.parameters.length; return sourceLength < targetParameterCount; } public isFunctionExpressionOrArrowFunction(node: Node): node is FunctionExpression | ArrowFunction { return node.kind === SyntaxKind.FunctionExpression || node.kind === SyntaxKind.ArrowFunction; } public getContextualSignatureForFunctionLikeDeclaration(node: FunctionLikeDeclaration): Signature { // Only function expressions, arrow functions, and object literal methods are contextually typed. return this.isFunctionExpressionOrArrowFunction(node) || isObjectLiteralMethod(node) ? this.getContextualSignature((node)) : undefined; } public getContextualTypeForFunctionLikeDeclaration(node: FunctionExpression | ArrowFunction | MethodDeclaration) { return isObjectLiteralMethod(node) ? this.getContextualTypeForObjectLiteralMethod(node) : this.getApparentTypeOfContextualType(node); } // Return the contextual signature for a given expression node. A contextual type provides a // contextual signature if it has a single call signature and if that call signature is non-generic. // If the contextual type is a union type, get the signature from each type possible and if they are // all identical ignoring their return type, the result is same signature but with return type as // union type of return types from these signatures public getContextualSignature(node: FunctionExpression | ArrowFunction | MethodDeclaration): Signature { Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isObjectLiteralMethod(node)); const type = this.getContextualTypeForFunctionLikeDeclaration(node); if (!type) { return undefined; } if (!(type.flags & TypeFlags.Union)) { return this.getContextualCallSignature(type, node); } let signatureList: Signature[]; const types = (type).types; for (const current of types) { const signature = this.getContextualCallSignature(current, node); if (signature) { if (!signatureList) { // This signature will contribute to contextual union signature signatureList = [signature]; } else if (!this.compareSignaturesIdentical(signatureList[0], signature, /*partialMatch*/ false, /*ignoreThisTypes*/ true, /*ignoreReturnTypes*/ true, this.compareTypesIdentical)) { // Signatures aren't identical, do not use return undefined; } else { // Use this signature for contextual union signature signatureList.push(signature); } } } // Result is union of signatures collected (return type is union of return types of this signature set) let result: Signature; if (signatureList) { result = this.cloneSignature(signatureList[0]); // Clear resolved return type we possibly got from cloneSignature result.resolvedReturnType = undefined; result.unionSignatures = signatureList; } return result; } public checkSpreadExpression(node: SpreadElement, checkMode?: CheckMode): Type { if (this.languageVersion < ScriptTarget.ES2015 && this.compilerOptions.downlevelIteration) { this.checkExternalEmitHelpers(node, ExternalEmitHelpers.SpreadIncludes); } const arrayOrIterableType = this.checkExpression(node.expression, checkMode); return this.checkIteratedTypeOrElementType(arrayOrIterableType, node.expression, /*allowStringInput*/ false, /*allowAsyncIterables*/ false); } public hasDefaultValue(node: BindingElement | Expression): boolean { return (node.kind === SyntaxKind.BindingElement && !!(node).initializer) || (node.kind === SyntaxKind.BinaryExpression && (node).operatorToken.kind === SyntaxKind.EqualsToken); } public checkArrayLiteral(node: ArrayLiteralExpression, checkMode?: CheckMode): Type { const elements = node.elements; let hasSpreadElement = false; const elementTypes: Type[] = []; const inDestructuringPattern = isAssignmentTarget(node); for (const e of elements) { if (inDestructuringPattern && e.kind === SyntaxKind.SpreadElement) { // Given the following situation: // var c: {}; // [...c] = ["", 0]; // // c is represented in the tree as a spread element in an array literal. // But c really functions as a rest element, and its purpose is to provide // a contextual type for the right hand side of the assignment. Therefore, // instead of calling checkExpression on "...c", which will give an error // if c is not iterable/array-like, we need to act as if we are trying to // get the contextual element type from it. So we do something similar to // getContextualTypeForElementExpression, which will crucially not error // if there is no index type / iterated type. const restArrayType = this.checkExpression((e).expression, checkMode); const restElementType = this.getIndexTypeOfType(restArrayType, IndexKind.Number) || this.getIteratedTypeOrElementType(restArrayType, /*errorNode*/ undefined, /*allowStringInput*/ false, /*allowAsyncIterables*/ false, /*checkAssignability*/ false); if (restElementType) { elementTypes.push(restElementType); } } else { const type = this.checkExpressionForMutableLocation(e, checkMode); elementTypes.push(type); } hasSpreadElement = hasSpreadElement || e.kind === SyntaxKind.SpreadElement; } if (!hasSpreadElement) { // If array literal is actually a destructuring pattern, mark it as an implied type. We do this such // that we get the same behavior for "var [x, y] = []" and "[x, y] = []". if (inDestructuringPattern && elementTypes.length) { const type = this.cloneTypeReference(this.createTupleType(elementTypes)); type.pattern = node; return type; } const contextualType = this.getApparentTypeOfContextualType(node); if (contextualType && this.contextualTypeIsTupleLikeType(contextualType)) { const pattern = contextualType.pattern; // If array literal is contextually typed by a binding pattern or an assignment pattern, pad the resulting // tuple type with the corresponding binding or assignment element types to make the lengths equal. if (pattern && (pattern.kind === SyntaxKind.ArrayBindingPattern || pattern.kind === SyntaxKind.ArrayLiteralExpression)) { const patternElements = (pattern).elements; for (let i = elementTypes.length; i < patternElements.length; i++) { const patternElement = patternElements[i]; if (this.hasDefaultValue(patternElement)) { elementTypes.push((contextualType).typeArguments[i]); } else { if (patternElement.kind !== SyntaxKind.OmittedExpression) { this.error(patternElement, Diagnostics.Initializer_provides_no_value_for_this_binding_element_and_the_binding_element_has_no_default_value); } elementTypes.push(this.unknownType); } } } if (elementTypes.length) { return this.createTupleType(elementTypes); } } } return this.createArrayType(elementTypes.length ? this.getUnionType(elementTypes, /*subtypeReduction*/ true) : this.strictNullChecks ? this.neverType : this.undefinedWideningType); } public isNumericName(name: DeclarationName): boolean { switch (name.kind) { case SyntaxKind.ComputedPropertyName: return this.isNumericComputedName(name); case SyntaxKind.Identifier: return this.isNumericLiteralName(name.escapedText); case SyntaxKind.NumericLiteral: case SyntaxKind.StringLiteral: return this.isNumericLiteralName(name.text); default: return false; } } public isNumericComputedName(name: ComputedPropertyName): boolean { // It seems odd to consider an expression of type Any to result in a numeric name, // but this behavior is consistent with checkIndexedAccess return this.isTypeAssignableToKind(this.checkComputedPropertyName(name), TypeFlags.NumberLike); } public isInfinityOrNaNString(name: string | __String): boolean { return name === "Infinity" || name === "-Infinity" || name === "NaN"; } public isNumericLiteralName(name: string | __String) { // The intent of numeric names is that // - they are names with text in a numeric form, and that // - setting properties/indexing with them is always equivalent to doing so with the numeric literal 'numLit', // acquired by applying the abstract 'ToNumber' operation on the name's text. // // The subtlety is in the latter portion, as we cannot reliably say that anything that looks like a numeric literal is a numeric name. // In fact, it is the case that the text of the name must be equal to 'ToString(numLit)' for this to hold. // // Consider the property name '"0xF00D"'. When one indexes with '0xF00D', they are actually indexing with the value of 'ToString(0xF00D)' // according to the ECMAScript specification, so it is actually as if the user indexed with the string '"61453"'. // Thus, the text of all numeric literals equivalent to '61543' such as '0xF00D', '0xf00D', '0170015', etc. are not valid numeric names // because their 'ToString' representation is not equal to their original text. // This is motivated by ECMA-262 sections 9.3.1, 9.8.1, 11.1.5, and 11.2.1. // // Here, we test whether 'ToString(ToNumber(name))' is exactly equal to 'name'. // The '+' prefix operator is equivalent here to applying the abstract ToNumber operation. // Applying the 'toString()' method on a number gives us the abstract ToString operation on a number. // // Note that this accepts the values 'Infinity', '-Infinity', and 'NaN', and that this is intentional. // This is desired behavior, because when indexing with them as numeric entities, you are indexing // with the strings '"Infinity"', '"-Infinity"', and '"NaN"' respectively. return (+name).toString() === name; } public checkComputedPropertyName(node: ComputedPropertyName): Type { const links = this.getNodeLinks(node.expression); if (!links.resolvedType) { links.resolvedType = this.checkExpression(node.expression); // This will allow types number, string, symbol or any. It will also allow enums, the unknown // type, and any union of these types (like string | number). if (links.resolvedType.flags & TypeFlags.Nullable || !this.isTypeAssignableToKind(links.resolvedType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbol) && !this.isTypeAssignableTo(links.resolvedType, this.getUnionType([this.stringType, this.numberType, this.esSymbolType]))) { this.error(node, Diagnostics.A_computed_property_name_must_be_of_type_string_number_symbol_or_any); } else { this.checkThatExpressionIsProperSymbolReference(node.expression, links.resolvedType, /*reportError*/ true); } } return links.resolvedType; } public getObjectLiteralIndexInfo(propertyNodes: NodeArray, offset: number, properties: Symbol[], kind: IndexKind): IndexInfo { const propTypes: Type[] = []; for (let i = 0; i < properties.length; i++) { if (kind === IndexKind.String || this.isNumericName(propertyNodes[i + offset].name)) { propTypes.push(this.getTypeOfSymbol(properties[i])); } } const unionType = propTypes.length ? this.getUnionType(propTypes, /*subtypeReduction*/ true) : this.undefinedType; return this.createIndexInfo(unionType, /*isReadonly*/ false); } public checkObjectLiteral(node: ObjectLiteralExpression, checkMode?: CheckMode): Type { const __conv_self__ = this; const inDestructuringPattern = isAssignmentTarget(node); // Grammar checking this.checkGrammarObjectLiteralExpression(node, inDestructuringPattern); let propertiesTable = createSymbolTable(); let propertiesArray: Symbol[] = []; let spread: Type = this.emptyObjectType; let propagatedFlags: TypeFlags = 0; const contextualType = this.getApparentTypeOfContextualType(node); const contextualTypeHasPattern = contextualType && contextualType.pattern && (contextualType.pattern.kind === SyntaxKind.ObjectBindingPattern || contextualType.pattern.kind === SyntaxKind.ObjectLiteralExpression); const isJSObjectLiteral = !contextualType && isInJavaScriptFile(node); let typeFlags: TypeFlags = 0; let patternWithComputedProperties = false; let hasComputedStringProperty = false; let hasComputedNumberProperty = false; const isInJSFile = isInJavaScriptFile(node); let offset = 0; for (let i = 0; i < node.properties.length; i++) { const memberDecl = node.properties[i]; let member = memberDecl.symbol; if (memberDecl.kind === SyntaxKind.PropertyAssignment || memberDecl.kind === SyntaxKind.ShorthandPropertyAssignment || isObjectLiteralMethod(memberDecl)) { let jsdocType: Type; if (isInJSFile) { jsdocType = this.getTypeForDeclarationFromJSDocComment(memberDecl); } let type: Type; if (memberDecl.kind === SyntaxKind.PropertyAssignment) { type = this.checkPropertyAssignment((memberDecl), checkMode); } else if (memberDecl.kind === SyntaxKind.MethodDeclaration) { type = this.checkObjectLiteralMethod((memberDecl), checkMode); } else { Debug.assert(memberDecl.kind === SyntaxKind.ShorthandPropertyAssignment); type = this.checkExpressionForMutableLocation((memberDecl).name, checkMode); } if (jsdocType) { this.checkTypeAssignableTo(type, jsdocType, memberDecl); type = jsdocType; } typeFlags |= type.flags; const prop = this.createSymbol(SymbolFlags.Property | member.flags, member.escapedName); if (inDestructuringPattern) { // If object literal is an assignment pattern and if the assignment pattern specifies a default value // for the property, make the property optional. const isOptional = (memberDecl.kind === SyntaxKind.PropertyAssignment && this.hasDefaultValue((memberDecl).initializer)) || (memberDecl.kind === SyntaxKind.ShorthandPropertyAssignment && (memberDecl).objectAssignmentInitializer); if (isOptional) { prop.flags |= SymbolFlags.Optional; } if (hasDynamicName(memberDecl)) { patternWithComputedProperties = true; } } else if (contextualTypeHasPattern && !(this.getObjectFlags(contextualType) & ObjectFlags.ObjectLiteralPatternWithComputedProperties)) { // If object literal is contextually typed by the implied type of a binding pattern, and if the // binding pattern specifies a default value for the property, make the property optional. const impliedProp = this.getPropertyOfType(contextualType, member.escapedName); if (impliedProp) { prop.flags |= impliedProp.flags & SymbolFlags.Optional; } else if (!this.compilerOptions.suppressExcessPropertyErrors && !this.getIndexInfoOfType(contextualType, IndexKind.String)) { this.error(memberDecl.name, Diagnostics.Object_literal_may_only_specify_known_properties_and_0_does_not_exist_in_type_1, this.symbolToString(member), this.typeToString(contextualType)); } } prop.declarations = member.declarations; prop.parent = member.parent; if (member.valueDeclaration) { prop.valueDeclaration = member.valueDeclaration; } prop.type = type; prop.target = member; member = prop; } else if (memberDecl.kind === SyntaxKind.SpreadAssignment) { if (this.languageVersion < ScriptTarget.ES2015) { this.checkExternalEmitHelpers(memberDecl, ExternalEmitHelpers.Assign); } if (propertiesArray.length > 0) { spread = this.getSpreadType(spread, createObjectLiteralType()); propertiesArray = []; propertiesTable = createSymbolTable(); hasComputedStringProperty = false; hasComputedNumberProperty = false; typeFlags = 0; } const type = this.checkExpression((memberDecl as SpreadAssignment).expression); if (!this.isValidSpreadType(type)) { this.error(memberDecl, Diagnostics.Spread_types_may_only_be_created_from_object_types); return this.unknownType; } spread = this.getSpreadType(spread, type); offset = i + 1; continue; } else { // TypeScript 1.0 spec (April 2014) // A get accessor declaration is processed in the same manner as // an ordinary function declaration(section 6.1) with no parameters. // A set accessor declaration is processed in the same manner // as an ordinary function declaration with a single parameter and a Void return type. Debug.assert(memberDecl.kind === SyntaxKind.GetAccessor || memberDecl.kind === SyntaxKind.SetAccessor); this.checkNodeDeferred(memberDecl); } if (hasDynamicName(memberDecl)) { if (this.isNumericName(memberDecl.name)) { hasComputedNumberProperty = true; } else { hasComputedStringProperty = true; } } else { propertiesTable.set(member.escapedName, member); } propertiesArray.push(member); } // If object literal is contextually typed by the implied type of a binding pattern, augment the result // type with those properties for which the binding pattern specifies a default value. if (contextualTypeHasPattern) { for (const prop of this.getPropertiesOfType(contextualType)) { if (!propertiesTable.get(prop.escapedName)) { if (!(prop.flags & SymbolFlags.Optional)) { this.error(prop.valueDeclaration || (prop).bindingElement, Diagnostics.Initializer_provides_no_value_for_this_binding_element_and_the_binding_element_has_no_default_value); } propertiesTable.set(prop.escapedName, prop); propertiesArray.push(prop); } } } if (spread !== this.emptyObjectType) { if (propertiesArray.length > 0) { spread = this.getSpreadType(spread, createObjectLiteralType()); } if (spread.flags & TypeFlags.Object) { // only set the symbol and flags if this is a (fresh) object type spread.flags |= propagatedFlags; spread.flags |= TypeFlags.FreshLiteral; (spread as ObjectType).objectFlags |= ObjectFlags.ObjectLiteral; spread.symbol = node.symbol; } return spread; } return createObjectLiteralType(); function createObjectLiteralType() { const stringIndexInfo = isJSObjectLiteral ? __conv_self__.jsObjectLiteralIndexInfo : hasComputedStringProperty ? __conv_self__.getObjectLiteralIndexInfo(node.properties, offset, propertiesArray, IndexKind.String) : undefined; const numberIndexInfo = hasComputedNumberProperty && !isJSObjectLiteral ? __conv_self__.getObjectLiteralIndexInfo(node.properties, offset, propertiesArray, IndexKind.Number) : undefined; const result = __conv_self__.createAnonymousType(node.symbol, propertiesTable, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo); const freshObjectLiteralFlag = __conv_self__.compilerOptions.suppressExcessPropertyErrors ? 0 : TypeFlags.FreshLiteral; result.flags |= TypeFlags.ContainsObjectLiteral | freshObjectLiteralFlag | (typeFlags & TypeFlags.PropagatingFlags); result.objectFlags |= ObjectFlags.ObjectLiteral; if (patternWithComputedProperties) { result.objectFlags |= ObjectFlags.ObjectLiteralPatternWithComputedProperties; } if (inDestructuringPattern) { result.pattern = node; } if (!(result.flags & TypeFlags.Nullable)) { propagatedFlags |= (result.flags & TypeFlags.PropagatingFlags); } return result; } } public isValidSpreadType(type: Type): boolean { const __conv_self__ = this; return !!(type.flags & (TypeFlags.Any | TypeFlags.Null | TypeFlags.Undefined | TypeFlags.NonPrimitive) || type.flags & TypeFlags.Object && !this.isGenericMappedType(type) || type.flags & TypeFlags.UnionOrIntersection && !forEach((type).types, t => !__conv_self__.isValidSpreadType(t))); } public checkJsxSelfClosingElement(node: JsxSelfClosingElement): Type { this.checkJsxOpeningLikeElement(node); return this.getJsxGlobalElementType() || this.anyType; } public checkJsxElement(node: JsxElement): Type { // Check attributes this.checkJsxOpeningLikeElement(node.openingElement); // Perform resolution on the closing tag so that rename/go to definition/etc work if (this.isJsxIntrinsicIdentifier(node.closingElement.tagName)) { this.getIntrinsicTagSymbol(node.closingElement); } else { this.checkExpression(node.closingElement.tagName); } return this.getJsxGlobalElementType() || this.anyType; } /** * Returns true iff the JSX element name would be a valid JS identifier, ignoring restrictions about keywords not being identifiers */ public isUnhyphenatedJsxName(name: string | __String) { // - is the only character supported in JSX attribute names that isn't valid in JavaScript identifiers return (name as string).indexOf("-") < 0; } /** * Returns true iff React would emit this tag name as a string rather than an identifier or qualified name */ public isJsxIntrinsicIdentifier(tagName: JsxTagNameExpression) { // TODO (yuisu): comment switch (tagName.kind) { case SyntaxKind.PropertyAccessExpression: case SyntaxKind.ThisKeyword: return false; case SyntaxKind.Identifier: return isIntrinsicJsxName((tagName).escapedText); default: Debug.fail(); } } /** * Get attributes type of the JSX opening-like element. The result is from resolving "attributes" property of the opening-like element. * * @param openingLikeElement a JSX opening-like element * @param filter a function to remove attributes that will not participate in checking whether attributes are assignable * @return an anonymous type (similar to the one returned by checkObjectLiteral) in which its properties are attributes property. * @remarks Because this function calls getSpreadType, it needs to use the same checks as checkObjectLiteral, * which also calls getSpreadType. */ public createJsxAttributesTypeFromAttributesProperty(openingLikeElement: JsxOpeningLikeElement, filter?: (symbol: Symbol) => boolean, checkMode?: CheckMode) { const __conv_self__ = this; const attributes = openingLikeElement.attributes; let attributesTable = createSymbolTable(); let spread: Type = this.emptyObjectType; let attributesArray: Symbol[] = []; let hasSpreadAnyType = false; let typeToIntersect: Type; let explicitlySpecifyChildrenAttribute = false; const jsxChildrenPropertyName = this.getJsxElementChildrenPropertyname(); for (const attributeDecl of attributes.properties) { const member = attributeDecl.symbol; if (isJsxAttribute(attributeDecl)) { const exprType = attributeDecl.initializer ? this.checkExpression(attributeDecl.initializer, checkMode) : this.trueType; // is sugar for const attributeSymbol = (this.createSymbol(SymbolFlags.Property | SymbolFlags.Transient | member.flags, member.escapedName)); attributeSymbol.declarations = member.declarations; attributeSymbol.parent = member.parent; if (member.valueDeclaration) { attributeSymbol.valueDeclaration = member.valueDeclaration; } attributeSymbol.type = exprType; attributeSymbol.target = member; attributesTable.set(attributeSymbol.escapedName, attributeSymbol); attributesArray.push(attributeSymbol); if (attributeDecl.name.escapedText === jsxChildrenPropertyName) { explicitlySpecifyChildrenAttribute = true; } } else { Debug.assert(attributeDecl.kind === SyntaxKind.JsxSpreadAttribute); if (attributesArray.length > 0) { spread = this.getSpreadType(spread, createJsxAttributesType(attributes.symbol, attributesTable)); attributesArray = []; attributesTable = createSymbolTable(); } const exprType = this.checkExpression(attributeDecl.expression); if (this.isTypeAny(exprType)) { hasSpreadAnyType = true; } if (this.isValidSpreadType(exprType)) { spread = this.getSpreadType(spread, exprType); } else { typeToIntersect = typeToIntersect ? this.getIntersectionType([typeToIntersect, exprType]) : exprType; } } } if (!hasSpreadAnyType) { if (spread !== this.emptyObjectType) { if (attributesArray.length > 0) { spread = this.getSpreadType(spread, createJsxAttributesType(attributes.symbol, attributesTable)); } attributesArray = this.getPropertiesOfType(spread); } attributesTable = createSymbolTable(); for (const attr of attributesArray) { if (!filter || filter(attr)) { attributesTable.set(attr.escapedName, attr); } } } // Handle children attribute const parent = openingLikeElement.parent.kind === SyntaxKind.JsxElement ? openingLikeElement.parent as JsxElement : undefined; // We have to check that openingElement of the parent is the one we are visiting as this may not be true for selfClosingElement if (parent && parent.openingElement === openingLikeElement && parent.children.length > 0) { const childrenTypes: Type[] = []; for (const child of (parent as JsxElement).children) { // In React, JSX text that contains only whitespaces will be ignored so we don't want to type-check that // because then type of children property will have constituent of string type. if (child.kind === SyntaxKind.JsxText) { if (!child.containsOnlyWhiteSpaces) { childrenTypes.push(this.stringType); } } else { childrenTypes.push(this.checkExpression(child, checkMode)); } } if (!hasSpreadAnyType && jsxChildrenPropertyName && jsxChildrenPropertyName !== "") { // Error if there is a attribute named "children" explicitly specified and children element. // This is because children element will overwrite the value from attributes. // Note: we will not warn "children" attribute overwritten if "children" attribute is specified in object spread. if (explicitlySpecifyChildrenAttribute) { this.error(attributes, Diagnostics._0_are_specified_twice_The_attribute_named_0_will_be_overwritten, unescapeLeadingUnderscores(jsxChildrenPropertyName)); } // If there are children in the body of JSX element, create dummy attribute "children" with anyType so that it will pass the attribute checking process const childrenPropSymbol = this.createSymbol(SymbolFlags.Property | SymbolFlags.Transient, jsxChildrenPropertyName); childrenPropSymbol.type = childrenTypes.length === 1 ? childrenTypes[0] : this.createArrayType(this.getUnionType(childrenTypes, /*subtypeReduction*/ false)); attributesTable.set(jsxChildrenPropertyName, childrenPropSymbol); } } if (hasSpreadAnyType) { return this.anyType; } const attributeType = createJsxAttributesType(attributes.symbol, attributesTable); return typeToIntersect && attributesTable.size ? this.getIntersectionType([typeToIntersect, attributeType]) : typeToIntersect ? typeToIntersect : attributeType; /** * Create anonymous type from given attributes symbol table. * @param symbol a symbol of JsxAttributes containing attributes corresponding to attributesTable * @param attributesTable a symbol table of attributes property */ function createJsxAttributesType(symbol: Symbol, attributesTable: UnderscoreEscapedMap) { const result = __conv_self__.createAnonymousType(symbol, attributesTable, emptyArray, emptyArray, /*stringIndexInfo*/ undefined, /*numberIndexInfo*/ undefined); result.flags |= TypeFlags.JsxAttributes | TypeFlags.ContainsObjectLiteral; result.objectFlags |= ObjectFlags.ObjectLiteral; return result; } } /** * Check attributes property of opening-like element. This function is called during chooseOverload to get call signature of a JSX opening-like element. * (See "checkApplicableSignatureForJsxOpeningLikeElement" for how the function is used) * @param node a JSXAttributes to be resolved of its type */ public checkJsxAttributes(node: JsxAttributes, checkMode?: CheckMode) { return this.createJsxAttributesTypeFromAttributesProperty((node.parent as JsxOpeningLikeElement), /*filter*/ undefined, checkMode); } public getJsxType(name: __String) { let jsxType = this.jsxTypes.get(name); if (jsxType === undefined) { this.jsxTypes.set(name, jsxType = this.getExportedTypeFromNamespace(this.JsxNames.JSX, name) || this.unknownType); } return jsxType; } /** * Looks up an intrinsic tag name and returns a symbol that either points to an intrinsic * property (in which case nodeLinks.jsxFlags will be IntrinsicNamedElement) or an intrinsic * string index signature (in which case nodeLinks.jsxFlags will be IntrinsicIndexedElement). * May also return unknownSymbol if both of these lookups fail. */ public getIntrinsicTagSymbol(node: JsxOpeningLikeElement | JsxClosingElement): Symbol { const links = this.getNodeLinks(node); if (!links.resolvedSymbol) { const intrinsicElementsType = this.getJsxType(this.JsxNames.IntrinsicElements); if (intrinsicElementsType !== this.unknownType) { // Property case if (!isIdentifier(node.tagName)) throw Debug.fail(); const intrinsicProp = this.getPropertyOfType(intrinsicElementsType, node.tagName.escapedText); if (intrinsicProp) { links.jsxFlags |= JsxFlags.IntrinsicNamedElement; return links.resolvedSymbol = intrinsicProp; } // Intrinsic string indexer case const indexSignatureType = this.getIndexTypeOfType(intrinsicElementsType, IndexKind.String); if (indexSignatureType) { links.jsxFlags |= JsxFlags.IntrinsicIndexedElement; return links.resolvedSymbol = intrinsicElementsType.symbol; } // Wasn't found this.error(node, Diagnostics.Property_0_does_not_exist_on_type_1, unescapeLeadingUnderscores(node.tagName.escapedText), "JSX." + this.JsxNames.IntrinsicElements); return links.resolvedSymbol = this.unknownSymbol; } else { if (this.noImplicitAny) { this.error(node, Diagnostics.JSX_element_implicitly_has_type_any_because_no_interface_JSX_0_exists, unescapeLeadingUnderscores(this.JsxNames.IntrinsicElements)); } return links.resolvedSymbol = this.unknownSymbol; } } return links.resolvedSymbol; } /** * Given a JSX element that is a class element, finds the Element Instance Type. If the * element is not a class element, or the class element type cannot be determined, returns 'undefined'. * For example, in the element , the element instance type is `MyClass` (not `typeof MyClass`). */ public getJsxElementInstanceType(node: JsxOpeningLikeElement, valueType: Type) { Debug.assert(!(valueType.flags & TypeFlags.Union)); if (this.isTypeAny(valueType)) { // Short-circuit if the class tag is using an element type 'any' return this.anyType; } // Resolve the signatures, preferring constructor let signatures = this.getSignaturesOfType(valueType, SignatureKind.Construct); if (signatures.length === 0) { // No construct signatures, try call signatures signatures = this.getSignaturesOfType(valueType, SignatureKind.Call); if (signatures.length === 0) { // We found no signatures at all, which is an error this.error(node.tagName, Diagnostics.JSX_element_type_0_does_not_have_any_construct_or_call_signatures, getTextOfNode(node.tagName)); return this.unknownType; } } const instantiatedSignatures = []; for (const signature of signatures) { if (signature.typeParameters) { const typeArguments = this.fillMissingTypeArguments(/*typeArguments*/ undefined, signature.typeParameters, /*minTypeArgumentCount*/ 0); instantiatedSignatures.push(this.getSignatureInstantiation(signature, typeArguments)); } else { instantiatedSignatures.push(signature); } } return this.getUnionType(map(instantiatedSignatures, this.getReturnTypeOfSignature), /*subtypeReduction*/ true); } /** * Look into JSX namespace and then look for container with matching name as nameOfAttribPropContainer. * Get a single property from that container if existed. Report an error if there are more than one property. * * @param nameOfAttribPropContainer a string of value JsxNames.ElementAttributesPropertyNameContainer or JsxNames.ElementChildrenAttributeNameContainer * if other string is given or the container doesn't exist, return undefined. */ public getNameFromJsxElementAttributesContainer(nameOfAttribPropContainer: __String): __String { // JSX const jsxNamespace = this.getGlobalSymbol(this.JsxNames.JSX, SymbolFlags.Namespace, /*diagnosticMessage*/ undefined); // JSX.ElementAttributesProperty | JSX.ElementChildrenAttribute [symbol] const jsxElementAttribPropInterfaceSym = jsxNamespace && this.getSymbol(jsxNamespace.exports, nameOfAttribPropContainer, SymbolFlags.Type); // JSX.ElementAttributesProperty | JSX.ElementChildrenAttribute [type] const jsxElementAttribPropInterfaceType = jsxElementAttribPropInterfaceSym && this.getDeclaredTypeOfSymbol(jsxElementAttribPropInterfaceSym); // The properties of JSX.ElementAttributesProperty | JSX.ElementChildrenAttribute const propertiesOfJsxElementAttribPropInterface = jsxElementAttribPropInterfaceType && this.getPropertiesOfType(jsxElementAttribPropInterfaceType); if (propertiesOfJsxElementAttribPropInterface) { // Element Attributes has zero properties, so the element attributes type will be the class instance type if (propertiesOfJsxElementAttribPropInterface.length === 0) { return "" as __String; } else if (propertiesOfJsxElementAttribPropInterface.length === 1) { return propertiesOfJsxElementAttribPropInterface[0].escapedName; } else if (propertiesOfJsxElementAttribPropInterface.length > 1) { // More than one property on ElementAttributesProperty is an error this.error(jsxElementAttribPropInterfaceSym.declarations[0], Diagnostics.The_global_type_JSX_0_may_not_have_more_than_one_property, unescapeLeadingUnderscores(nameOfAttribPropContainer)); } } return undefined; } /// e.g. "props" for React.d.ts, /// or 'undefined' if ElementAttributesProperty doesn't exist (which means all /// non-intrinsic elements' attributes type is 'any'), /// or '' if it has 0 properties (which means every /// non-intrinsic elements' attributes type is the element instance type) public getJsxElementPropertiesName() { if (!this._hasComputedJsxElementPropertiesName) { this._hasComputedJsxElementPropertiesName = true; this._jsxElementPropertiesName = this.getNameFromJsxElementAttributesContainer(this.JsxNames.ElementAttributesPropertyNameContainer); } return this._jsxElementPropertiesName; } public getJsxElementChildrenPropertyname(): __String { if (!this._hasComputedJsxElementChildrenPropertyName) { this._hasComputedJsxElementChildrenPropertyName = true; this._jsxElementChildrenPropertyName = this.getNameFromJsxElementAttributesContainer(this.JsxNames.ElementChildrenAttributeNameContainer); } return this._jsxElementChildrenPropertyName; } public getApparentTypeOfJsxPropsType(propsType: Type): Type { if (!propsType) { return undefined; } if (propsType.flags & TypeFlags.Intersection) { const propsApparentType: Type[] = []; for (const t of (propsType).types) { propsApparentType.push(this.getApparentType(t)); } return this.getIntersectionType(propsApparentType); } return this.getApparentType(propsType); } /** * Get JSX attributes type by trying to resolve openingLikeElement as a stateless function component. * Return only attributes type of successfully resolved call signature. * This function assumes that the caller handled other possible element type of the JSX element (e.g. stateful component) * Unlike tryGetAllJsxStatelessFunctionAttributesType, this function is a default behavior of type-checkers. * @param openingLikeElement a JSX opening-like element to find attributes type * @param elementType a type of the opening-like element. This elementType can't be an union type * @param elemInstanceType an element instance type (the result of newing or invoking this tag) * @param elementClassType a JSX-ElementClass type. This is a result of looking up ElementClass interface in the JSX global */ public defaultTryGetJsxStatelessFunctionAttributesType(openingLikeElement: JsxOpeningLikeElement, elementType: Type, elemInstanceType: Type, elementClassType?: Type): Type { Debug.assert(!(elementType.flags & TypeFlags.Union)); if (!elementClassType || !this.isTypeAssignableTo(elemInstanceType, elementClassType)) { const jsxStatelessElementType = this.getJsxGlobalStatelessElementType(); if (jsxStatelessElementType) { // We don't call getResolvedSignature here because we have already resolve the type of JSX Element. const callSignature = this.getResolvedJsxStatelessFunctionSignature(openingLikeElement, elementType, /*candidatesOutArray*/ undefined); if (callSignature !== this.unknownSignature) { const callReturnType = callSignature && this.getReturnTypeOfSignature(callSignature); let paramType = callReturnType && (callSignature.parameters.length === 0 ? this.emptyObjectType : this.getTypeOfSymbol(callSignature.parameters[0])); paramType = this.getApparentTypeOfJsxPropsType(paramType); if (callReturnType && this.isTypeAssignableTo(callReturnType, jsxStatelessElementType)) { // Intersect in JSX.IntrinsicAttributes if it exists const intrinsicAttributes = this.getJsxType(this.JsxNames.IntrinsicAttributes); if (intrinsicAttributes !== this.unknownType) { paramType = this.intersectTypes(intrinsicAttributes, paramType); } return paramType; } } } } return undefined; } /** * Get JSX attributes type by trying to resolve openingLikeElement as a stateless function component. * Return all attributes type of resolved call signature including candidate signatures. * This function assumes that the caller handled other possible element type of the JSX element. * This function is a behavior used by language service when looking up completion in JSX element. * @param openingLikeElement a JSX opening-like element to find attributes type * @param elementType a type of the opening-like element. This elementType can't be an union type * @param elemInstanceType an element instance type (the result of newing or invoking this tag) * @param elementClassType a JSX-ElementClass type. This is a result of looking up ElementClass interface in the JSX global */ public tryGetAllJsxStatelessFunctionAttributesType(openingLikeElement: JsxOpeningLikeElement, elementType: Type, elemInstanceType: Type, elementClassType?: Type): Type { Debug.assert(!(elementType.flags & TypeFlags.Union)); if (!elementClassType || !this.isTypeAssignableTo(elemInstanceType, elementClassType)) { // Is this is a stateless function component? See if its single signature's return type is assignable to the JSX Element Type const jsxStatelessElementType = this.getJsxGlobalStatelessElementType(); if (jsxStatelessElementType) { // We don't call getResolvedSignature because here we have already resolve the type of JSX Element. const candidatesOutArray: Signature[] = []; this.getResolvedJsxStatelessFunctionSignature(openingLikeElement, elementType, candidatesOutArray); let result: Type; let allMatchingAttributesType: Type; for (const candidate of candidatesOutArray) { const callReturnType = this.getReturnTypeOfSignature(candidate); let paramType = callReturnType && (candidate.parameters.length === 0 ? this.emptyObjectType : this.getTypeOfSymbol(candidate.parameters[0])); paramType = this.getApparentTypeOfJsxPropsType(paramType); if (callReturnType && this.isTypeAssignableTo(callReturnType, jsxStatelessElementType)) { let shouldBeCandidate = true; for (const attribute of openingLikeElement.attributes.properties) { if (isJsxAttribute(attribute) && this.isUnhyphenatedJsxName(attribute.name.escapedText) && !this.getPropertyOfType(paramType, attribute.name.escapedText)) { shouldBeCandidate = false; break; } } if (shouldBeCandidate) { result = this.intersectTypes(result, paramType); } allMatchingAttributesType = this.intersectTypes(allMatchingAttributesType, paramType); } } // If we can't find any matching, just return everything. if (!result) { result = allMatchingAttributesType; } // Intersect in JSX.IntrinsicAttributes if it exists const intrinsicAttributes = this.getJsxType(this.JsxNames.IntrinsicAttributes); if (intrinsicAttributes !== this.unknownType) { result = this.intersectTypes(intrinsicAttributes, result); } return result; } } return undefined; } /** * Resolve attributes type of the given opening-like element. The attributes type is a type of attributes associated with the given elementType. * For instance: * declare function Foo(attr: { p1: string}): JSX.Element; * ; // This function will try resolve "Foo" and return an attributes type of "Foo" which is "{ p1: string }" * * The function is intended to initially be called from getAttributesTypeFromJsxOpeningLikeElement which already handle JSX-intrinsic-element.. * This function will try to resolve custom JSX attributes type in following order: string literal, stateless function, and stateful component * * @param openingLikeElement a non-intrinsic JSXOPeningLikeElement * @param shouldIncludeAllStatelessAttributesType a boolean indicating whether to include all attributes types from all stateless function signature * @param elementType an instance type of the given opening-like element. If undefined, the function will check type openinglikeElement's tagname. * @param elementClassType a JSX-ElementClass type. This is a result of looking up ElementClass interface in the JSX global (imported from react.d.ts) * @return attributes type if able to resolve the type of node * anyType if there is no type ElementAttributesProperty or there is an error * emptyObjectType if there is no "prop" in the element instance type */ public resolveCustomJsxElementAttributesType(openingLikeElement: JsxOpeningLikeElement, shouldIncludeAllStatelessAttributesType: boolean, elementType: Type = this.checkExpression(openingLikeElement.tagName), elementClassType?: Type): Type { const __conv_self__ = this; if (elementType.flags & TypeFlags.Union) { const types = (elementType as UnionType).types; return this.getUnionType(types.map(type => { return __conv_self__.resolveCustomJsxElementAttributesType(openingLikeElement, shouldIncludeAllStatelessAttributesType, type, elementClassType); }), /*subtypeReduction*/ true); } // If the elemType is a string type, we have to return anyType to prevent an error downstream as we will try to find construct or call signature of the type if (elementType.flags & TypeFlags.String) { return this.anyType; } else if (elementType.flags & TypeFlags.StringLiteral) { // If the elemType is a stringLiteral type, we can then provide a check to make sure that the string literal type is one of the Jsx intrinsic element type // For example: // var CustomTag: "h1" = "h1"; // Hello World const intrinsicElementsType = this.getJsxType(this.JsxNames.IntrinsicElements); if (intrinsicElementsType !== this.unknownType) { const stringLiteralTypeName = escapeLeadingUnderscores((elementType).value); const intrinsicProp = this.getPropertyOfType(intrinsicElementsType, stringLiteralTypeName); if (intrinsicProp) { return this.getTypeOfSymbol(intrinsicProp); } const indexSignatureType = this.getIndexTypeOfType(intrinsicElementsType, IndexKind.String); if (indexSignatureType) { return indexSignatureType; } this.error(openingLikeElement, Diagnostics.Property_0_does_not_exist_on_type_1, unescapeLeadingUnderscores(stringLiteralTypeName), "JSX." + this.JsxNames.IntrinsicElements); } // If we need to report an error, we already done so here. So just return any to prevent any more error downstream return this.anyType; } // Get the element instance type (the result of newing or invoking this tag) const elemInstanceType = this.getJsxElementInstanceType(openingLikeElement, elementType); // If we should include all stateless attributes type, then get all attributes type from all stateless function signature. // Otherwise get only attributes type from the signature picked by choose-overload logic. const statelessAttributesType = shouldIncludeAllStatelessAttributesType ? this.tryGetAllJsxStatelessFunctionAttributesType(openingLikeElement, elementType, elemInstanceType, elementClassType) : this.defaultTryGetJsxStatelessFunctionAttributesType(openingLikeElement, elementType, elemInstanceType, elementClassType); if (statelessAttributesType) { return statelessAttributesType; } // Issue an error if this return type isn't assignable to JSX.ElementClass if (elementClassType) { this.checkTypeRelatedTo(elemInstanceType, elementClassType, this.assignableRelation, openingLikeElement, Diagnostics.JSX_element_type_0_is_not_a_constructor_function_for_JSX_elements); } if (this.isTypeAny(elemInstanceType)) { return elemInstanceType; } const propsName = this.getJsxElementPropertiesName(); if (propsName === undefined) { // There is no type ElementAttributesProperty, return 'any' return this.anyType; } else if (propsName === "") { // If there is no e.g. 'props' member in ElementAttributesProperty, use the element class type instead return elemInstanceType; } else { const attributesType = this.getTypeOfPropertyOfType(elemInstanceType, propsName); if (!attributesType) { // There is no property named 'props' on this instance type return this.emptyObjectType; } else if (this.isTypeAny(attributesType) || (attributesType === this.unknownType)) { // Props is of type 'any' or unknown return attributesType; } else { // Normal case -- add in IntrinsicClassElements and IntrinsicElements let apparentAttributesType = attributesType; const intrinsicClassAttribs = this.getJsxType(this.JsxNames.IntrinsicClassAttributes); if (intrinsicClassAttribs !== this.unknownType) { const typeParams = this.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(intrinsicClassAttribs.symbol); if (typeParams) { if (typeParams.length === 1) { apparentAttributesType = this.intersectTypes(this.createTypeReference((intrinsicClassAttribs), [elemInstanceType]), apparentAttributesType); } } else { apparentAttributesType = this.intersectTypes(attributesType, intrinsicClassAttribs); } } const intrinsicAttribs = this.getJsxType(this.JsxNames.IntrinsicAttributes); if (intrinsicAttribs !== this.unknownType) { apparentAttributesType = this.intersectTypes(intrinsicAttribs, apparentAttributesType); } return apparentAttributesType; } } } /** * Get attributes type of the given intrinsic opening-like Jsx element by resolving the tag name. * The function is intended to be called from a function which has checked that the opening element is an intrinsic element. * @param node an intrinsic JSX opening-like element */ public getIntrinsicAttributesTypeFromJsxOpeningLikeElement(node: JsxOpeningLikeElement): Type { Debug.assert(this.isJsxIntrinsicIdentifier(node.tagName)); const links = this.getNodeLinks(node); if (!links.resolvedJsxElementAttributesType) { const symbol = this.getIntrinsicTagSymbol(node); if (links.jsxFlags & JsxFlags.IntrinsicNamedElement) { return links.resolvedJsxElementAttributesType = this.getTypeOfSymbol(symbol); } else if (links.jsxFlags & JsxFlags.IntrinsicIndexedElement) { return links.resolvedJsxElementAttributesType = this.getIndexInfoOfSymbol(symbol, IndexKind.String).type; } else { return links.resolvedJsxElementAttributesType = this.unknownType; } } return links.resolvedJsxElementAttributesType; } /** * Get attributes type of the given custom opening-like JSX element. * This function is intended to be called from a caller that handles intrinsic JSX element already. * @param node a custom JSX opening-like element * @param shouldIncludeAllStatelessAttributesType a boolean value used by language service to get all possible attributes type from an overload stateless function component */ public getCustomJsxElementAttributesType(node: JsxOpeningLikeElement, shouldIncludeAllStatelessAttributesType: boolean): Type { const links = this.getNodeLinks(node); const linkLocation = shouldIncludeAllStatelessAttributesType ? "resolvedJsxElementAllAttributesType" : "resolvedJsxElementAttributesType"; if (!links[linkLocation]) { const elemClassType = this.getJsxGlobalElementClassType(); return links[linkLocation] = this.resolveCustomJsxElementAttributesType(node, shouldIncludeAllStatelessAttributesType, /*elementType*/ undefined, elemClassType); } return links[linkLocation]; } /** * Get all possible attributes type, especially from an overload stateless function component, of the given JSX opening-like element. * This function is called by language service (see: completions-tryGetGlobalSymbols). * @param node a JSX opening-like element to get attributes type for */ public getAllAttributesTypeFromJsxOpeningLikeElement(node: JsxOpeningLikeElement): Type { if (this.isJsxIntrinsicIdentifier(node.tagName)) { return this.getIntrinsicAttributesTypeFromJsxOpeningLikeElement(node); } else { // Because in language service, the given JSX opening-like element may be incomplete and therefore, // we can't resolve to exact signature if the element is a stateless function component so the best thing to do is return all attributes type from all overloads. return this.getCustomJsxElementAttributesType(node, /*shouldIncludeAllStatelessAttributesType*/ true); } } /** * Get the attributes type, which indicates the attributes that are valid on the given JSXOpeningLikeElement. * @param node a JSXOpeningLikeElement node * @return an attributes type of the given node */ public getAttributesTypeFromJsxOpeningLikeElement(node: JsxOpeningLikeElement): Type { if (this.isJsxIntrinsicIdentifier(node.tagName)) { return this.getIntrinsicAttributesTypeFromJsxOpeningLikeElement(node); } else { return this.getCustomJsxElementAttributesType(node, /*shouldIncludeAllStatelessAttributesType*/ false); } } /** * Given a JSX attribute, returns the symbol for the corresponds property * of the element attributes type. Will return unknownSymbol for attributes * that have no matching element attributes type property. */ public getJsxAttributePropertySymbol(attrib: JsxAttribute): Symbol { const attributesType = this.getAttributesTypeFromJsxOpeningLikeElement((attrib.parent.parent as JsxOpeningElement)); const prop = this.getPropertyOfType(attributesType, attrib.name.escapedText); return prop || this.unknownSymbol; } public getJsxGlobalElementClassType(): Type { if (!this.deferredJsxElementClassType) { this.deferredJsxElementClassType = this.getExportedTypeFromNamespace(this.JsxNames.JSX, this.JsxNames.ElementClass); } return this.deferredJsxElementClassType; } public getJsxGlobalElementType(): Type { if (!this.deferredJsxElementType) { this.deferredJsxElementType = this.getExportedTypeFromNamespace(this.JsxNames.JSX, this.JsxNames.Element); } return this.deferredJsxElementType; } public getJsxGlobalStatelessElementType(): Type { if (!this.deferredJsxStatelessElementType) { const jsxElementType = this.getJsxGlobalElementType(); if (jsxElementType) { this.deferredJsxStatelessElementType = this.getUnionType([jsxElementType, this.nullType]); } } return this.deferredJsxStatelessElementType; } /** * Returns all the properties of the Jsx.IntrinsicElements interface */ public getJsxIntrinsicTagNames(): Symbol[] { const intrinsics = this.getJsxType(this.JsxNames.IntrinsicElements); return intrinsics ? this.getPropertiesOfType(intrinsics) : emptyArray; } public checkJsxPreconditions(errorNode: Node) { // Preconditions for using JSX if ((this.compilerOptions.jsx || JsxEmit.None) === JsxEmit.None) { this.error(errorNode, Diagnostics.Cannot_use_JSX_unless_the_jsx_flag_is_provided); } if (this.getJsxGlobalElementType() === undefined) { if (this.noImplicitAny) { this.error(errorNode, Diagnostics.JSX_element_implicitly_has_type_any_because_the_global_type_JSX_Element_does_not_exist); } } } public checkJsxOpeningLikeElement(node: JsxOpeningLikeElement) { this.checkGrammarJsxElement(node); this.checkJsxPreconditions(node); // The reactNamespace/jsxFactory's root symbol should be marked as 'used' so we don't incorrectly elide its import. // And if there is no reactNamespace/jsxFactory's symbol in scope when targeting React emit, we should issue an error. const reactRefErr = this.diagnostics && this.compilerOptions.jsx === JsxEmit.React ? Diagnostics.Cannot_find_name_0 : undefined; const reactNamespace = this.getJsxNamespace(); const reactSym = this.resolveName(node.tagName, reactNamespace, SymbolFlags.Value, reactRefErr, reactNamespace); if (reactSym) { // Mark local symbol as referenced here because it might not have been marked // if jsx emit was not react as there wont be error being emitted reactSym.isReferenced = true; // If react symbol is alias, mark it as refereced if (reactSym.flags & SymbolFlags.Alias && !this.isConstEnumOrConstEnumOnlyModule(this.resolveAlias(reactSym))) { this.markAliasSymbolAsReferenced(reactSym); } } this.checkJsxAttributesAssignableToTagNameAttributes(node); } /** * Check if a property with the given name is known anywhere in the given type. In an object type, a property * is considered known if * 1. the object type is empty and the check is for assignability, or * 2. if the object type has index signatures, or * 3. if the property is actually declared in the object type * (this means that 'toString', for example, is not usually a known property). * 4. In a union or intersection type, * a property is considered known if it is known in any constituent type. * @param targetType a type to search a given name in * @param name a property name to search * @param isComparingJsxAttributes a boolean flag indicating whether we are searching in JsxAttributesType */ public isKnownProperty(targetType: Type, name: __String, isComparingJsxAttributes: boolean): boolean { if (targetType.flags & TypeFlags.Object) { const resolved = this.resolveStructuredTypeMembers((targetType)); if (resolved.stringIndexInfo || resolved.numberIndexInfo && this.isNumericLiteralName(name) || this.getPropertyOfObjectType(targetType, name) || isComparingJsxAttributes && !this.isUnhyphenatedJsxName(name)) { // For JSXAttributes, if the attribute has a hyphenated name, consider that the attribute to be known. return true; } } else if (targetType.flags & TypeFlags.UnionOrIntersection) { for (const t of (targetType).types) { if (this.isKnownProperty(t, name, isComparingJsxAttributes)) { return true; } } } return false; } /** * Check whether the given attributes of JSX opening-like element is assignable to the tagName attributes. * Get the attributes type of the opening-like element through resolving the tagName, "target attributes" * Check assignablity between given attributes property, "source attributes", and the "target attributes" * @param openingLikeElement an opening-like JSX element to check its JSXAttributes */ public checkJsxAttributesAssignableToTagNameAttributes(openingLikeElement: JsxOpeningLikeElement) { const __conv_self__ = this; // The function involves following steps: // 1. Figure out expected attributes type by resolving tagName of the JSX opening-like element, targetAttributesType. // During these steps, we will try to resolve the tagName as intrinsic name, stateless function, stateful component (in the order) // 2. Solved JSX attributes type given by users, sourceAttributesType, which is by resolving "attributes" property of the JSX opening-like element. // 3. Check if the two are assignable to each other // targetAttributesType is a type of an attributes from resolving tagName of an opening-like JSX element. const targetAttributesType = this.isJsxIntrinsicIdentifier(openingLikeElement.tagName) ? this.getIntrinsicAttributesTypeFromJsxOpeningLikeElement(openingLikeElement) : this.getCustomJsxElementAttributesType(openingLikeElement, /*shouldIncludeAllStatelessAttributesType*/ false); // sourceAttributesType is a type of an attributes properties. // i.e
// attr1 and attr2 are treated as JSXAttributes attached in the JsxOpeningLikeElement as "attributes". const sourceAttributesType = this.createJsxAttributesTypeFromAttributesProperty(openingLikeElement, attribute => { return __conv_self__.isUnhyphenatedJsxName(attribute.escapedName) || !!(__conv_self__.getPropertyOfType(targetAttributesType, attribute.escapedName)); }); // If the targetAttributesType is an emptyObjectType, indicating that there is no property named 'props' on this instance type. // but there exists a sourceAttributesType, we need to explicitly give an error as normal assignability check allow excess properties and will pass. if (targetAttributesType === this.emptyObjectType && (this.isTypeAny(sourceAttributesType) || (sourceAttributesType).properties.length > 0)) { this.error(openingLikeElement, Diagnostics.JSX_element_class_does_not_support_attributes_because_it_does_not_have_a_0_property, unescapeLeadingUnderscores(this.getJsxElementPropertiesName())); } else { // Check if sourceAttributesType assignable to targetAttributesType though this check will allow excess properties const isSourceAttributeTypeAssignableToTarget = this.checkTypeAssignableTo(sourceAttributesType, targetAttributesType, openingLikeElement.attributes.properties.length > 0 ? openingLikeElement.attributes : openingLikeElement); // After we check for assignability, we will do another pass to check that all explicitly specified attributes have correct name corresponding in targetAttributeType. // This will allow excess properties in spread type as it is very common pattern to spread outter attributes into React component in its render method. if (isSourceAttributeTypeAssignableToTarget && !this.isTypeAny(sourceAttributesType) && !this.isTypeAny(targetAttributesType)) { for (const attribute of openingLikeElement.attributes.properties) { if (isJsxAttribute(attribute) && !this.isKnownProperty(targetAttributesType, attribute.name.escapedText, /*isComparingJsxAttributes*/ true)) { this.error(attribute, Diagnostics.Property_0_does_not_exist_on_type_1, unescapeLeadingUnderscores(attribute.name.escapedText), this.typeToString(targetAttributesType)); // We break here so that errors won't be cascading break; } } } } } public checkJsxExpression(node: JsxExpression, checkMode?: CheckMode) { if (node.expression) { const type = this.checkExpression(node.expression, checkMode); if (node.dotDotDotToken && type !== this.anyType && !this.isArrayType(type)) { this.error(node, Diagnostics.JSX_spread_child_must_be_an_array_type, node.toString(), this.typeToString(type)); } return type; } else { return this.unknownType; } } // If a symbol is a synthesized symbol with no value declaration, we assume it is a property. Example of this are the synthesized // '.prototype' property as well as synthesized tuple index properties. public getDeclarationKindFromSymbol(s: Symbol) { return s.valueDeclaration ? s.valueDeclaration.kind : SyntaxKind.PropertyDeclaration; } public getDeclarationNodeFlagsFromSymbol(s: Symbol): NodeFlags { return s.valueDeclaration ? getCombinedNodeFlags(s.valueDeclaration) : 0; } public isMethodLike(symbol: Symbol) { return !!(symbol.flags & SymbolFlags.Method || getCheckFlags(symbol) & CheckFlags.SyntheticMethod); } /** * Check whether the requested property access is valid. * Returns true if node is a valid property access, and false otherwise. * @param node The node to be checked. * @param left The left hand side of the property access (e.g.: the super in `super.foo`). * @param type The type of left. * @param prop The symbol for the right hand side of the property access. */ public checkPropertyAccessibility(node: PropertyAccessExpression | QualifiedName | VariableLikeDeclaration, left: Expression | QualifiedName, type: Type, prop: Symbol): boolean { const __conv_self__ = this; const flags = getDeclarationModifierFlagsFromSymbol(prop); const errorNode = node.kind === SyntaxKind.PropertyAccessExpression || node.kind === SyntaxKind.VariableDeclaration ? (node).name : (node).right; if (getCheckFlags(prop) & CheckFlags.ContainsPrivate) { // Synthetic property with private constituent property this.error(errorNode, Diagnostics.Property_0_has_conflicting_declarations_and_is_inaccessible_in_type_1, this.symbolToString(prop), this.typeToString(type)); return false; } if (left.kind === SyntaxKind.SuperKeyword) { // TS 1.0 spec (April 2014): 4.8.2 // - In a constructor, instance member function, instance member accessor, or // instance member variable initializer where this references a derived class instance, // a super property access is permitted and must specify a public instance member function of the base class. // - In a static member function or static member accessor // where this references the constructor function object of a derived class, // a super property access is permitted and must specify a public static member function of the base class. if (this.languageVersion < ScriptTarget.ES2015) { const hasNonMethodDeclaration = this.forEachProperty(prop, p => { const propKind = __conv_self__.getDeclarationKindFromSymbol(p); return propKind !== SyntaxKind.MethodDeclaration && propKind !== SyntaxKind.MethodSignature; }); if (hasNonMethodDeclaration) { this.error(errorNode, Diagnostics.Only_public_and_protected_methods_of_the_base_class_are_accessible_via_the_super_keyword); return false; } } if (flags & ModifierFlags.Abstract) { // A method cannot be accessed in a super property access if the method is abstract. // This error could mask a private property access error. But, a member // cannot simultaneously be private and abstract, so this will trigger an // additional error elsewhere. this.error(errorNode, Diagnostics.Abstract_method_0_in_class_1_cannot_be_accessed_via_super_expression, this.symbolToString(prop), this.typeToString(this.getDeclaringClass(prop))); return false; } } // Public properties are otherwise accessible. if (!(flags & ModifierFlags.NonPublicAccessibilityModifier)) { return true; } // Property is known to be private or protected at this point // Private property is accessible if the property is within the declaring class if (flags & ModifierFlags.Private) { const declaringClassDeclaration = (this.getClassLikeDeclarationOfSymbol(this.getParentOfSymbol(prop))); if (!this.isNodeWithinClass(node, declaringClassDeclaration)) { this.error(errorNode, Diagnostics.Property_0_is_private_and_only_accessible_within_class_1, this.symbolToString(prop), this.typeToString(this.getDeclaringClass(prop))); return false; } return true; } // Property is known to be protected at this point // All protected properties of a supertype are accessible in a super access if (left.kind === SyntaxKind.SuperKeyword) { return true; } // Find the first enclosing class that has the declaring classes of the protected constituents // of the property as base classes const enclosingClass = this.forEachEnclosingClass(node, enclosingDeclaration => { const enclosingClass = (__conv_self__.getDeclaredTypeOfSymbol(__conv_self__.getSymbolOfNode(enclosingDeclaration))); return __conv_self__.isClassDerivedFromDeclaringClasses(enclosingClass, prop) ? enclosingClass : undefined; }); // A protected property is accessible if the property is within the declaring class or classes derived from it if (!enclosingClass) { this.error(errorNode, Diagnostics.Property_0_is_protected_and_only_accessible_within_class_1_and_its_subclasses, this.symbolToString(prop), this.typeToString(this.getDeclaringClass(prop) || type)); return false; } // No further restrictions for static properties if (flags & ModifierFlags.Static) { return true; } // An instance property must be accessed through an instance of the enclosing class if (type.flags & TypeFlags.TypeParameter && (type as TypeParameter).isThisType) { // get the original type -- represented as the type constraint of the 'this' type type = this.getConstraintOfTypeParameter((type)); } if (!(this.getObjectFlags(this.getTargetType(type)) & ObjectFlags.ClassOrInterface && this.hasBaseType(type, enclosingClass))) { this.error(errorNode, Diagnostics.Property_0_is_protected_and_only_accessible_through_an_instance_of_class_1, this.symbolToString(prop), this.typeToString(enclosingClass)); return false; } return true; } public checkNonNullExpression(node: Expression | QualifiedName) { return this.checkNonNullType(this.checkExpression(node), node); } public checkNonNullType(type: Type, errorNode: Node): Type { const kind = (this.strictNullChecks ? this.getFalsyFlags(type) : type.flags) & TypeFlags.Nullable; if (kind) { this.error(errorNode, kind & TypeFlags.Undefined ? kind & TypeFlags.Null ? Diagnostics.Object_is_possibly_null_or_undefined : Diagnostics.Object_is_possibly_undefined : Diagnostics.Object_is_possibly_null); const t = this.getNonNullableType(type); return t.flags & (TypeFlags.Nullable | TypeFlags.Never) ? this.unknownType : t; } return type; } public checkPropertyAccessExpression(node: PropertyAccessExpression) { return this.checkPropertyAccessExpressionOrQualifiedName(node, node.expression, node.name); } public checkQualifiedName(node: QualifiedName) { return this.checkPropertyAccessExpressionOrQualifiedName(node, node.left, node.right); } public checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, right: Identifier) { const type = this.checkNonNullExpression(left); if (this.isTypeAny(type) || type === this.silentNeverType) { return type; } const apparentType = this.getApparentType(this.getWidenedType(type)); if (apparentType === this.unknownType || (type.flags & TypeFlags.TypeParameter && this.isTypeAny(apparentType))) { // handle cases when type is Type parameter with invalid or any constraint return apparentType; } const prop = this.getPropertyOfType(apparentType, right.escapedText); if (!prop) { const stringIndexType = this.getIndexTypeOfType(apparentType, IndexKind.String); if (stringIndexType) { return stringIndexType; } if (right.escapedText && !this.checkAndReportErrorForExtendingInterface(node)) { this.reportNonexistentProperty(right, type.flags & TypeFlags.TypeParameter && (type as TypeParameter).isThisType ? apparentType : type); } return this.unknownType; } if (prop.valueDeclaration) { if (this.isInPropertyInitializer(node) && !this.isBlockScopedNameDeclaredBeforeUse(prop.valueDeclaration, right)) { this.error(right, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, unescapeLeadingUnderscores(right.escapedText)); } if (prop.valueDeclaration.kind === SyntaxKind.ClassDeclaration && node.parent && node.parent.kind !== SyntaxKind.TypeReference && !isInAmbientContext(prop.valueDeclaration) && !this.isBlockScopedNameDeclaredBeforeUse(prop.valueDeclaration, right)) { this.error(right, Diagnostics.Class_0_used_before_its_declaration, unescapeLeadingUnderscores(right.escapedText)); } } this.markPropertyAsReferenced(prop); this.getNodeLinks(node).resolvedSymbol = prop; this.checkPropertyAccessibility(node, left, apparentType, prop); const propType = this.getDeclaredOrApparentType(prop, node); const assignmentKind = getAssignmentTargetKind(node); if (assignmentKind) { if (this.isReferenceToReadonlyEntity((node), prop) || this.isReferenceThroughNamespaceImport((node))) { this.error(right, Diagnostics.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property, unescapeLeadingUnderscores(right.escapedText)); return this.unknownType; } } // Only compute control flow type if this is a property access expression that isn't an // assignment target, and the referenced property was declared as a variable, property, // accessor, or optional method. if (node.kind !== SyntaxKind.PropertyAccessExpression || assignmentKind === AssignmentKind.Definite || !(prop.flags & (SymbolFlags.Variable | SymbolFlags.Property | SymbolFlags.Accessor)) && !(prop.flags & SymbolFlags.Method && propType.flags & TypeFlags.Union)) { return propType; } const flowType = this.getFlowTypeOfReference(node, propType); return assignmentKind ? this.getBaseTypeOfLiteralType(flowType) : flowType; } public reportNonexistentProperty(propNode: Identifier, containingType: Type) { let errorInfo: DiagnosticMessageChain; if (containingType.flags & TypeFlags.Union && !(containingType.flags & TypeFlags.Primitive)) { for (const subtype of (containingType as UnionType).types) { if (!this.getPropertyOfType(subtype, propNode.escapedText)) { errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1, declarationNameToString(propNode), this.typeToString(subtype)); break; } } } const suggestion = this.getSuggestionForNonexistentProperty(propNode, containingType); if (suggestion) { errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2, declarationNameToString(propNode), this.typeToString(containingType), suggestion); } else { errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1, declarationNameToString(propNode), this.typeToString(containingType)); } this.diagnostics.add(createDiagnosticForNodeFromMessageChain(propNode, errorInfo)); } public getSuggestionForNonexistentProperty(node: Identifier, containingType: Type): __String | undefined { const suggestion = this.getSpellingSuggestionForName(unescapeLeadingUnderscores(node.escapedText), this.getPropertiesOfType(containingType), SymbolFlags.Value); return suggestion && suggestion.escapedName; } public getSuggestionForNonexistentSymbol(location: Node, name: __String, meaning: SymbolFlags): __String { const __conv_self__ = this; const result = this.resolveNameHelper(location, name, meaning, /*nameNotFoundMessage*/ undefined, name, (symbols, name, meaning) => { const symbol = __conv_self__.getSymbol(symbols, name, meaning); if (symbol) { // Sometimes the symbol is found when location is a return type of a function: `typeof x` and `x` is declared in the body of the function // So the table *contains* `x` but `x` isn't actually in scope. // However, resolveNameHelper will continue and call this callback again, so we'll eventually get a correct suggestion. return symbol; } return __conv_self__.getSpellingSuggestionForName(unescapeLeadingUnderscores(name), arrayFrom(symbols.values()), meaning); }); if (result) { return result.escapedName; } } /** * Given a name and a list of symbols whose names are *not* equal to the name, return a spelling suggestion if there is one that is close enough. * Names less than length 3 only check for case-insensitive equality, not levenshtein distance. * * If there is a candidate that's the same except for case, return that. * If there is a candidate that's within one edit of the name, return that. * Otherwise, return the candidate with the smallest Levenshtein distance, * except for candidates: * * With no name * * Whose meaning doesn't match the `meaning` parameter. * * Whose length differs from the target name by more than 0.3 of the length of the name. * * Whose levenshtein distance is more than 0.4 of the length of the name * (0.4 allows 1 substitution/transposition for every 5 characters, * and 1 insertion/deletion at 3 characters) * Names longer than 30 characters don't get suggestions because Levenshtein distance is an n**2 algorithm. */ public getSpellingSuggestionForName(name: string, symbols: Symbol[], meaning: SymbolFlags): Symbol | undefined { const worstDistance = name.length * 0.4; const maximumLengthDifference = Math.min(3, name.length * 0.34); let bestDistance = Number.MAX_VALUE; let bestCandidate = undefined; let justCheckExactMatches = false; if (name.length > 30) { return undefined; } name = name.toLowerCase(); for (const candidate of symbols) { let candidateName = unescapeLeadingUnderscores(candidate.escapedName); if (candidate.flags & meaning && candidateName && Math.abs(candidateName.length - name.length) < maximumLengthDifference) { candidateName = candidateName.toLowerCase(); if (candidateName === name) { return candidate; } if (justCheckExactMatches) { continue; } if (candidateName.length < 3 || name.length < 3 || candidateName === "eval" || candidateName === "intl" || candidateName === "undefined" || candidateName === "map" || candidateName === "nan" || candidateName === "set") { continue; } const distance = levenshtein(name, candidateName); if (distance > worstDistance) { continue; } if (distance < 3) { justCheckExactMatches = true; bestCandidate = candidate; } else if (distance < bestDistance) { bestDistance = distance; bestCandidate = candidate; } } } return bestCandidate; } public markPropertyAsReferenced(prop: Symbol) { if (prop && this.noUnusedIdentifiers && (prop.flags & SymbolFlags.ClassMember) && prop.valueDeclaration && hasModifier(prop.valueDeclaration, ModifierFlags.Private)) { if (getCheckFlags(prop) & CheckFlags.Instantiated) { this.getSymbolLinks(prop).target.isReferenced = true; } else { prop.isReferenced = true; } } } public isInPropertyInitializer(node: Node): boolean { while (node) { if (node.parent && node.parent.kind === SyntaxKind.PropertyDeclaration && (node.parent as PropertyDeclaration).initializer === node) { return true; } node = node.parent; } return false; } public isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: __String): boolean { const left = node.kind === SyntaxKind.PropertyAccessExpression ? (node).expression : (node).left; return this.isValidPropertyAccessWithType(node, left, propertyName, this.getWidenedType(this.checkExpression(left))); } public isValidPropertyAccessWithType(node: PropertyAccessExpression | QualifiedName, left: LeftHandSideExpression | QualifiedName, propertyName: __String, type: Type): boolean { if (type !== this.unknownType && !this.isTypeAny(type)) { const prop = this.getPropertyOfType(type, propertyName); if (prop) { return this.checkPropertyAccessibility(node, left, type, prop); } // In js files properties of unions are allowed in completion if (isInJavaScriptFile(left) && (type.flags & TypeFlags.Union)) { for (const elementType of (type).types) { if (this.isValidPropertyAccessWithType(node, left, propertyName, elementType)) { return true; } } } return false; } return true; } /** * Return the symbol of the for-in variable declared or referenced by the given for-in statement. */ public getForInVariableSymbol(node: ForInStatement): Symbol { const initializer = node.initializer; if (initializer.kind === SyntaxKind.VariableDeclarationList) { const variable = (initializer).declarations[0]; if (variable && !isBindingPattern(variable.name)) { return this.getSymbolOfNode(variable); } } else if (initializer.kind === SyntaxKind.Identifier) { return this.getResolvedSymbol((initializer)); } return undefined; } /** * Return true if the given type is considered to have numeric property names. */ public hasNumericPropertyNames(type: Type) { return this.getIndexTypeOfType(type, IndexKind.Number) && !this.getIndexTypeOfType(type, IndexKind.String); } /** * Return true if given node is an expression consisting of an identifier (possibly parenthesized) * that references a for-in variable for an object with numeric property names. */ public isForInVariableForNumericPropertyNames(expr: Expression) { const e = skipParentheses(expr); if (e.kind === SyntaxKind.Identifier) { const symbol = this.getResolvedSymbol((e)); if (symbol.flags & SymbolFlags.Variable) { let child: Node = expr; let node = expr.parent; while (node) { if (node.kind === SyntaxKind.ForInStatement && child === (node).statement && this.getForInVariableSymbol((node)) === symbol && this.hasNumericPropertyNames(this.getTypeOfExpression((node).expression))) { return true; } child = node; node = node.parent; } } } return false; } public checkIndexedAccess(node: ElementAccessExpression): Type { const objectType = this.checkNonNullExpression(node.expression); const indexExpression = node.argumentExpression; if (!indexExpression) { const sourceFile = getSourceFileOfNode(node); if (node.parent.kind === SyntaxKind.NewExpression && (node.parent).expression === node) { const start = skipTrivia(sourceFile.text, node.expression.end); const end = node.end; this.grammarErrorAtPos(sourceFile, start, end - start, Diagnostics.new_T_cannot_be_used_to_create_an_array_Use_new_Array_T_instead); } else { const start = node.end - "]".length; const end = node.end; this.grammarErrorAtPos(sourceFile, start, end - start, Diagnostics.Expression_expected); } return this.unknownType; } const indexType = this.isForInVariableForNumericPropertyNames(indexExpression) ? this.numberType : this.checkExpression(indexExpression); if (objectType === this.unknownType || objectType === this.silentNeverType) { return objectType; } if (this.isConstEnumObjectType(objectType) && indexExpression.kind !== SyntaxKind.StringLiteral) { this.error(indexExpression, Diagnostics.A_const_enum_member_can_only_be_accessed_using_a_string_literal); return this.unknownType; } return this.checkIndexedAccessIndexType(this.getIndexedAccessType(objectType, indexType, node), node); } public checkThatExpressionIsProperSymbolReference(expression: Expression, expressionType: Type, reportError: boolean): boolean { if (expressionType === this.unknownType) { // There is already an error, so no need to report one. return false; } if (!isWellKnownSymbolSyntactically(expression)) { return false; } // Make sure the property type is the primitive symbol type if ((expressionType.flags & TypeFlags.ESSymbol) === 0) { if (reportError) { this.error(expression, Diagnostics.A_computed_property_name_of_the_form_0_must_be_of_type_symbol, getTextOfNode(expression)); } return false; } // The name is Symbol., so make sure Symbol actually resolves to the // global Symbol object const leftHandSide = (expression).expression; const leftHandSideSymbol = this.getResolvedSymbol(leftHandSide); if (!leftHandSideSymbol) { return false; } const globalESSymbol = this.getGlobalESSymbolConstructorSymbol(/*reportErrors*/ true); if (!globalESSymbol) { // Already errored when we tried to look up the symbol return false; } if (leftHandSideSymbol !== globalESSymbol) { if (reportError) { this.error(leftHandSide, Diagnostics.Symbol_reference_does_not_refer_to_the_global_Symbol_constructor_object); } return false; } return true; } public callLikeExpressionMayHaveTypeArguments(node: CallLikeExpression): node is CallExpression | NewExpression { // TODO: Also include tagged templates (https://github.com/Microsoft/TypeScript/issues/11947) return isCallOrNewExpression(node); } public resolveUntypedCall(node: CallLikeExpression): Signature { const __conv_self__ = this; if (this.callLikeExpressionMayHaveTypeArguments(node)) { // Check type arguments even though we will give an error that untyped calls may not accept type arguments. // This gets us diagnostics for the type arguments and marks them as referenced. forEach(node.typeArguments, this.checkSourceElement); } if (node.kind === SyntaxKind.TaggedTemplateExpression) { this.checkExpression((node).template); } else if (node.kind !== SyntaxKind.Decorator) { forEach((node).arguments, argument => { __conv_self__.checkExpression(argument); }); } return this.anySignature; } public resolveErrorCall(node: CallLikeExpression): Signature { this.resolveUntypedCall(node); return this.unknownSignature; } // Re-order candidate signatures into the result array. Assumes the result array to be empty. // The candidate list orders groups in reverse, but within a group signatures are kept in declaration order // A nit here is that we reorder only signatures that belong to the same symbol, // so order how inherited signatures are processed is still preserved. // interface A { (x: string): void } // interface B extends A { (x: 'foo'): string } // const b: B; // b('foo') // <- here overloads should be processed as [(x:'foo'): string, (x: string): void] public reorderCandidates(signatures: Signature[], result: Signature[]): void { let lastParent: Node; let lastSymbol: Symbol; let cutoffIndex = 0; let index: number; let specializedIndex = -1; let spliceIndex: number; Debug.assert(!result.length); for (const signature of signatures) { const symbol = signature.declaration && this.getSymbolOfNode(signature.declaration); const parent = signature.declaration && signature.declaration.parent; if (!lastSymbol || symbol === lastSymbol) { if (lastParent && parent === lastParent) { index++; } else { lastParent = parent; index = cutoffIndex; } } else { // current declaration belongs to a different symbol // set cutoffIndex so re-orderings in the future won't change result set from 0 to cutoffIndex index = cutoffIndex = result.length; lastParent = parent; } lastSymbol = symbol; // specialized signatures always need to be placed before non-specialized signatures regardless // of the cutoff position; see GH#1133 if (signature.hasLiteralTypes) { specializedIndex++; spliceIndex = specializedIndex; // The cutoff index always needs to be greater than or equal to the specialized signature index // in order to prevent non-specialized signatures from being added before a specialized // signature. cutoffIndex++; } else { spliceIndex = index; } result.splice(spliceIndex, 0, signature); } } public getSpreadArgumentIndex(args: ReadonlyArray): number { for (let i = 0; i < args.length; i++) { const arg = args[i]; if (arg && arg.kind === SyntaxKind.SpreadElement) { return i; } } return -1; } public hasCorrectArity(node: CallLikeExpression, args: ReadonlyArray, signature: Signature, signatureHelpTrailingComma = false) { let argCount: number; // Apparent number of arguments we will have in this call let typeArguments: NodeArray; // Type arguments (undefined if none) let callIsIncomplete: boolean; // In incomplete call we want to be lenient when we have too few arguments let isDecorator: boolean; let spreadArgIndex = -1; if (isJsxOpeningLikeElement(node)) { // The arity check will be done in "checkApplicableSignatureForJsxOpeningLikeElement". return true; } if (node.kind === SyntaxKind.TaggedTemplateExpression) { const tagExpression = node; // Even if the call is incomplete, we'll have a missing expression as our last argument, // so we can say the count is just the arg list length argCount = args.length; typeArguments = undefined; if (tagExpression.template.kind === SyntaxKind.TemplateExpression) { // If a tagged template expression lacks a tail literal, the call is incomplete. // Specifically, a template only can end in a TemplateTail or a Missing literal. const templateExpression = tagExpression.template; const lastSpan = lastOrUndefined(templateExpression.templateSpans); Debug.assert(lastSpan !== undefined); // we should always have at least one span. callIsIncomplete = nodeIsMissing(lastSpan.literal) || !!lastSpan.literal.isUnterminated; } else { // If the template didn't end in a backtick, or its beginning occurred right prior to EOF, // then this might actually turn out to be a TemplateHead in the future; // so we consider the call to be incomplete. const templateLiteral = tagExpression.template; Debug.assert(templateLiteral.kind === SyntaxKind.NoSubstitutionTemplateLiteral); callIsIncomplete = !!templateLiteral.isUnterminated; } } else if (node.kind === SyntaxKind.Decorator) { isDecorator = true; typeArguments = undefined; argCount = this.getEffectiveArgumentCount(node, /*args*/ undefined, signature); } else { const callExpression = node; if (!callExpression.arguments) { // This only happens when we have something of the form: 'new C' Debug.assert(callExpression.kind === SyntaxKind.NewExpression); return signature.minArgumentCount === 0; } argCount = signatureHelpTrailingComma ? args.length + 1 : args.length; // If we are missing the close parenthesis, the call is incomplete. callIsIncomplete = callExpression.arguments.end === callExpression.end; typeArguments = callExpression.typeArguments; spreadArgIndex = this.getSpreadArgumentIndex(args); } // If the user supplied type arguments, but the number of type arguments does not match // the declared number of type parameters, the call has an incorrect arity. const numTypeParameters = length(signature.typeParameters); const minTypeArgumentCount = this.getMinTypeArgumentCount(signature.typeParameters); const hasRightNumberOfTypeArgs = !typeArguments || (typeArguments.length >= minTypeArgumentCount && typeArguments.length <= numTypeParameters); if (!hasRightNumberOfTypeArgs) { return false; } // If spread arguments are present, check that they correspond to a rest parameter. If so, no // further checking is necessary. if (spreadArgIndex >= 0) { return this.isRestParameterIndex(signature, spreadArgIndex) || spreadArgIndex >= signature.minArgumentCount; } // Too many arguments implies incorrect arity. if (!signature.hasRestParameter && argCount > signature.parameters.length) { return false; } // If the call is incomplete, we should skip the lower bound check. const hasEnoughArguments = argCount >= signature.minArgumentCount; return callIsIncomplete || hasEnoughArguments; } // If type has a single call signature and no other members, return that signature. Otherwise, return undefined. public getSingleCallSignature(type: Type): Signature { if (type.flags & TypeFlags.Object) { const resolved = this.resolveStructuredTypeMembers((type)); if (resolved.callSignatures.length === 1 && resolved.constructSignatures.length === 0 && resolved.properties.length === 0 && !resolved.stringIndexInfo && !resolved.numberIndexInfo) { return resolved.callSignatures[0]; } } return undefined; } // Instantiate a generic signature in the context of a non-generic signature (section 3.8.5 in TypeScript spec) public instantiateSignatureInContextOf(signature: Signature, contextualSignature: Signature, contextualMapper?: TypeMapper, compareTypes?: TypeComparer): Signature { const __conv_self__ = this; const context = this.createInferenceContext(signature, InferenceFlags.InferUnionTypes, compareTypes); this.forEachMatchingParameterType(contextualSignature, signature, (source, target) => { // Type parameters from outer context referenced by source type are fixed by instantiation of the source type __conv_self__.inferTypes(context.inferences, __conv_self__.instantiateType(source, contextualMapper || __conv_self__.identityMapper), target); }); if (!contextualMapper) { this.inferTypes(context.inferences, this.getReturnTypeOfSignature(contextualSignature), this.getReturnTypeOfSignature(signature), InferencePriority.ReturnType); } return this.getSignatureInstantiation(signature, this.getInferredTypes(context)); } public inferTypeArguments(node: CallLikeExpression, signature: Signature, args: ReadonlyArray, excludeArgument: boolean[], context: InferenceContext): Type[] { // Clear out all the inference results from the last time inferTypeArguments was called on this context for (const inference of context.inferences) { // As an optimization, we don't have to clear (and later recompute) inferred types // for type parameters that have already been fixed on the previous call to inferTypeArguments. // It would be just as correct to reset all of them. But then we'd be repeating the same work // for the type parameters that were fixed, namely the work done by getInferredType. if (!inference.isFixed) { inference.inferredType = undefined; } } // If a contextual type is available, infer from that type to the return type of the call expression. For // example, given a 'function wrap(cb: (x: T) => U): (x: T) => U' and a call expression // 'let f: (x: string) => number = wrap(s => s.length)', we infer from the declared type of 'f' to the // return type of 'wrap'. if (isExpression(node)) { const contextualType = this.getContextualType(node); if (contextualType) { // We clone the contextual mapper to avoid disturbing a resolution in progress for an // outer call expression. Effectively we just want a snapshot of whatever has been // inferred for any outer call expression so far. const instantiatedType = this.instantiateType(contextualType, this.cloneTypeMapper(this.getContextualMapper(node))); // If the contextual type is a generic function type with a single call signature, we // instantiate the type with its own type parameters and type arguments. This ensures that // the type parameters are not erased to type any during type inference such that they can // be inferred as actual types from the contextual type. For example: // declare function arrayMap(f: (x: T) => U): (a: T[]) => U[]; // const boxElements: (a: A[]) => { value: A }[] = arrayMap(value => ({ value })); // Above, the type of the 'value' parameter is inferred to be 'A'. const contextualSignature = this.getSingleCallSignature(instantiatedType); const inferenceSourceType = contextualSignature && contextualSignature.typeParameters ? this.getOrCreateTypeFromSignature(this.getSignatureInstantiation(contextualSignature, contextualSignature.typeParameters)) : instantiatedType; const inferenceTargetType = this.getReturnTypeOfSignature(signature); // Inferences made from return types have lower priority than all other inferences. this.inferTypes(context.inferences, inferenceSourceType, inferenceTargetType, InferencePriority.ReturnType); } } const thisType = this.getThisTypeOfSignature(signature); if (thisType) { const thisArgumentNode = this.getThisArgumentOfCall(node); const thisArgumentType = thisArgumentNode ? this.checkExpression(thisArgumentNode) : this.voidType; this.inferTypes(context.inferences, thisArgumentType, thisType); } // We perform two passes over the arguments. In the first pass we infer from all arguments, but use // wildcards for all context sensitive function expressions. const argCount = this.getEffectiveArgumentCount(node, args, signature); for (let i = 0; i < argCount; i++) { const arg = this.getEffectiveArgument(node, args, i); // If the effective argument is 'undefined', then it is an argument that is present but is synthetic. if (arg === undefined || arg.kind !== SyntaxKind.OmittedExpression) { const paramType = this.getTypeAtPosition(signature, i); let argType = this.getEffectiveArgumentType(node, i); // If the effective argument type is 'undefined', there is no synthetic type // for the argument. In that case, we should check the argument. if (argType === undefined) { // For context sensitive arguments we pass the identityMapper, which is a signal to treat all // context sensitive function expressions as wildcards const mapper = excludeArgument && excludeArgument[i] !== undefined ? this.identityMapper : context; argType = this.checkExpressionWithContextualType(arg, paramType, mapper); } this.inferTypes(context.inferences, argType, paramType); } } // In the second pass we visit only context sensitive arguments, and only those that aren't excluded, this // time treating function expressions normally (which may cause previously inferred type arguments to be fixed // as we construct types for contextually typed parameters) // Decorators will not have `excludeArgument`, as their arguments cannot be contextually typed. // Tagged template expressions will always have `undefined` for `excludeArgument[0]`. if (excludeArgument) { for (let i = 0; i < argCount; i++) { // No need to check for omitted args and template expressions, their exclusion value is always undefined if (excludeArgument[i] === false) { const arg = args[i]; const paramType = this.getTypeAtPosition(signature, i); this.inferTypes(context.inferences, this.checkExpressionWithContextualType(arg, paramType, context), paramType); } } } return this.getInferredTypes(context); } public checkTypeArguments(signature: Signature, typeArgumentNodes: ReadonlyArray, typeArgumentTypes: Type[], reportErrors: boolean, headMessage?: DiagnosticMessage): boolean { const typeParameters = signature.typeParameters; let typeArgumentsAreAssignable = true; let mapper: TypeMapper; for (let i = 0; i < typeArgumentNodes.length; i++) { if (typeArgumentsAreAssignable /* so far */) { const constraint = this.getConstraintOfTypeParameter(typeParameters[i]); if (constraint) { let errorInfo: DiagnosticMessageChain; let typeArgumentHeadMessage = Diagnostics.Type_0_does_not_satisfy_the_constraint_1; if (reportErrors && headMessage) { errorInfo = chainDiagnosticMessages(errorInfo, typeArgumentHeadMessage); typeArgumentHeadMessage = headMessage; } if (!mapper) { mapper = this.createTypeMapper(typeParameters, typeArgumentTypes); } const typeArgument = typeArgumentTypes[i]; typeArgumentsAreAssignable = this.checkTypeAssignableTo(typeArgument, this.getTypeWithThisArgument(this.instantiateType(constraint, mapper), typeArgument), reportErrors ? typeArgumentNodes[i] : undefined, typeArgumentHeadMessage, errorInfo); } } } return typeArgumentsAreAssignable; } /** * Check if the given signature can possibly be a signature called by the JSX opening-like element. * @param node a JSX opening-like element we are trying to figure its call signature * @param signature a candidate signature we are trying whether it is a call signature * @param relation a relationship to check parameter and argument type * @param excludeArgument */ public checkApplicableSignatureForJsxOpeningLikeElement(node: JsxOpeningLikeElement, signature: Signature, relation: Map) { // JSX opening-like element has correct arity for stateless-function component if the one of the following condition is true: // 1. callIsIncomplete // 2. attributes property has same number of properties as the parameter object type. // We can figure that out by resolving attributes property and check number of properties in the resolved type // If the call has correct arity, we will then check if the argument type and parameter type is assignable const callIsIncomplete = node.attributes.end === node.end; // If we are missing the close "/>", the call is incomplete if (callIsIncomplete) { return true; } const headMessage = Diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1; // Stateless function components can have maximum of three arguments: "props", "context", and "updater". // However "context" and "updater" are implicit and can't be specify by users. Only the first parameter, props, // can be specified by users through attributes property. const paramType = this.getTypeAtPosition(signature, 0); const attributesType = this.checkExpressionWithContextualType(node.attributes, paramType, /*contextualMapper*/ undefined); const argProperties = this.getPropertiesOfType(attributesType); for (const arg of argProperties) { if (!this.getPropertyOfType(paramType, arg.escapedName) && this.isUnhyphenatedJsxName(arg.escapedName)) { return false; } } return this.checkTypeRelatedTo(attributesType, paramType, relation, /*errorNode*/ undefined, headMessage); } public checkApplicableSignature(node: CallLikeExpression, args: ReadonlyArray, signature: Signature, relation: Map, excludeArgument: boolean[], reportErrors: boolean) { if (isJsxOpeningLikeElement(node)) { return this.checkApplicableSignatureForJsxOpeningLikeElement((node), signature, relation); } const thisType = this.getThisTypeOfSignature(signature); if (thisType && thisType !== this.voidType && node.kind !== SyntaxKind.NewExpression) { // If the called expression is not of the form `x.f` or `x["f"]`, then sourceType = voidType // If the signature's 'this' type is voidType, then the check is skipped -- anything is compatible. // If the expression is a new expression, then the check is skipped. const thisArgumentNode = this.getThisArgumentOfCall(node); const thisArgumentType = thisArgumentNode ? this.checkExpression(thisArgumentNode) : this.voidType; const errorNode = reportErrors ? (thisArgumentNode || node) : undefined; const headMessage = Diagnostics.The_this_context_of_type_0_is_not_assignable_to_method_s_this_of_type_1; if (!this.checkTypeRelatedTo(thisArgumentType, this.getThisTypeOfSignature(signature), relation, errorNode, headMessage)) { return false; } } const headMessage = Diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1; const argCount = this.getEffectiveArgumentCount(node, args, signature); for (let i = 0; i < argCount; i++) { const arg = this.getEffectiveArgument(node, args, i); // If the effective argument is 'undefined', then it is an argument that is present but is synthetic. if (arg === undefined || arg.kind !== SyntaxKind.OmittedExpression) { // Check spread elements against rest type (from arity check we know spread argument corresponds to a rest parameter) const paramType = this.getTypeAtPosition(signature, i); // If the effective argument type is undefined, there is no synthetic type for the argument. // In that case, we should check the argument. const argType = this.getEffectiveArgumentType(node, i) || this.checkExpressionWithContextualType(arg, paramType, excludeArgument && excludeArgument[i] ? this.identityMapper : undefined); // If one or more arguments are still excluded (as indicated by a non-null excludeArgument parameter), // we obtain the regular type of any object literal arguments because we may not have inferred complete // parameter types yet and therefore excess property checks may yield false positives (see #17041). const checkArgType = excludeArgument ? this.getRegularTypeOfObjectLiteral(argType) : argType; // Use argument expression as error location when reporting errors const errorNode = reportErrors ? this.getEffectiveArgumentErrorNode(node, i, arg) : undefined; if (!this.checkTypeRelatedTo(checkArgType, paramType, relation, errorNode, headMessage)) { return false; } } } return true; } /** * Returns the this argument in calls like x.f(...) and x[f](...). Undefined otherwise. */ public getThisArgumentOfCall(node: CallLikeExpression): LeftHandSideExpression { if (node.kind === SyntaxKind.CallExpression) { const callee = (node).expression; if (callee.kind === SyntaxKind.PropertyAccessExpression) { return (callee as PropertyAccessExpression).expression; } else if (callee.kind === SyntaxKind.ElementAccessExpression) { return (callee as ElementAccessExpression).expression; } } } /** * Returns the effective arguments for an expression that works like a function invocation. * * If 'node' is a CallExpression or a NewExpression, then its argument list is returned. * If 'node' is a TaggedTemplateExpression, a new argument list is constructed from the substitution * expressions, where the first element of the list is `undefined`. * If 'node' is a Decorator, the argument list will be `undefined`, and its arguments and types * will be supplied from calls to `getEffectiveArgumentCount` and `getEffectiveArgumentType`. */ public getEffectiveCallArguments(node: CallLikeExpression): ReadonlyArray { if (node.kind === SyntaxKind.TaggedTemplateExpression) { const template = (node).template; const args: Expression[] = [undefined]; if (template.kind === SyntaxKind.TemplateExpression) { forEach((template).templateSpans, span => { args.push(span.expression); }); } return args; } else if (node.kind === SyntaxKind.Decorator) { // For a decorator, we return undefined as we will determine // the number and types of arguments for a decorator using // `getEffectiveArgumentCount` and `getEffectiveArgumentType` below. return undefined; } else if (isJsxOpeningLikeElement(node)) { return node.attributes.properties.length > 0 ? [node.attributes] : emptyArray; } else { return node.arguments || emptyArray; } } /** * Returns the effective argument count for a node that works like a function invocation. * If 'node' is a Decorator, the number of arguments is derived from the decoration * target and the signature: * If 'node.target' is a class declaration or class expression, the effective argument * count is 1. * If 'node.target' is a parameter declaration, the effective argument count is 3. * If 'node.target' is a property declaration, the effective argument count is 2. * If 'node.target' is a method or accessor declaration, the effective argument count * is 3, although it can be 2 if the signature only accepts two arguments, allowing * us to match a property decorator. * Otherwise, the argument count is the length of the 'args' array. */ public getEffectiveArgumentCount(node: CallLikeExpression, args: ReadonlyArray, signature: Signature) { if (node.kind === SyntaxKind.Decorator) { switch (node.parent.kind) { case SyntaxKind.ClassDeclaration: case SyntaxKind.ClassExpression: // A class decorator will have one argument (see `ClassDecorator` in core.d.ts) return 1; case SyntaxKind.PropertyDeclaration: // A property declaration decorator will have two arguments (see // `PropertyDecorator` in core.d.ts) return 2; case SyntaxKind.MethodDeclaration: case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: // A method or accessor declaration decorator will have two or three arguments (see // `PropertyDecorator` and `MethodDecorator` in core.d.ts) // If we are emitting decorators for ES3, we will only pass two arguments. if (this.languageVersion === ScriptTarget.ES3) { return 2; } // If the method decorator signature only accepts a target and a key, we will only // type check those arguments. return signature.parameters.length >= 3 ? 3 : 2; case SyntaxKind.Parameter: // A parameter declaration decorator will have three arguments (see // `ParameterDecorator` in core.d.ts) return 3; } } else { return args.length; } } /** * Returns the effective type of the first argument to a decorator. * If 'node' is a class declaration or class expression, the effective argument type * is the type of the static side of the class. * If 'node' is a parameter declaration, the effective argument type is either the type * of the static or instance side of the class for the parameter's parent method, * depending on whether the method is declared static. * For a constructor, the type is always the type of the static side of the class. * If 'node' is a property, method, or accessor declaration, the effective argument * type is the type of the static or instance side of the parent class for class * element, depending on whether the element is declared static. */ public getEffectiveDecoratorFirstArgumentType(node: Node): Type { // The first argument to a decorator is its `target`. if (node.kind === SyntaxKind.ClassDeclaration) { // For a class decorator, the `target` is the type of the class (e.g. the // "static" or "constructor" side of the class) const classSymbol = this.getSymbolOfNode(node); return this.getTypeOfSymbol(classSymbol); } if (node.kind === SyntaxKind.Parameter) { // For a parameter decorator, the `target` is the parent type of the // parameter's containing method. node = node.parent; if (node.kind === SyntaxKind.Constructor) { const classSymbol = this.getSymbolOfNode(node); return this.getTypeOfSymbol(classSymbol); } } if (node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.GetAccessor || node.kind === SyntaxKind.SetAccessor) { // For a property or method decorator, the `target` is the // "static"-side type of the parent of the member if the member is // declared "static"; otherwise, it is the "instance"-side type of the // parent of the member. return this.getParentTypeOfClassElement((node)); } Debug.fail("Unsupported decorator target."); return this.unknownType; } /** * Returns the effective type for the second argument to a decorator. * If 'node' is a parameter, its effective argument type is one of the following: * If 'node.parent' is a constructor, the effective argument type is 'any', as we * will emit `undefined`. * If 'node.parent' is a member with an identifier, numeric, or string literal name, * the effective argument type will be a string literal type for the member name. * If 'node.parent' is a computed property name, the effective argument type will * either be a symbol type or the string type. * If 'node' is a member with an identifier, numeric, or string literal name, the * effective argument type will be a string literal type for the member name. * If 'node' is a computed property name, the effective argument type will either * be a symbol type or the string type. * A class decorator does not have a second argument type. */ public getEffectiveDecoratorSecondArgumentType(node: Node) { // The second argument to a decorator is its `propertyKey` if (node.kind === SyntaxKind.ClassDeclaration) { Debug.fail("Class decorators should not have a second synthetic argument."); return this.unknownType; } if (node.kind === SyntaxKind.Parameter) { node = node.parent; if (node.kind === SyntaxKind.Constructor) { // For a constructor parameter decorator, the `propertyKey` will be `undefined`. return this.anyType; } // For a non-constructor parameter decorator, the `propertyKey` will be either // a string or a symbol, based on the name of the parameter's containing method. } if (node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.GetAccessor || node.kind === SyntaxKind.SetAccessor) { // The `propertyKey` for a property or method decorator will be a // string literal type if the member name is an identifier, number, or string; // otherwise, if the member name is a computed property name it will // be either string or symbol. const element = node; switch (element.name.kind) { case SyntaxKind.Identifier: return this.getLiteralType(unescapeLeadingUnderscores(element.name.escapedText)); case SyntaxKind.NumericLiteral: case SyntaxKind.StringLiteral: return this.getLiteralType(element.name.text); case SyntaxKind.ComputedPropertyName: const nameType = this.checkComputedPropertyName(element.name); if (this.isTypeAssignableToKind(nameType, TypeFlags.ESSymbol)) { return nameType; } else { return this.stringType; } default: Debug.fail("Unsupported property name."); return this.unknownType; } } Debug.fail("Unsupported decorator target."); return this.unknownType; } /** * Returns the effective argument type for the third argument to a decorator. * If 'node' is a parameter, the effective argument type is the number type. * If 'node' is a method or accessor, the effective argument type is a * `TypedPropertyDescriptor` instantiated with the type of the member. * Class and property decorators do not have a third effective argument. */ public getEffectiveDecoratorThirdArgumentType(node: Node) { // The third argument to a decorator is either its `descriptor` for a method decorator // or its `parameterIndex` for a parameter decorator if (node.kind === SyntaxKind.ClassDeclaration) { Debug.fail("Class decorators should not have a third synthetic argument."); return this.unknownType; } if (node.kind === SyntaxKind.Parameter) { // The `parameterIndex` for a parameter decorator is always a number return this.numberType; } if (node.kind === SyntaxKind.PropertyDeclaration) { Debug.fail("Property decorators should not have a third synthetic argument."); return this.unknownType; } if (node.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.GetAccessor || node.kind === SyntaxKind.SetAccessor) { // The `descriptor` for a method decorator will be a `TypedPropertyDescriptor` // for the type of the member. const propertyType = this.getTypeOfNode(node); return this.createTypedPropertyDescriptorType(propertyType); } Debug.fail("Unsupported decorator target."); return this.unknownType; } /** * Returns the effective argument type for the provided argument to a decorator. */ public getEffectiveDecoratorArgumentType(node: Decorator, argIndex: number): Type { if (argIndex === 0) { return this.getEffectiveDecoratorFirstArgumentType(node.parent); } else if (argIndex === 1) { return this.getEffectiveDecoratorSecondArgumentType(node.parent); } else if (argIndex === 2) { return this.getEffectiveDecoratorThirdArgumentType(node.parent); } Debug.fail("Decorators should not have a fourth synthetic argument."); return this.unknownType; } /** * Gets the effective argument type for an argument in a call expression. */ public getEffectiveArgumentType(node: CallLikeExpression, argIndex: number): Type { // Decorators provide special arguments, a tagged template expression provides // a special first argument, and string literals get string literal types // unless we're reporting errors if (node.kind === SyntaxKind.Decorator) { return this.getEffectiveDecoratorArgumentType((node), argIndex); } else if (argIndex === 0 && node.kind === SyntaxKind.TaggedTemplateExpression) { return this.getGlobalTemplateStringsArrayType(); } // This is not a synthetic argument, so we return 'undefined' // to signal that the caller needs to check the argument. return undefined; } /** * Gets the effective argument expression for an argument in a call expression. */ public getEffectiveArgument(node: CallLikeExpression, args: ReadonlyArray, argIndex: number) { // For a decorator or the first argument of a tagged template expression we return undefined. if (node.kind === SyntaxKind.Decorator || (argIndex === 0 && node.kind === SyntaxKind.TaggedTemplateExpression)) { return undefined; } return args[argIndex]; } /** * Gets the error node to use when reporting errors for an effective argument. */ public getEffectiveArgumentErrorNode(node: CallLikeExpression, argIndex: number, arg: Expression) { if (node.kind === SyntaxKind.Decorator) { // For a decorator, we use the expression of the decorator for error reporting. return (node).expression; } else if (argIndex === 0 && node.kind === SyntaxKind.TaggedTemplateExpression) { // For a the first argument of a tagged template expression, we use the template of the tag for error reporting. return (node).template; } else { return arg; } } public resolveCall(node: CallLikeExpression, signatures: Signature[], candidatesOutArray: Signature[], fallbackError?: DiagnosticMessage): Signature { const __conv_self__ = this; const isTaggedTemplate = node.kind === SyntaxKind.TaggedTemplateExpression; const isDecorator = node.kind === SyntaxKind.Decorator; const isJsxOpeningOrSelfClosingElement = isJsxOpeningLikeElement(node); let typeArguments: ReadonlyArray; if (!isTaggedTemplate && !isDecorator && !isJsxOpeningOrSelfClosingElement) { typeArguments = (node).typeArguments; // We already perform checking on the type arguments on the class declaration itself. if ((node).expression.kind !== SyntaxKind.SuperKeyword) { forEach(typeArguments, this.checkSourceElement); } } const candidates = candidatesOutArray || []; // reorderCandidates fills up the candidates array directly this.reorderCandidates(signatures, candidates); if (!candidates.length) { this.diagnostics.add(createDiagnosticForNode(node, Diagnostics.Call_target_does_not_contain_any_signatures)); return this.resolveErrorCall(node); } const args = this.getEffectiveCallArguments(node); // The following applies to any value of 'excludeArgument[i]': // - true: the argument at 'i' is susceptible to a one-time permanent contextual typing. // - undefined: the argument at 'i' is *not* susceptible to permanent contextual typing. // - false: the argument at 'i' *was* and *has been* permanently contextually typed. // // The idea is that we will perform type argument inference & assignability checking once // without using the susceptible parameters that are functions, and once more for each of those // parameters, contextually typing each as we go along. // // For a tagged template, then the first argument be 'undefined' if necessary // because it represents a TemplateStringsArray. // // For a decorator, no arguments are susceptible to contextual typing due to the fact // decorators are applied to a declaration by the emitter, and not to an expression. const isSingleNonGenericCandidate = candidates.length === 1 && !candidates[0].typeParameters; let excludeArgument: boolean[]; let excludeCount = 0; if (!isDecorator && !isSingleNonGenericCandidate) { // We do not need to call `getEffectiveArgumentCount` here as it only // applies when calculating the number of arguments for a decorator. for (let i = isTaggedTemplate ? 1 : 0; i < args.length; i++) { if (this.isContextSensitive(args[i])) { if (!excludeArgument) { excludeArgument = new Array(args.length); } excludeArgument[i] = true; excludeCount++; } } } // The following variables are captured and modified by calls to chooseOverload. // If overload resolution or type argument inference fails, we want to report the // best error possible. The best error is one which says that an argument was not // assignable to a parameter. This implies that everything else about the overload // was fine. So if there is any overload that is only incorrect because of an // argument, we will report an error on that one. // // function foo(s: string): void; // function foo(n: number): void; // Report argument error on this overload // function foo(): void; // foo(true); // // If none of the overloads even made it that far, there are two possibilities. // There was a problem with type arguments for some overload, in which case // report an error on that. Or none of the overloads even had correct arity, // in which case give an arity error. // // function foo(x: T): void; // Report type argument error // function foo(): void; // foo(0); // let candidateForArgumentError: Signature; let candidateForTypeArgumentError: Signature; let result: Signature; // If we are in signature help, a trailing comma indicates that we intend to provide another argument, // so we will only accept overloads with arity at least 1 higher than the current number of provided arguments. const signatureHelpTrailingComma = candidatesOutArray && node.kind === SyntaxKind.CallExpression && (node).arguments.hasTrailingComma; // Section 4.12.1: // if the candidate list contains one or more signatures for which the type of each argument // expression is a subtype of each corresponding parameter type, the return type of the first // of those signatures becomes the return type of the function call. // Otherwise, the return type of the first signature in the candidate list becomes the return // type of the function call. // // Whether the call is an error is determined by assignability of the arguments. The subtype pass // is just important for choosing the best signature. So in the case where there is only one // signature, the subtype pass is useless. So skipping it is an optimization. if (candidates.length > 1) { result = chooseOverload(candidates, this.subtypeRelation, signatureHelpTrailingComma); } if (!result) { result = chooseOverload(candidates, this.assignableRelation, signatureHelpTrailingComma); } if (result) { return result; } // No signatures were applicable. Now report errors based on the last applicable signature with // no arguments excluded from assignability checks. // If candidate is undefined, it means that no candidates had a suitable arity. In that case, // skip the checkApplicableSignature check. if (candidateForArgumentError) { if (isJsxOpeningOrSelfClosingElement) { // We do not report any error here because any error will be handled in "resolveCustomJsxElementAttributesType". return candidateForArgumentError; } // excludeArgument is undefined, in this case also equivalent to [undefined, undefined, ...] // The importance of excludeArgument is to prevent us from typing function expression parameters // in arguments too early. If possible, we'd like to only type them once we know the correct // overload. However, this matters for the case where the call is correct. When the call is // an error, we don't need to exclude any arguments, although it would cause no harm to do so. this.checkApplicableSignature(node, args, candidateForArgumentError, this.assignableRelation, /*excludeArgument*/ undefined, /*reportErrors*/ true); } else if (candidateForTypeArgumentError) { const typeArguments = (node).typeArguments; this.checkTypeArguments(candidateForTypeArgumentError, typeArguments, map(typeArguments, this.getTypeFromTypeNode), /*reportErrors*/ true, fallbackError); } else if (typeArguments && every(signatures, sig => length(sig.typeParameters) !== typeArguments.length)) { let min = Number.POSITIVE_INFINITY; let max = Number.NEGATIVE_INFINITY; for (const sig of signatures) { min = Math.min(min, this.getMinTypeArgumentCount(sig.typeParameters)); max = Math.max(max, length(sig.typeParameters)); } const paramCount = min < max ? `${min}-${max}` : min; this.diagnostics.add(createDiagnosticForNode(node, Diagnostics.Expected_0_type_arguments_but_got_1, paramCount, typeArguments.length)); } else if (args) { let min = Number.POSITIVE_INFINITY; let max = Number.NEGATIVE_INFINITY; for (const sig of signatures) { min = Math.min(min, sig.minArgumentCount); max = Math.max(max, sig.parameters.length); } const hasRestParameter = some(signatures, sig => sig.hasRestParameter); const hasSpreadArgument = this.getSpreadArgumentIndex(args) > -1; const paramCount = hasRestParameter ? min : min < max ? `${min}-${max}` : min; const argCount = args.length - (hasSpreadArgument ? 1 : 0); const error = hasRestParameter && hasSpreadArgument ? Diagnostics.Expected_at_least_0_arguments_but_got_a_minimum_of_1 : hasRestParameter ? Diagnostics.Expected_at_least_0_arguments_but_got_1 : hasSpreadArgument ? Diagnostics.Expected_0_arguments_but_got_a_minimum_of_1 : Diagnostics.Expected_0_arguments_but_got_1; this.diagnostics.add(createDiagnosticForNode(node, error, paramCount, argCount)); } else if (fallbackError) { this.diagnostics.add(createDiagnosticForNode(node, fallbackError)); } // No signature was applicable. We have already reported the errors for the invalid signature. // If this is a type resolution session, e.g. Language Service, try to get better information than anySignature. // Pick the longest signature. This way we can get a contextual type for cases like: // declare function f(a: { xa: number; xb: number; }, b: number); // f({ | // Also, use explicitly-supplied type arguments if they are provided, so we can get a contextual signature in cases like: // declare function f(k: keyof T); // f(" if (!this.produceDiagnostics) { Debug.assert(candidates.length > 0); // Else would have exited above. const bestIndex = this.getLongestCandidateIndex(candidates, this.apparentArgumentCount === undefined ? args.length : this.apparentArgumentCount); const candidate = candidates[bestIndex]; const { typeParameters } = candidate; if (typeParameters && this.callLikeExpressionMayHaveTypeArguments(node) && node.typeArguments) { const typeArguments = node.typeArguments.map(this.getTypeOfNode); while (typeArguments.length > typeParameters.length) { typeArguments.pop(); } while (typeArguments.length < typeParameters.length) { typeArguments.push(this.getDefaultTypeArgumentType(isInJavaScriptFile(node))); } const instantiated = this.createSignatureInstantiation(candidate, typeArguments); candidates[bestIndex] = instantiated; return instantiated; } return candidate; } return this.resolveErrorCall(node); function chooseOverload(candidates: Signature[], relation: Map, signatureHelpTrailingComma = false) { candidateForArgumentError = undefined; candidateForTypeArgumentError = undefined; if (isSingleNonGenericCandidate) { const candidate = candidates[0]; if (!__conv_self__.hasCorrectArity(node, args, candidate, signatureHelpTrailingComma)) { return undefined; } if (!__conv_self__.checkApplicableSignature(node, args, candidate, relation, excludeArgument, /*reportErrors*/ false)) { candidateForArgumentError = candidate; return undefined; } return candidate; } for (let candidateIndex = 0; candidateIndex < candidates.length; candidateIndex++) { const originalCandidate = candidates[candidateIndex]; if (!__conv_self__.hasCorrectArity(node, args, originalCandidate, signatureHelpTrailingComma)) { continue; } let candidate: Signature; const inferenceContext = originalCandidate.typeParameters ? __conv_self__.createInferenceContext(originalCandidate, /*flags*/ isInJavaScriptFile(node) ? InferenceFlags.AnyDefault : 0) : undefined; while (true) { candidate = originalCandidate; if (candidate.typeParameters) { let typeArgumentTypes: Type[]; if (typeArguments) { typeArgumentTypes = __conv_self__.fillMissingTypeArguments(map(typeArguments, __conv_self__.getTypeFromTypeNode), candidate.typeParameters, __conv_self__.getMinTypeArgumentCount(candidate.typeParameters)); if (!__conv_self__.checkTypeArguments(candidate, typeArguments, typeArgumentTypes, /*reportErrors*/ false)) { candidateForTypeArgumentError = originalCandidate; break; } } else { typeArgumentTypes = __conv_self__.inferTypeArguments(node, candidate, args, excludeArgument, inferenceContext); } candidate = __conv_self__.getSignatureInstantiation(candidate, typeArgumentTypes); } if (!__conv_self__.checkApplicableSignature(node, args, candidate, relation, excludeArgument, /*reportErrors*/ false)) { candidateForArgumentError = candidate; break; } if (excludeCount === 0) { candidates[candidateIndex] = candidate; return candidate; } excludeCount--; if (excludeCount > 0) { excludeArgument[indexOf(excludeArgument, /*value*/ true)] = false; } else { excludeArgument = undefined; } } } return undefined; } } public getLongestCandidateIndex(candidates: Signature[], argsCount: number): number { let maxParamsIndex = -1; let maxParams = -1; for (let i = 0; i < candidates.length; i++) { const candidate = candidates[i]; if (candidate.hasRestParameter || candidate.parameters.length >= argsCount) { return i; } if (candidate.parameters.length > maxParams) { maxParams = candidate.parameters.length; maxParamsIndex = i; } } return maxParamsIndex; } public resolveCallExpression(node: CallExpression, candidatesOutArray: Signature[]): Signature { if (node.expression.kind === SyntaxKind.SuperKeyword) { const superType = this.checkSuperExpression(node.expression); if (superType !== this.unknownType) { // In super call, the candidate signatures are the matching arity signatures of the base constructor function instantiated // with the type arguments specified in the extends clause. const baseTypeNode = getClassExtendsHeritageClauseElement(getContainingClass(node)); if (baseTypeNode) { const baseConstructors = this.getInstantiatedConstructorsForTypeArguments(superType, baseTypeNode.typeArguments, baseTypeNode); return this.resolveCall(node, baseConstructors, candidatesOutArray); } } return this.resolveUntypedCall(node); } const funcType = this.checkNonNullExpression(node.expression); if (funcType === this.silentNeverType) { return this.silentNeverSignature; } const apparentType = this.getApparentType(funcType); if (apparentType === this.unknownType) { // Another error has already been reported return this.resolveErrorCall(node); } // Technically, this signatures list may be incomplete. We are taking the apparent type, // but we are not including call signatures that may have been added to the Object or // Function interface, since they have none by default. This is a bit of a leap of faith // that the user will not add any. const callSignatures = this.getSignaturesOfType(apparentType, SignatureKind.Call); const constructSignatures = this.getSignaturesOfType(apparentType, SignatureKind.Construct); // TS 1.0 Spec: 4.12 // In an untyped function call no TypeArgs are permitted, Args can be any argument list, no contextual // types are provided for the argument expressions, and the result is always of type Any. if (this.isUntypedFunctionCall(funcType, apparentType, callSignatures.length, constructSignatures.length)) { // The unknownType indicates that an error already occurred (and was reported). No // need to report another error in this case. if (funcType !== this.unknownType && node.typeArguments) { this.error(node, Diagnostics.Untyped_function_calls_may_not_accept_type_arguments); } return this.resolveUntypedCall(node); } // If FuncExpr's apparent type(section 3.8.1) is a function type, the call is a typed function call. // TypeScript employs overload resolution in typed function calls in order to support functions // with multiple call signatures. if (!callSignatures.length) { if (constructSignatures.length) { this.error(node, Diagnostics.Value_of_type_0_is_not_callable_Did_you_mean_to_include_new, this.typeToString(funcType)); } else { this.error(node, Diagnostics.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures, this.typeToString(apparentType)); } return this.resolveErrorCall(node); } return this.resolveCall(node, callSignatures, candidatesOutArray); } /** * TS 1.0 spec: 4.12 * If FuncExpr is of type Any, or of an object type that has no call or construct signatures * but is a subtype of the Function interface, the call is an untyped function call. */ public isUntypedFunctionCall(funcType: Type, apparentFuncType: Type, numCallSignatures: number, numConstructSignatures: number) { if (this.isTypeAny(funcType)) { return true; } if (this.isTypeAny(apparentFuncType) && funcType.flags & TypeFlags.TypeParameter) { return true; } if (!numCallSignatures && !numConstructSignatures) { // We exclude union types because we may have a union of function types that happen to have // no common signatures. if (funcType.flags & TypeFlags.Union) { return false; } return this.isTypeAssignableTo(funcType, this.globalFunctionType); } return false; } public resolveNewExpression(node: NewExpression, candidatesOutArray: Signature[]): Signature { if (node.arguments && this.languageVersion < ScriptTarget.ES5) { const spreadIndex = this.getSpreadArgumentIndex(node.arguments); if (spreadIndex >= 0) { this.error(node.arguments[spreadIndex], Diagnostics.Spread_operator_in_new_expressions_is_only_available_when_targeting_ECMAScript_5_and_higher); } } let expressionType = this.checkNonNullExpression(node.expression); if (expressionType === this.silentNeverType) { return this.silentNeverSignature; } // If expressionType's apparent type(section 3.8.1) is an object type with one or // more construct signatures, the expression is processed in the same manner as a // function call, but using the construct signatures as the initial set of candidate // signatures for overload resolution. The result type of the function call becomes // the result type of the operation. expressionType = this.getApparentType(expressionType); if (expressionType === this.unknownType) { // Another error has already been reported return this.resolveErrorCall(node); } // If the expression is a class of abstract type, then it cannot be instantiated. // Note, only class declarations can be declared abstract. // In the case of a merged class-module or class-interface declaration, // only the class declaration node will have the Abstract flag set. const valueDecl = expressionType.symbol && this.getClassLikeDeclarationOfSymbol(expressionType.symbol); if (valueDecl && hasModifier(valueDecl, ModifierFlags.Abstract)) { this.error(node, Diagnostics.Cannot_create_an_instance_of_the_abstract_class_0, declarationNameToString(getNameOfDeclaration(valueDecl))); return this.resolveErrorCall(node); } // TS 1.0 spec: 4.11 // If expressionType is of type Any, Args can be any argument // list and the result of the operation is of type Any. if (this.isTypeAny(expressionType)) { if (node.typeArguments) { this.error(node, Diagnostics.Untyped_function_calls_may_not_accept_type_arguments); } return this.resolveUntypedCall(node); } // Technically, this signatures list may be incomplete. We are taking the apparent type, // but we are not including construct signatures that may have been added to the Object or // Function interface, since they have none by default. This is a bit of a leap of faith // that the user will not add any. const constructSignatures = this.getSignaturesOfType(expressionType, SignatureKind.Construct); if (constructSignatures.length) { if (!this.isConstructorAccessible(node, constructSignatures[0])) { return this.resolveErrorCall(node); } return this.resolveCall(node, constructSignatures, candidatesOutArray); } // If expressionType's apparent type is an object type with no construct signatures but // one or more call signatures, the expression is processed as a function call. A compile-time // error occurs if the result of the function call is not Void. The type of the result of the // operation is Any. It is an error to have a Void this type. const callSignatures = this.getSignaturesOfType(expressionType, SignatureKind.Call); if (callSignatures.length) { const signature = this.resolveCall(node, callSignatures, candidatesOutArray); if (!this.isJavaScriptConstructor(signature.declaration) && this.getReturnTypeOfSignature(signature) !== this.voidType) { this.error(node, Diagnostics.Only_a_void_function_can_be_called_with_the_new_keyword); } if (this.getThisTypeOfSignature(signature) === this.voidType) { this.error(node, Diagnostics.A_function_that_is_called_with_the_new_keyword_cannot_have_a_this_type_that_is_void); } return signature; } this.error(node, Diagnostics.Cannot_use_new_with_an_expression_whose_type_lacks_a_call_or_construct_signature); return this.resolveErrorCall(node); } public isConstructorAccessible(node: NewExpression, signature: Signature) { if (!signature || !signature.declaration) { return true; } const declaration = signature.declaration; const modifiers = getSelectedModifierFlags(declaration, ModifierFlags.NonPublicAccessibilityModifier); // Public constructor is accessible. if (!modifiers) { return true; } const declaringClassDeclaration = (this.getClassLikeDeclarationOfSymbol(declaration.parent.symbol)); const declaringClass = (this.getDeclaredTypeOfSymbol(declaration.parent.symbol)); // A private or protected constructor can only be instantiated within its own class (or a subclass, for protected) if (!this.isNodeWithinClass(node, declaringClassDeclaration)) { const containingClass = getContainingClass(node); if (containingClass) { const containingType = this.getTypeOfNode(containingClass); let baseTypes = this.getBaseTypes((containingType as InterfaceType)); while (baseTypes.length) { const baseType = baseTypes[0]; if (modifiers & ModifierFlags.Protected && baseType.symbol === declaration.parent.symbol) { return true; } baseTypes = this.getBaseTypes((baseType as InterfaceType)); } } if (modifiers & ModifierFlags.Private) { this.error(node, Diagnostics.Constructor_of_class_0_is_private_and_only_accessible_within_the_class_declaration, this.typeToString(declaringClass)); } if (modifiers & ModifierFlags.Protected) { this.error(node, Diagnostics.Constructor_of_class_0_is_protected_and_only_accessible_within_the_class_declaration, this.typeToString(declaringClass)); } return false; } return true; } public resolveTaggedTemplateExpression(node: TaggedTemplateExpression, candidatesOutArray: Signature[]): Signature { const tagType = this.checkExpression(node.tag); const apparentType = this.getApparentType(tagType); if (apparentType === this.unknownType) { // Another error has already been reported return this.resolveErrorCall(node); } const callSignatures = this.getSignaturesOfType(apparentType, SignatureKind.Call); const constructSignatures = this.getSignaturesOfType(apparentType, SignatureKind.Construct); if (this.isUntypedFunctionCall(tagType, apparentType, callSignatures.length, constructSignatures.length)) { return this.resolveUntypedCall(node); } if (!callSignatures.length) { this.error(node, Diagnostics.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures, this.typeToString(apparentType)); return this.resolveErrorCall(node); } return this.resolveCall(node, callSignatures, candidatesOutArray); } /** * Gets the localized diagnostic head message to use for errors when resolving a decorator as a call expression. */ public getDiagnosticHeadMessageForDecoratorResolution(node: Decorator) { switch (node.parent.kind) { case SyntaxKind.ClassDeclaration: case SyntaxKind.ClassExpression: return Diagnostics.Unable_to_resolve_signature_of_class_decorator_when_called_as_an_expression; case SyntaxKind.Parameter: return Diagnostics.Unable_to_resolve_signature_of_parameter_decorator_when_called_as_an_expression; case SyntaxKind.PropertyDeclaration: return Diagnostics.Unable_to_resolve_signature_of_property_decorator_when_called_as_an_expression; case SyntaxKind.MethodDeclaration: case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: return Diagnostics.Unable_to_resolve_signature_of_method_decorator_when_called_as_an_expression; } } /** * Resolves a decorator as if it were a call expression. */ public resolveDecorator(node: Decorator, candidatesOutArray: Signature[]): Signature { const funcType = this.checkExpression(node.expression); const apparentType = this.getApparentType(funcType); if (apparentType === this.unknownType) { return this.resolveErrorCall(node); } const callSignatures = this.getSignaturesOfType(apparentType, SignatureKind.Call); const constructSignatures = this.getSignaturesOfType(apparentType, SignatureKind.Construct); if (this.isUntypedFunctionCall(funcType, apparentType, callSignatures.length, constructSignatures.length)) { return this.resolveUntypedCall(node); } const headMessage = this.getDiagnosticHeadMessageForDecoratorResolution(node); if (!callSignatures.length) { let errorInfo: DiagnosticMessageChain; errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures, this.typeToString(apparentType)); errorInfo = chainDiagnosticMessages(errorInfo, headMessage); this.diagnostics.add(createDiagnosticForNodeFromMessageChain(node, errorInfo)); return this.resolveErrorCall(node); } return this.resolveCall(node, callSignatures, candidatesOutArray, headMessage); } /** * This function is similar to getResolvedSignature but is exclusively for trying to resolve JSX stateless-function component. * The main reason we have to use this function instead of getResolvedSignature because, the caller of this function will already check the type of openingLikeElement's tagName * and pass the type as elementType. The elementType can not be a union (as such case should be handled by the caller of this function) * Note: at this point, we are still not sure whether the opening-like element is a stateless function component or not. * @param openingLikeElement an opening-like JSX element to try to resolve as JSX stateless function * @param elementType an element type of the opneing-like element by checking opening-like element's tagname. * @param candidatesOutArray an array of signature to be filled in by the function. It is passed by signature help in the language service; * the function will fill it up with appropriate candidate signatures */ public getResolvedJsxStatelessFunctionSignature(openingLikeElement: JsxOpeningLikeElement, elementType: Type, candidatesOutArray: Signature[]): Signature { Debug.assert(!(elementType.flags & TypeFlags.Union)); const callSignature = this.resolveStatelessJsxOpeningLikeElement(openingLikeElement, elementType, candidatesOutArray); return callSignature; } /** * Try treating a given opening-like element as stateless function component and resolve a tagName to a function signature. * @param openingLikeElement an JSX opening-like element we want to try resolve its stateless function if possible * @param elementType a type of the opening-like JSX element, a result of resolving tagName in opening-like element. * @param candidatesOutArray an array of signature to be filled in by the function. It is passed by signature help in the language service; * the function will fill it up with appropriate candidate signatures * @return a resolved signature if we can find function matching function signature through resolve call or a first signature in the list of functions. * otherwise return undefined if tag-name of the opening-like element doesn't have call signatures */ public resolveStatelessJsxOpeningLikeElement(openingLikeElement: JsxOpeningLikeElement, elementType: Type, candidatesOutArray: Signature[]): Signature { // If this function is called from language service, elementType can be a union type. This is not possible if the function is called from compiler (see: resolveCustomJsxElementAttributesType) if (elementType.flags & TypeFlags.Union) { const types = (elementType as UnionType).types; let result: Signature; for (const type of types) { result = result || this.resolveStatelessJsxOpeningLikeElement(openingLikeElement, type, candidatesOutArray); } return result; } const callSignatures = elementType && this.getSignaturesOfType(elementType, SignatureKind.Call); if (callSignatures && callSignatures.length > 0) { return this.resolveCall(openingLikeElement, callSignatures, candidatesOutArray); } return undefined; } public resolveSignature(node: CallLikeExpression, candidatesOutArray?: Signature[]): Signature { switch (node.kind) { case SyntaxKind.CallExpression: return this.resolveCallExpression((node), candidatesOutArray); case SyntaxKind.NewExpression: return this.resolveNewExpression((node), candidatesOutArray); case SyntaxKind.TaggedTemplateExpression: return this.resolveTaggedTemplateExpression((node), candidatesOutArray); case SyntaxKind.Decorator: return this.resolveDecorator((node), candidatesOutArray); case SyntaxKind.JsxOpeningElement: case SyntaxKind.JsxSelfClosingElement: // This code-path is called by language service return this.resolveStatelessJsxOpeningLikeElement((node), this.checkExpression((node).tagName), candidatesOutArray); } Debug.fail("Branch in 'resolveSignature' should be unreachable."); } /** * Resolve a signature of a given call-like expression. * @param node a call-like expression to try resolve a signature for * @param candidatesOutArray an array of signature to be filled in by the function. It is passed by signature help in the language service; * the function will fill it up with appropriate candidate signatures * @return a signature of the call-like expression or undefined if one can't be found */ public getResolvedSignature(node: CallLikeExpression, candidatesOutArray?: Signature[]): Signature { const links = this.getNodeLinks(node); // If getResolvedSignature has already been called, we will have cached the resolvedSignature. // However, it is possible that either candidatesOutArray was not passed in the first time, // or that a different candidatesOutArray was passed in. Therefore, we need to redo the work // to correctly fill the candidatesOutArray. const cached = links.resolvedSignature; if (cached && cached !== this.resolvingSignature && !candidatesOutArray) { return cached; } links.resolvedSignature = this.resolvingSignature; const result = this.resolveSignature(node, candidatesOutArray); // If signature resolution originated in control flow type analysis (for example to compute the // assigned type in a flow assignment) we don't cache the result as it may be based on temporary // types from the control flow analysis. links.resolvedSignature = this.flowLoopStart === this.flowLoopCount ? result : cached; return result; } /** * Indicates whether a declaration can be treated as a constructor in a JavaScript * file. */ public isJavaScriptConstructor(node: Declaration): boolean { if (isInJavaScriptFile(node)) { // If the node has a @class tag, treat it like a constructor. if (getJSDocClassTag(node)) return true; // If the symbol of the node has members, treat it like a constructor. const symbol = isFunctionDeclaration(node) || isFunctionExpression(node) ? this.getSymbolOfNode(node) : isVariableDeclaration(node) && isFunctionExpression(node.initializer) ? this.getSymbolOfNode(node.initializer) : undefined; return symbol && symbol.members !== undefined; } return false; } public getInferredClassType(symbol: Symbol) { const links = this.getSymbolLinks(symbol); if (!links.inferredClassType) { links.inferredClassType = this.createAnonymousType(symbol, symbol.members || this.emptySymbols, emptyArray, emptyArray, /*stringIndexType*/ undefined, /*numberIndexType*/ undefined); } return links.inferredClassType; } public isInferredClassType(type: Type) { return type.symbol && this.getObjectFlags(type) & ObjectFlags.Anonymous && this.getSymbolLinks(type.symbol).inferredClassType === type; } /** * Syntactically and semantically checks a call or new expression. * @param node The call/new expression to be checked. * @returns On success, the expression's signature's return type. On failure, anyType. */ public checkCallExpression(node: CallExpression | NewExpression): Type { // Grammar checking; stop grammar-checking if checkGrammarTypeArguments return true this.checkGrammarTypeArguments(node, node.typeArguments) || this.checkGrammarArguments(node, node.arguments); const signature = this.getResolvedSignature(node); if (node.expression.kind === SyntaxKind.SuperKeyword) { return this.voidType; } if (node.kind === SyntaxKind.NewExpression) { const declaration = signature.declaration; if (declaration && declaration.kind !== SyntaxKind.Constructor && declaration.kind !== SyntaxKind.ConstructSignature && declaration.kind !== SyntaxKind.ConstructorType && !isJSDocConstructSignature(declaration)) { // When resolved signature is a call signature (and not a construct signature) the result type is any, unless // the declaring function had members created through 'x.prototype.y = expr' or 'this.y = expr' psuedodeclarations // in a JS file // Note:JS inferred classes might come from a variable declaration instead of a function declaration. // In this case, using getResolvedSymbol directly is required to avoid losing the members from the declaration. let funcSymbol = node.expression.kind === SyntaxKind.Identifier ? this.getResolvedSymbol((node.expression as Identifier)) : this.checkExpression(node.expression).symbol; if (funcSymbol && isDeclarationOfFunctionOrClassExpression(funcSymbol)) { funcSymbol = this.getSymbolOfNode((funcSymbol.valueDeclaration).initializer); } if (funcSymbol && funcSymbol.flags & SymbolFlags.Function && (funcSymbol.members || getJSDocClassTag(funcSymbol.valueDeclaration))) { return this.getInferredClassType(funcSymbol); } else if (this.noImplicitAny) { this.error(node, Diagnostics.new_expression_whose_target_lacks_a_construct_signature_implicitly_has_an_any_type); } return this.anyType; } } // In JavaScript files, calls to any identifier 'require' are treated as external module imports if (isInJavaScriptFile(node) && this.isCommonJsRequire(node)) { return this.resolveExternalModuleTypeByLiteral((node.arguments[0])); } return this.getReturnTypeOfSignature(signature); } public checkImportCallExpression(node: ImportCall): Type { // Check grammar of dynamic import this.checkGrammarArguments(node, node.arguments) || this.checkGrammarImportCallExpression(node); if (node.arguments.length === 0) { return this.createPromiseReturnType(node, this.anyType); } const specifier = node.arguments[0]; const specifierType = this.checkExpressionCached(specifier); // Even though multiple arugments is grammatically incorrect, type-check extra arguments for completion for (let i = 1; i < node.arguments.length; ++i) { this.checkExpressionCached(node.arguments[i]); } if (specifierType.flags & TypeFlags.Undefined || specifierType.flags & TypeFlags.Null || !this.isTypeAssignableTo(specifierType, this.stringType)) { this.error(specifier, Diagnostics.Dynamic_import_s_specifier_must_be_of_type_string_but_here_has_type_0, this.typeToString(specifierType)); } // resolveExternalModuleName will return undefined if the moduleReferenceExpression is not a string literal const moduleSymbol = this.resolveExternalModuleName(node, specifier); if (moduleSymbol) { const esModuleSymbol = this.resolveESModuleSymbol(moduleSymbol, specifier, /*dontRecursivelyResolve*/ true); if (esModuleSymbol) { return this.createPromiseReturnType(node, this.getTypeWithSyntheticDefaultImportType(this.getTypeOfSymbol(esModuleSymbol), esModuleSymbol)); } } return this.createPromiseReturnType(node, this.anyType); } public getTypeWithSyntheticDefaultImportType(type: Type, symbol: Symbol): Type { if (this.allowSyntheticDefaultImports && type && type !== this.unknownType) { const synthType = type as SyntheticDefaultModuleType; if (!synthType.syntheticType) { if (!this.getPropertyOfType(type, InternalSymbolName.Default)) { const memberTable = createSymbolTable(); const newSymbol = this.createSymbol(SymbolFlags.Alias, InternalSymbolName.Default); newSymbol.target = this.resolveSymbol(symbol); memberTable.set(InternalSymbolName.Default, newSymbol); const anonymousSymbol = this.createSymbol(SymbolFlags.TypeLiteral, InternalSymbolName.Type); const defaultContainingObject = this.createAnonymousType(anonymousSymbol, memberTable, emptyArray, emptyArray, /*stringIndexInfo*/ undefined, /*numberIndexInfo*/ undefined); anonymousSymbol.type = defaultContainingObject; synthType.syntheticType = this.getIntersectionType([type, defaultContainingObject]); } else { synthType.syntheticType = type; } } return synthType.syntheticType; } return type; } public isCommonJsRequire(node: Node) { if (!isRequireCall(node, /*checkArgumentIsStringLiteral*/ true)) { return false; } // Make sure require is not a local function if (!isIdentifier(node.expression)) throw Debug.fail(); const resolvedRequire = this.resolveName(node.expression, node.expression.escapedText, SymbolFlags.Value, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined); if (!resolvedRequire) { // project does not contain symbol named 'require' - assume commonjs require return true; } // project includes symbol named 'require' - make sure that it it ambient and local non-alias if (resolvedRequire.flags & SymbolFlags.Alias) { return false; } const targetDeclarationKind = resolvedRequire.flags & SymbolFlags.Function ? SyntaxKind.FunctionDeclaration : resolvedRequire.flags & SymbolFlags.Variable ? SyntaxKind.VariableDeclaration : SyntaxKind.Unknown; if (targetDeclarationKind !== SyntaxKind.Unknown) { const decl = getDeclarationOfKind(resolvedRequire, targetDeclarationKind); // function/variable declaration should be ambient return isInAmbientContext(decl); } return false; } public checkTaggedTemplateExpression(node: TaggedTemplateExpression): Type { return this.getReturnTypeOfSignature(this.getResolvedSignature(node)); } public checkAssertion(node: AssertionExpression) { return this.checkAssertionWorker(node, node.type, node.expression); } public checkAssertionWorker(errNode: Node, type: TypeNode, expression: UnaryExpression | Expression, checkMode?: CheckMode) { const exprType = this.getRegularTypeOfObjectLiteral(this.getBaseTypeOfLiteralType(this.checkExpression(expression, checkMode))); this.checkSourceElement(type); const targetType = this.getTypeFromTypeNode(type); if (this.produceDiagnostics && targetType !== this.unknownType) { const widenedType = this.getWidenedType(exprType); if (!this.isTypeComparableTo(targetType, widenedType)) { this.checkTypeComparableTo(exprType, targetType, errNode, Diagnostics.Type_0_cannot_be_converted_to_type_1); } } return targetType; } public checkNonNullAssertion(node: NonNullExpression) { return this.getNonNullableType(this.checkExpression(node.expression)); } public checkMetaProperty(node: MetaProperty) { this.checkGrammarMetaProperty(node); const container = getNewTargetContainer(node); if (!container) { this.error(node, Diagnostics.Meta_property_0_is_only_allowed_in_the_body_of_a_function_declaration_function_expression_or_constructor, "new.target"); return this.unknownType; } else if (container.kind === SyntaxKind.Constructor) { const symbol = this.getSymbolOfNode(container.parent); return this.getTypeOfSymbol(symbol); } else { const symbol = this.getSymbolOfNode(container); return this.getTypeOfSymbol(symbol); } } public getTypeOfParameter(symbol: Symbol) { const type = this.getTypeOfSymbol(symbol); if (this.strictNullChecks) { const declaration = symbol.valueDeclaration; if (declaration && (declaration).initializer) { return this.getNullableType(type, TypeFlags.Undefined); } } return type; } public getTypeAtPosition(signature: Signature, pos: number): Type { return signature.hasRestParameter ? pos < signature.parameters.length - 1 ? this.getTypeOfParameter(signature.parameters[pos]) : this.getRestTypeOfSignature(signature) : pos < signature.parameters.length ? this.getTypeOfParameter(signature.parameters[pos]) : this.anyType; } public getTypeOfFirstParameterOfSignature(signature: Signature) { return signature.parameters.length > 0 ? this.getTypeAtPosition(signature, 0) : this.neverType; } public inferFromAnnotatedParameters(signature: Signature, context: Signature, mapper: TypeMapper) { const len = signature.parameters.length - (signature.hasRestParameter ? 1 : 0); for (let i = 0; i < len; i++) { const declaration = signature.parameters[i].valueDeclaration; if (declaration.type) { const typeNode = getEffectiveTypeAnnotationNode(declaration); if (typeNode) { this.inferTypes((mapper).inferences, this.getTypeFromTypeNode(typeNode), this.getTypeAtPosition(context, i)); } } } } public assignContextualParameterTypes(signature: Signature, context: Signature) { signature.typeParameters = context.typeParameters; if (context.thisParameter) { const parameter = signature.thisParameter; if (!parameter || parameter.valueDeclaration && !(parameter.valueDeclaration).type) { if (!parameter) { signature.thisParameter = this.createSymbolWithType(context.thisParameter, /*type*/ undefined); } this.assignTypeToParameterAndFixTypeParameters(signature.thisParameter, this.getTypeOfSymbol(context.thisParameter)); } } const len = signature.parameters.length - (signature.hasRestParameter ? 1 : 0); for (let i = 0; i < len; i++) { const parameter = signature.parameters[i]; if (!getEffectiveTypeAnnotationNode(parameter.valueDeclaration)) { const contextualParameterType = this.getTypeAtPosition(context, i); this.assignTypeToParameterAndFixTypeParameters(parameter, contextualParameterType); } } if (signature.hasRestParameter && this.isRestParameterIndex(context, signature.parameters.length - 1)) { const parameter = lastOrUndefined(signature.parameters); if (!getEffectiveTypeAnnotationNode(parameter.valueDeclaration)) { const contextualParameterType = this.getTypeOfSymbol(lastOrUndefined(context.parameters)); this.assignTypeToParameterAndFixTypeParameters(parameter, contextualParameterType); } } } // When contextual typing assigns a type to a parameter that contains a binding pattern, we also need to push // the destructured type into the contained binding elements. public assignBindingElementTypes(pattern: BindingPattern) { for (const element of pattern.elements) { if (!isOmittedExpression(element)) { if (element.name.kind === SyntaxKind.Identifier) { this.getSymbolLinks(this.getSymbolOfNode(element)).type = this.getTypeForBindingElement(element); } else { this.assignBindingElementTypes(element.name); } } } } public assignTypeToParameterAndFixTypeParameters(parameter: Symbol, contextualType: Type) { const links = this.getSymbolLinks(parameter); if (!links.type) { links.type = contextualType; const decl = parameter.valueDeclaration as ParameterDeclaration; if (decl.name.kind !== SyntaxKind.Identifier) { // if inference didn't come up with anything but {}, fall back to the binding pattern if present. if (links.type === this.emptyObjectType) { links.type = this.getTypeFromBindingPattern(decl.name); } this.assignBindingElementTypes(decl.name); } } } public createPromiseType(promisedType: Type): Type { // creates a `Promise` type where `T` is the promisedType argument const globalPromiseType = this.getGlobalPromiseType(/*reportErrors*/ true); if (globalPromiseType !== this.emptyGenericType) { // if the promised type is itself a promise, get the underlying type; otherwise, fallback to the promised type promisedType = this.getAwaitedType(promisedType) || this.emptyObjectType; return this.createTypeReference((globalPromiseType), [promisedType]); } return this.emptyObjectType; } public createPromiseReturnType(func: FunctionLikeDeclaration | ImportCall, promisedType: Type) { const promiseType = this.createPromiseType(promisedType); if (promiseType === this.emptyObjectType) { this.error(func, isImportCall(func) ? Diagnostics.A_dynamic_import_call_returns_a_Promise_Make_sure_you_have_a_declaration_for_Promise_or_include_ES2015_in_your_lib_option : Diagnostics.An_async_function_or_method_must_return_a_Promise_Make_sure_you_have_a_declaration_for_Promise_or_include_ES2015_in_your_lib_option); return this.unknownType; } else if (!this.getGlobalPromiseConstructorSymbol(/*reportErrors*/ true)) { this.error(func, isImportCall(func) ? Diagnostics.A_dynamic_import_call_in_ES5_SlashES3_requires_the_Promise_constructor_Make_sure_you_have_a_declaration_for_the_Promise_constructor_or_include_ES2015_in_your_lib_option : Diagnostics.An_async_function_or_method_in_ES5_SlashES3_requires_the_Promise_constructor_Make_sure_you_have_a_declaration_for_the_Promise_constructor_or_include_ES2015_in_your_lib_option); } return promiseType; } public getReturnTypeFromBody(func: FunctionLikeDeclaration, checkMode?: CheckMode): Type { const contextualSignature = this.getContextualSignatureForFunctionLikeDeclaration(func); if (!func.body) { return this.unknownType; } const functionFlags = getFunctionFlags(func); let type: Type; if (func.body.kind !== SyntaxKind.Block) { type = this.checkExpressionCached((func.body), checkMode); if (functionFlags & FunctionFlags.Async) { // From within an async function you can return either a non-promise value or a promise. Any // Promise/A+ compatible implementation will always assimilate any foreign promise, so the // return type of the body should be unwrapped to its awaited type, which we will wrap in // the native Promise type later in this function. type = this.checkAwaitedType(type, /*errorNode*/ func, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member); } } else { let types: Type[]; if (functionFlags & FunctionFlags.Generator) { types = concatenate(this.checkAndAggregateYieldOperandTypes(func, checkMode), this.checkAndAggregateReturnExpressionTypes(func, checkMode)); if (!types || types.length === 0) { const iterableIteratorAny = functionFlags & FunctionFlags.Async ? this.createAsyncIterableIteratorType(this.anyType) // AsyncGenerator function : this.createIterableIteratorType(this.anyType); // Generator function if (this.noImplicitAny) { this.error(func.asteriskToken, Diagnostics.Generator_implicitly_has_type_0_because_it_does_not_yield_any_values_Consider_supplying_a_return_type, this.typeToString(iterableIteratorAny)); } return iterableIteratorAny; } } else { types = this.checkAndAggregateReturnExpressionTypes(func, checkMode); if (!types) { // For an async function, the return type will not be never, but rather a Promise for never. return functionFlags & FunctionFlags.Async ? this.createPromiseReturnType(func, this.neverType) // Async function : this.neverType; // Normal function } if (types.length === 0) { // For an async function, the return type will not be void, but rather a Promise for void. return functionFlags & FunctionFlags.Async ? this.createPromiseReturnType(func, this.voidType) // Async function : this.voidType; // Normal function } } // Return a union of the return expression types. type = this.getUnionType(types, /*subtypeReduction*/ true); if (functionFlags & FunctionFlags.Generator) { type = functionFlags & FunctionFlags.Async ? this.createAsyncIterableIteratorType(type) // AsyncGenerator function : this.createIterableIteratorType(type); // Generator function } } if (!contextualSignature) { this.reportErrorsFromWidening(func, type); } if (this.isUnitType(type) && !(contextualSignature && this.isLiteralContextualType(contextualSignature === this.getSignatureFromDeclaration(func) ? type : this.getReturnTypeOfSignature(contextualSignature)))) { type = this.getWidenedLiteralType(type); } const widenedType = this.getWidenedType(type); // From within an async function you can return either a non-promise value or a promise. Any // Promise/A+ compatible implementation will always assimilate any foreign promise, so the // return type of the body is awaited type of the body, wrapped in a native Promise type. return (functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Async ? this.createPromiseReturnType(func, widenedType) // Async function : widenedType; // Generator function, AsyncGenerator function, or normal function } public checkAndAggregateYieldOperandTypes(func: FunctionLikeDeclaration, checkMode: CheckMode): Type[] { const __conv_self__ = this; const aggregatedTypes: Type[] = []; const functionFlags = getFunctionFlags(func); forEachYieldExpression((func.body), yieldExpression => { const expr = yieldExpression.expression; if (expr) { let type = __conv_self__.checkExpressionCached(expr, checkMode); if (yieldExpression.asteriskToken) { // A yield* expression effectively yields everything that its operand yields type = __conv_self__.checkIteratedTypeOrElementType(type, yieldExpression.expression, /*allowStringInput*/ false, (functionFlags & FunctionFlags.Async) !== 0); } if (functionFlags & FunctionFlags.Async) { type = __conv_self__.checkAwaitedType(type, expr, yieldExpression.asteriskToken ? Diagnostics.Type_of_iterated_elements_of_a_yield_Asterisk_operand_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member : Diagnostics.Type_of_yield_operand_in_an_async_generator_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member); } if (!contains(aggregatedTypes, type)) { aggregatedTypes.push(type); } } }); return aggregatedTypes; } public isExhaustiveSwitchStatement(node: SwitchStatement): boolean { if (!node.possiblyExhaustive) { return false; } const type = this.getTypeOfExpression(node.expression); if (!this.isLiteralType(type)) { return false; } const switchTypes = this.getSwitchClauseTypes(node); if (!switchTypes.length) { return false; } return this.eachTypeContainedIn(this.mapType(type, this.getRegularTypeOfLiteralType), switchTypes); } public functionHasImplicitReturn(func: FunctionLikeDeclaration) { if (!(func.flags & NodeFlags.HasImplicitReturn)) { return false; } const lastStatement = lastOrUndefined((func.body).statements); if (lastStatement && lastStatement.kind === SyntaxKind.SwitchStatement && this.isExhaustiveSwitchStatement((lastStatement))) { return false; } return true; } public checkAndAggregateReturnExpressionTypes(func: FunctionLikeDeclaration, checkMode: CheckMode): Type[] { const __conv_self__ = this; const functionFlags = getFunctionFlags(func); const aggregatedTypes: Type[] = []; let hasReturnWithNoExpression = this.functionHasImplicitReturn(func); let hasReturnOfTypeNever = false; forEachReturnStatement((func.body), returnStatement => { const expr = returnStatement.expression; if (expr) { let type = __conv_self__.checkExpressionCached(expr, checkMode); if (functionFlags & FunctionFlags.Async) { // From within an async function you can return either a non-promise value or a promise. Any // Promise/A+ compatible implementation will always assimilate any foreign promise, so the // return type of the body should be unwrapped to its awaited type, which should be wrapped in // the native Promise type by the caller. type = __conv_self__.checkAwaitedType(type, func, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member); } if (type.flags & TypeFlags.Never) { hasReturnOfTypeNever = true; } else if (!contains(aggregatedTypes, type)) { aggregatedTypes.push(type); } } else { hasReturnWithNoExpression = true; } }); if (aggregatedTypes.length === 0 && !hasReturnWithNoExpression && (hasReturnOfTypeNever || func.kind === SyntaxKind.FunctionExpression || func.kind === SyntaxKind.ArrowFunction)) { return undefined; } if (this.strictNullChecks && aggregatedTypes.length && hasReturnWithNoExpression) { if (!contains(aggregatedTypes, this.undefinedType)) { aggregatedTypes.push(this.undefinedType); } } return aggregatedTypes; } /** * TypeScript Specification 1.0 (6.3) - July 2014 * An explicitly typed function whose return type isn't the Void type, * the Any type, or a union type containing the Void or Any type as a constituent * must have at least one return statement somewhere in its body. * An exception to this rule is if the function implementation consists of a single 'throw' statement. * * @param returnType - return type of the function, can be undefined if return type is not explicitly specified */ public checkAllCodePathsInNonVoidFunctionReturnOrThrow(func: FunctionLikeDeclaration, returnType: Type): void { if (!this.produceDiagnostics) { return; } // Functions with with an explicitly specified 'void' or 'any' return type don't need any return expressions. if (returnType && this.maybeTypeOfKind(returnType, TypeFlags.Any | TypeFlags.Void)) { return; } // If all we have is a function signature, or an arrow function with an expression body, then there is nothing to check. // also if HasImplicitReturn flag is not set this means that all codepaths in function body end with return or throw if (nodeIsMissing(func.body) || func.body.kind !== SyntaxKind.Block || !this.functionHasImplicitReturn(func)) { return; } const hasExplicitReturn = func.flags & NodeFlags.HasExplicitReturn; if (returnType && returnType.flags & TypeFlags.Never) { this.error(getEffectiveReturnTypeNode(func), Diagnostics.A_function_returning_never_cannot_have_a_reachable_end_point); } else if (returnType && !hasExplicitReturn) { // minimal check: function has syntactic return type annotation and no explicit return statements in the body // this function does not conform to the specification. // NOTE: having returnType !== undefined is a precondition for entering this branch so func.type will always be present this.error(getEffectiveReturnTypeNode(func), Diagnostics.A_function_whose_declared_type_is_neither_void_nor_any_must_return_a_value); } else if (returnType && this.strictNullChecks && !this.isTypeAssignableTo(this.undefinedType, returnType)) { this.error(getEffectiveReturnTypeNode(func), Diagnostics.Function_lacks_ending_return_statement_and_return_type_does_not_include_undefined); } else if (this.compilerOptions.noImplicitReturns) { if (!returnType) { // If return type annotation is omitted check if function has any explicit return statements. // If it does not have any - its inferred return type is void - don't do any checks. // Otherwise get inferred return type from function body and report error only if it is not void / anytype if (!hasExplicitReturn) { return; } const inferredReturnType = this.getReturnTypeOfSignature(this.getSignatureFromDeclaration(func)); if (this.isUnwrappedReturnTypeVoidOrAny(func, inferredReturnType)) { return; } } this.error(getEffectiveReturnTypeNode(func) || func, Diagnostics.Not_all_code_paths_return_a_value); } } public checkFunctionExpressionOrObjectLiteralMethod(node: FunctionExpression | MethodDeclaration, checkMode?: CheckMode): Type { Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isObjectLiteralMethod(node)); // The identityMapper object is used to indicate that function expressions are wildcards if (checkMode === CheckMode.SkipContextSensitive && this.isContextSensitive(node)) { this.checkNodeDeferred(node); return this.anyFunctionType; } // Grammar checking const hasGrammarError = this.checkGrammarFunctionLikeDeclaration(node); if (!hasGrammarError && node.kind === SyntaxKind.FunctionExpression) { this.checkGrammarForGenerator(node); } const links = this.getNodeLinks(node); const type = this.getTypeOfSymbol(node.symbol); // Check if function expression is contextually typed and assign parameter types if so. if (!(links.flags & NodeCheckFlags.ContextChecked)) { const contextualSignature = this.getContextualSignature(node); // If a type check is started at a function expression that is an argument of a function call, obtaining the // contextual type may recursively get back to here during overload resolution of the call. If so, we will have // already assigned contextual types. if (!(links.flags & NodeCheckFlags.ContextChecked)) { links.flags |= NodeCheckFlags.ContextChecked; if (contextualSignature) { const signature = this.getSignaturesOfType(type, SignatureKind.Call)[0]; if (this.isContextSensitive(node)) { const contextualMapper = this.getContextualMapper(node); if (checkMode === CheckMode.Inferential) { this.inferFromAnnotatedParameters(signature, contextualSignature, contextualMapper); } const instantiatedContextualSignature = contextualMapper === this.identityMapper ? contextualSignature : this.instantiateSignature(contextualSignature, contextualMapper); this.assignContextualParameterTypes(signature, instantiatedContextualSignature); } if (!getEffectiveReturnTypeNode(node) && !signature.resolvedReturnType) { const returnType = this.getReturnTypeFromBody(node, checkMode); if (!signature.resolvedReturnType) { signature.resolvedReturnType = returnType; } } } this.checkSignatureDeclaration(node); this.checkNodeDeferred(node); } } if (this.produceDiagnostics && node.kind !== SyntaxKind.MethodDeclaration) { this.checkCollisionWithCapturedSuperVariable(node, (node).name); this.checkCollisionWithCapturedThisVariable(node, (node).name); this.checkCollisionWithCapturedNewTargetVariable(node, (node).name); } return type; } public checkFunctionExpressionOrObjectLiteralMethodDeferred(node: ArrowFunction | FunctionExpression | MethodDeclaration) { Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isObjectLiteralMethod(node)); const functionFlags = getFunctionFlags(node); const returnTypeNode = getEffectiveReturnTypeNode(node); const returnOrPromisedType = returnTypeNode && ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Async ? this.checkAsyncFunctionReturnType(node) : this.getTypeFromTypeNode(returnTypeNode)); // AsyncGenerator function, Generator function, or normal function if ((functionFlags & FunctionFlags.Generator) === 0) { // return is not necessary in the body of generators this.checkAllCodePathsInNonVoidFunctionReturnOrThrow(node, returnOrPromisedType); } if (node.body) { if (!returnTypeNode) { // There are some checks that are only performed in getReturnTypeFromBody, that may produce errors // we need. An example is the noImplicitAny errors resulting from widening the return expression // of a function. Because checking of function expression bodies is deferred, there was never an // appropriate time to do this during the main walk of the file (see the comment at the top of // checkFunctionExpressionBodies). So it must be done now. this.getReturnTypeOfSignature(this.getSignatureFromDeclaration(node)); } if (node.body.kind === SyntaxKind.Block) { this.checkSourceElement(node.body); } else { // From within an async function you can return either a non-promise value or a promise. Any // Promise/A+ compatible implementation will always assimilate any foreign promise, so we // should not be checking assignability of a promise to the return type. Instead, we need to // check assignability of the awaited type of the expression body against the promised type of // its return type annotation. const exprType = this.checkExpression((node.body)); if (returnOrPromisedType) { if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Async) { const awaitedType = this.checkAwaitedType(exprType, node.body, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member); this.checkTypeAssignableTo(awaitedType, returnOrPromisedType, node.body); } else { this.checkTypeAssignableTo(exprType, returnOrPromisedType, node.body); } } } this.registerForUnusedIdentifiersCheck(node); } } public checkArithmeticOperandType(operand: Node, type: Type, diagnostic: DiagnosticMessage): boolean { if (!this.isTypeAssignableToKind(type, TypeFlags.NumberLike)) { this.error(operand, diagnostic); return false; } return true; } public isReadonlySymbol(symbol: Symbol): boolean { // The following symbols are considered read-only: // Properties with a 'readonly' modifier // Variables declared with 'const' // Get accessors without matching set accessors // Enum members // Unions and intersections of the above (unions and intersections eagerly set isReadonly on creation) return !!(getCheckFlags(symbol) & CheckFlags.Readonly || symbol.flags & SymbolFlags.Property && getDeclarationModifierFlagsFromSymbol(symbol) & ModifierFlags.Readonly || symbol.flags & SymbolFlags.Variable && this.getDeclarationNodeFlagsFromSymbol(symbol) & NodeFlags.Const || symbol.flags & SymbolFlags.Accessor && !(symbol.flags & SymbolFlags.SetAccessor) || symbol.flags & SymbolFlags.EnumMember); } public isReferenceToReadonlyEntity(expr: Expression, symbol: Symbol): boolean { if (this.isReadonlySymbol(symbol)) { // Allow assignments to readonly properties within constructors of the same class declaration. if (symbol.flags & SymbolFlags.Property && (expr.kind === SyntaxKind.PropertyAccessExpression || expr.kind === SyntaxKind.ElementAccessExpression) && (expr as PropertyAccessExpression | ElementAccessExpression).expression.kind === SyntaxKind.ThisKeyword) { // Look for if this is the constructor for the class that `symbol` is a property of. const func = getContainingFunction(expr); if (!(func && func.kind === SyntaxKind.Constructor)) { return true; } // If func.parent is a class and symbol is a (readonly) property of that class, or // if func is a constructor and symbol is a (readonly) parameter property declared in it, // then symbol is writeable here. return !(func.parent === symbol.valueDeclaration.parent || func === symbol.valueDeclaration.parent); } return true; } return false; } public isReferenceThroughNamespaceImport(expr: Expression): boolean { if (expr.kind === SyntaxKind.PropertyAccessExpression || expr.kind === SyntaxKind.ElementAccessExpression) { const node = skipParentheses((expr as PropertyAccessExpression | ElementAccessExpression).expression); if (node.kind === SyntaxKind.Identifier) { const symbol = this.getNodeLinks(node).resolvedSymbol; if (symbol.flags & SymbolFlags.Alias) { const declaration = this.getDeclarationOfAliasSymbol(symbol); return declaration && declaration.kind === SyntaxKind.NamespaceImport; } } } return false; } public checkReferenceExpression(expr: Expression, invalidReferenceMessage: DiagnosticMessage): boolean { // References are combinations of identifiers, parentheses, and property accesses. const node = skipOuterExpressions(expr, OuterExpressionKinds.Assertions | OuterExpressionKinds.Parentheses); if (node.kind !== SyntaxKind.Identifier && node.kind !== SyntaxKind.PropertyAccessExpression && node.kind !== SyntaxKind.ElementAccessExpression) { this.error(expr, invalidReferenceMessage); return false; } return true; } public checkDeleteExpression(node: DeleteExpression): Type { this.checkExpression(node.expression); const expr = skipParentheses(node.expression); if (expr.kind !== SyntaxKind.PropertyAccessExpression && expr.kind !== SyntaxKind.ElementAccessExpression) { this.error(expr, Diagnostics.The_operand_of_a_delete_operator_must_be_a_property_reference); return this.booleanType; } const links = this.getNodeLinks(expr); const symbol = this.getExportSymbolOfValueSymbolIfExported(links.resolvedSymbol); if (symbol && this.isReadonlySymbol(symbol)) { this.error(expr, Diagnostics.The_operand_of_a_delete_operator_cannot_be_a_read_only_property); } return this.booleanType; } public checkTypeOfExpression(node: TypeOfExpression): Type { this.checkExpression(node.expression); return this.typeofType; } public checkVoidExpression(node: VoidExpression): Type { this.checkExpression(node.expression); return this.undefinedWideningType; } public checkAwaitExpression(node: AwaitExpression): Type { // Grammar checking if (this.produceDiagnostics) { if (!(node.flags & NodeFlags.AwaitContext)) { this.grammarErrorOnFirstToken(node, Diagnostics.await_expression_is_only_allowed_within_an_async_function); } if (this.isInParameterInitializerBeforeContainingFunction(node)) { this.error(node, Diagnostics.await_expressions_cannot_be_used_in_a_parameter_initializer); } } const operandType = this.checkExpression(node.expression); return this.checkAwaitedType(operandType, node, Diagnostics.Type_of_await_operand_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member); } public checkPrefixUnaryExpression(node: PrefixUnaryExpression): Type { const operandType = this.checkExpression(node.operand); if (operandType === this.silentNeverType) { return this.silentNeverType; } if (node.operand.kind === SyntaxKind.NumericLiteral) { if (node.operator === SyntaxKind.MinusToken) { return this.getFreshTypeOfLiteralType(this.getLiteralType(-(node.operand).text)); } else if (node.operator === SyntaxKind.PlusToken) { return this.getFreshTypeOfLiteralType(this.getLiteralType(+(node.operand).text)); } } switch (node.operator) { case SyntaxKind.PlusToken: case SyntaxKind.MinusToken: case SyntaxKind.TildeToken: this.checkNonNullType(operandType, node.operand); if (this.maybeTypeOfKind(operandType, TypeFlags.ESSymbol)) { this.error(node.operand, Diagnostics.The_0_operator_cannot_be_applied_to_type_symbol, tokenToString(node.operator)); } return this.numberType; case SyntaxKind.ExclamationToken: const facts = this.getTypeFacts(operandType) & (TypeFacts.Truthy | TypeFacts.Falsy); return facts === TypeFacts.Truthy ? this.falseType : facts === TypeFacts.Falsy ? this.trueType : this.booleanType; case SyntaxKind.PlusPlusToken: case SyntaxKind.MinusMinusToken: const ok = this.checkArithmeticOperandType(node.operand, this.checkNonNullType(operandType, node.operand), Diagnostics.An_arithmetic_operand_must_be_of_type_any_number_or_an_enum_type); if (ok) { // run check only if former checks succeeded to avoid reporting cascading errors this.checkReferenceExpression(node.operand, Diagnostics.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access); } return this.numberType; } return this.unknownType; } public checkPostfixUnaryExpression(node: PostfixUnaryExpression): Type { const operandType = this.checkExpression(node.operand); if (operandType === this.silentNeverType) { return this.silentNeverType; } const ok = this.checkArithmeticOperandType(node.operand, this.checkNonNullType(operandType, node.operand), Diagnostics.An_arithmetic_operand_must_be_of_type_any_number_or_an_enum_type); if (ok) { // run check only if former checks succeeded to avoid reporting cascading errors this.checkReferenceExpression(node.operand, Diagnostics.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access); } return this.numberType; } // Return true if type might be of the given kind. A union or intersection type might be of a given // kind if at least one constituent type is of the given kind. public maybeTypeOfKind(type: Type, kind: TypeFlags): boolean { if (type.flags & kind) { return true; } if (type.flags & TypeFlags.UnionOrIntersection) { const types = (type).types; for (const t of types) { if (this.maybeTypeOfKind(t, kind)) { return true; } } } return false; } public isTypeAssignableToKind(source: Type, kind: TypeFlags, strict?: boolean): boolean { if (source.flags & kind) { return true; } if (strict && source.flags & (TypeFlags.Any | TypeFlags.Void | TypeFlags.Undefined | TypeFlags.Null)) { return false; } return (kind & TypeFlags.NumberLike && this.isTypeAssignableTo(source, this.numberType)) || (kind & TypeFlags.StringLike && this.isTypeAssignableTo(source, this.stringType)) || (kind & TypeFlags.BooleanLike && this.isTypeAssignableTo(source, this.booleanType)) || (kind & TypeFlags.Void && this.isTypeAssignableTo(source, this.voidType)) || (kind & TypeFlags.Never && this.isTypeAssignableTo(source, this.neverType)) || (kind & TypeFlags.Null && this.isTypeAssignableTo(source, this.nullType)) || (kind & TypeFlags.Undefined && this.isTypeAssignableTo(source, this.undefinedType)) || (kind & TypeFlags.ESSymbol && this.isTypeAssignableTo(source, this.esSymbolType)) || (kind & TypeFlags.NonPrimitive && this.isTypeAssignableTo(source, this.nonPrimitiveType)); } public isConstEnumObjectType(type: Type): boolean { return this.getObjectFlags(type) & ObjectFlags.Anonymous && type.symbol && this.isConstEnumSymbol(type.symbol); } public isConstEnumSymbol(symbol: Symbol): boolean { return (symbol.flags & SymbolFlags.ConstEnum) !== 0; } public checkInstanceOfExpression(left: Expression, right: Expression, leftType: Type, rightType: Type): Type { if (leftType === this.silentNeverType || rightType === this.silentNeverType) { return this.silentNeverType; } // TypeScript 1.0 spec (April 2014): 4.15.4 // The instanceof operator requires the left operand to be of type Any, an object type, or a type parameter type, // and the right operand to be of type Any, a subtype of the 'Function' interface type, or have a call or construct signature. // The result is always of the Boolean primitive type. // NOTE: do not raise error if leftType is unknown as related error was already reported if (!this.isTypeAny(leftType) && this.isTypeAssignableToKind(leftType, TypeFlags.Primitive)) { this.error(left, Diagnostics.The_left_hand_side_of_an_instanceof_expression_must_be_of_type_any_an_object_type_or_a_type_parameter); } // NOTE: do not raise error if right is unknown as related error was already reported if (!(this.isTypeAny(rightType) || this.getSignaturesOfType(rightType, SignatureKind.Call).length || this.getSignaturesOfType(rightType, SignatureKind.Construct).length || this.isTypeSubtypeOf(rightType, this.globalFunctionType))) { this.error(right, Diagnostics.The_right_hand_side_of_an_instanceof_expression_must_be_of_type_any_or_of_a_type_assignable_to_the_Function_interface_type); } return this.booleanType; } public checkInExpression(left: Expression, right: Expression, leftType: Type, rightType: Type): Type { if (leftType === this.silentNeverType || rightType === this.silentNeverType) { return this.silentNeverType; } leftType = this.checkNonNullType(leftType, left); rightType = this.checkNonNullType(rightType, right); // TypeScript 1.0 spec (April 2014): 4.15.5 // The in operator requires the left operand to be of type Any, the String primitive type, or the Number primitive type, // and the right operand to be of type Any, an object type, or a type parameter type. // The result is always of the Boolean primitive type. if (!(this.isTypeComparableTo(leftType, this.stringType) || this.isTypeAssignableToKind(leftType, TypeFlags.NumberLike | TypeFlags.ESSymbol))) { this.error(left, Diagnostics.The_left_hand_side_of_an_in_expression_must_be_of_type_any_string_number_or_symbol); } if (!this.isTypeAssignableToKind(rightType, TypeFlags.NonPrimitive | TypeFlags.TypeVariable)) { this.error(right, Diagnostics.The_right_hand_side_of_an_in_expression_must_be_of_type_any_an_object_type_or_a_type_parameter); } return this.booleanType; } public checkObjectLiteralAssignment(node: ObjectLiteralExpression, sourceType: Type): Type { const properties = node.properties; for (const p of properties) { this.checkObjectLiteralDestructuringPropertyAssignment(sourceType, p, properties); } return sourceType; } /** Note: If property cannot be a SpreadAssignment, then allProperties does not need to be provided */ public checkObjectLiteralDestructuringPropertyAssignment(objectLiteralType: Type, property: ObjectLiteralElementLike, allProperties?: ReadonlyArray) { if (property.kind === SyntaxKind.PropertyAssignment || property.kind === SyntaxKind.ShorthandPropertyAssignment) { const name = (property).name; if (name.kind === SyntaxKind.ComputedPropertyName) { this.checkComputedPropertyName((name)); } if (this.isComputedNonLiteralName(name)) { return undefined; } const text = getTextOfPropertyName(name); const type = this.isTypeAny(objectLiteralType) ? objectLiteralType : this.getTypeOfPropertyOfType(objectLiteralType, text) || this.isNumericLiteralName(text) && this.getIndexTypeOfType(objectLiteralType, IndexKind.Number) || this.getIndexTypeOfType(objectLiteralType, IndexKind.String); if (type) { if (property.kind === SyntaxKind.ShorthandPropertyAssignment) { return this.checkDestructuringAssignment((property), type); } else { // non-shorthand property assignments should always have initializers return this.checkDestructuringAssignment((property).initializer, type); } } else { this.error(name, Diagnostics.Type_0_has_no_property_1_and_no_string_index_signature, this.typeToString(objectLiteralType), declarationNameToString(name)); } } else if (property.kind === SyntaxKind.SpreadAssignment) { if (this.languageVersion < ScriptTarget.ESNext) { this.checkExternalEmitHelpers(property, ExternalEmitHelpers.Rest); } const nonRestNames: PropertyName[] = []; if (allProperties) { for (let i = 0; i < allProperties.length - 1; i++) { nonRestNames.push(allProperties[i].name); } } const type = this.getRestType(objectLiteralType, nonRestNames, objectLiteralType.symbol); return this.checkDestructuringAssignment(property.expression, type); } else { this.error(property, Diagnostics.Property_assignment_expected); } } public checkArrayLiteralAssignment(node: ArrayLiteralExpression, sourceType: Type, checkMode?: CheckMode): Type { if (this.languageVersion < ScriptTarget.ES2015 && this.compilerOptions.downlevelIteration) { this.checkExternalEmitHelpers(node, ExternalEmitHelpers.Read); } // 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 = this.checkIteratedTypeOrElementType(sourceType, node, /*allowStringInput*/ false, /*allowAsyncIterables*/ false) || this.unknownType; const elements = node.elements; for (let i = 0; i < elements.length; i++) { this.checkArrayLiteralDestructuringElementAssignment(node, sourceType, i, elementType, checkMode); } return sourceType; } public checkArrayLiteralDestructuringElementAssignment(node: ArrayLiteralExpression, sourceType: Type, elementIndex: number, elementType: Type, checkMode?: CheckMode) { const elements = node.elements; const element = elements[elementIndex]; if (element.kind !== SyntaxKind.OmittedExpression) { if (element.kind !== SyntaxKind.SpreadElement) { const propName = "" + elementIndex as __String; const type = this.isTypeAny(sourceType) ? sourceType : this.isTupleLikeType(sourceType) ? this.getTypeOfPropertyOfType(sourceType, propName) : elementType; if (type) { return this.checkDestructuringAssignment(element, type, checkMode); } else { // We still need to check element expression here because we may need to set appropriate flag on the expression // such as NodeCheckFlags.LexicalThis on "this"expression. this.checkExpression(element); if (this.isTupleType(sourceType)) { this.error(element, Diagnostics.Tuple_type_0_with_length_1_cannot_be_assigned_to_tuple_with_length_2, this.typeToString(sourceType), this.getTypeReferenceArity((sourceType)), elements.length); } else { this.error(element, Diagnostics.Type_0_has_no_property_1, this.typeToString(sourceType), (propName as string)); } } } else { if (elementIndex < elements.length - 1) { this.error(element, Diagnostics.A_rest_element_must_be_last_in_a_destructuring_pattern); } else { const restExpression = (element).expression; if (restExpression.kind === SyntaxKind.BinaryExpression && (restExpression).operatorToken.kind === SyntaxKind.EqualsToken) { this.error((restExpression).operatorToken, Diagnostics.A_rest_element_cannot_have_an_initializer); } else { return this.checkDestructuringAssignment(restExpression, this.createArrayType(elementType), checkMode); } } } } return undefined; } public checkDestructuringAssignment(exprOrAssignment: Expression | ShorthandPropertyAssignment, sourceType: Type, checkMode?: CheckMode): Type { let target: Expression; if (exprOrAssignment.kind === SyntaxKind.ShorthandPropertyAssignment) { const prop = exprOrAssignment; if (prop.objectAssignmentInitializer) { // In strict null checking mode, if a default value of a non-undefined type is specified, remove // undefined from the final type. if (this.strictNullChecks && !(this.getFalsyFlags(this.checkExpression(prop.objectAssignmentInitializer)) & TypeFlags.Undefined)) { sourceType = this.getTypeWithFacts(sourceType, TypeFacts.NEUndefined); } this.checkBinaryLikeExpression(prop.name, prop.equalsToken, prop.objectAssignmentInitializer, checkMode); } target = (exprOrAssignment).name; } else { target = exprOrAssignment; } if (target.kind === SyntaxKind.BinaryExpression && (target).operatorToken.kind === SyntaxKind.EqualsToken) { this.checkBinaryExpression((target), checkMode); target = (target).left; } if (target.kind === SyntaxKind.ObjectLiteralExpression) { return this.checkObjectLiteralAssignment((target), sourceType); } if (target.kind === SyntaxKind.ArrayLiteralExpression) { return this.checkArrayLiteralAssignment((target), sourceType, checkMode); } return this.checkReferenceAssignment(target, sourceType, checkMode); } public checkReferenceAssignment(target: Expression, sourceType: Type, checkMode?: CheckMode): Type { const targetType = this.checkExpression(target, checkMode); const error = target.parent.kind === SyntaxKind.SpreadAssignment ? Diagnostics.The_target_of_an_object_rest_assignment_must_be_a_variable_or_a_property_access : Diagnostics.The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access; if (this.checkReferenceExpression(target, error)) { this.checkTypeAssignableTo(sourceType, targetType, target, /*headMessage*/ undefined); } return sourceType; } /** * This is a *shallow* check: An expression is side-effect-free if the * evaluation of the expression *itself* cannot produce side effects. * For example, x++ / 3 is side-effect free because the / operator * does not have side effects. * The intent is to "smell test" an expression for correctness in positions where * its value is discarded (e.g. the left side of the comma operator). */ public isSideEffectFree(node: Node): boolean { node = skipParentheses(node); switch (node.kind) { case SyntaxKind.Identifier: case SyntaxKind.StringLiteral: case SyntaxKind.RegularExpressionLiteral: case SyntaxKind.TaggedTemplateExpression: case SyntaxKind.TemplateExpression: case SyntaxKind.NoSubstitutionTemplateLiteral: case SyntaxKind.NumericLiteral: case SyntaxKind.TrueKeyword: case SyntaxKind.FalseKeyword: case SyntaxKind.NullKeyword: case SyntaxKind.UndefinedKeyword: case SyntaxKind.FunctionExpression: case SyntaxKind.ClassExpression: case SyntaxKind.ArrowFunction: case SyntaxKind.ArrayLiteralExpression: case SyntaxKind.ObjectLiteralExpression: case SyntaxKind.TypeOfExpression: case SyntaxKind.NonNullExpression: case SyntaxKind.JsxSelfClosingElement: case SyntaxKind.JsxElement: return true; case SyntaxKind.ConditionalExpression: return this.isSideEffectFree((node as ConditionalExpression).whenTrue) && this.isSideEffectFree((node as ConditionalExpression).whenFalse); case SyntaxKind.BinaryExpression: if (isAssignmentOperator((node as BinaryExpression).operatorToken.kind)) { return false; } return this.isSideEffectFree((node as BinaryExpression).left) && this.isSideEffectFree((node as BinaryExpression).right); case SyntaxKind.PrefixUnaryExpression: case SyntaxKind.PostfixUnaryExpression: // Unary operators ~, !, +, and - have no side effects. // The rest do. switch ((node as PrefixUnaryExpression).operator) { case SyntaxKind.ExclamationToken: case SyntaxKind.PlusToken: case SyntaxKind.MinusToken: case SyntaxKind.TildeToken: return true; } return false; // Some forms listed here for clarity case SyntaxKind.VoidExpression: // Explicit opt-out case SyntaxKind.TypeAssertionExpression: // Not SEF, but can produce useful type warnings case SyntaxKind.AsExpression: // Not SEF, but can produce useful type warnings default: return false; } } public isTypeEqualityComparableTo(source: Type, target: Type) { return (target.flags & TypeFlags.Nullable) !== 0 || this.isTypeComparableTo(source, target); } public getBestChoiceType(type1: Type, type2: Type): Type { const firstAssignableToSecond = this.isTypeAssignableTo(type1, type2); const secondAssignableToFirst = this.isTypeAssignableTo(type2, type1); return secondAssignableToFirst && !firstAssignableToSecond ? type1 : firstAssignableToSecond && !secondAssignableToFirst ? type2 : this.getUnionType([type1, type2], /*subtypeReduction*/ true); } public checkBinaryExpression(node: BinaryExpression, checkMode?: CheckMode) { return this.checkBinaryLikeExpression(node.left, node.operatorToken, node.right, checkMode, node); } public checkBinaryLikeExpression(left: Expression, operatorToken: Node, right: Expression, checkMode?: CheckMode, errorNode?: Node) { const __conv_self__ = this; const operator = operatorToken.kind; if (operator === SyntaxKind.EqualsToken && (left.kind === SyntaxKind.ObjectLiteralExpression || left.kind === SyntaxKind.ArrayLiteralExpression)) { return this.checkDestructuringAssignment(left, this.checkExpression(right, checkMode), checkMode); } let leftType = this.checkExpression(left, checkMode); let rightType = this.checkExpression(right, checkMode); switch (operator) { case SyntaxKind.AsteriskToken: case SyntaxKind.AsteriskAsteriskToken: case SyntaxKind.AsteriskEqualsToken: case SyntaxKind.AsteriskAsteriskEqualsToken: case SyntaxKind.SlashToken: case SyntaxKind.SlashEqualsToken: case SyntaxKind.PercentToken: case SyntaxKind.PercentEqualsToken: case SyntaxKind.MinusToken: case SyntaxKind.MinusEqualsToken: case SyntaxKind.LessThanLessThanToken: case SyntaxKind.LessThanLessThanEqualsToken: case SyntaxKind.GreaterThanGreaterThanToken: case SyntaxKind.GreaterThanGreaterThanEqualsToken: case SyntaxKind.GreaterThanGreaterThanGreaterThanToken: case SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken: case SyntaxKind.BarToken: case SyntaxKind.BarEqualsToken: case SyntaxKind.CaretToken: case SyntaxKind.CaretEqualsToken: case SyntaxKind.AmpersandToken: case SyntaxKind.AmpersandEqualsToken: if (leftType === this.silentNeverType || rightType === this.silentNeverType) { return this.silentNeverType; } leftType = this.checkNonNullType(leftType, left); rightType = this.checkNonNullType(rightType, right); let suggestedOperator: SyntaxKind; // if a user tries to apply a bitwise operator to 2 boolean operands // try and return them a helpful suggestion if ((leftType.flags & TypeFlags.BooleanLike) && (rightType.flags & TypeFlags.BooleanLike) && (suggestedOperator = getSuggestedBooleanOperator(operatorToken.kind)) !== undefined) { this.error(errorNode || operatorToken, Diagnostics.The_0_operator_is_not_allowed_for_boolean_types_Consider_using_1_instead, tokenToString(operatorToken.kind), tokenToString(suggestedOperator)); } else { // otherwise just check each operand separately and report errors as normal const leftOk = this.checkArithmeticOperandType(left, leftType, Diagnostics.The_left_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_or_an_enum_type); const rightOk = this.checkArithmeticOperandType(right, rightType, Diagnostics.The_right_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_or_an_enum_type); if (leftOk && rightOk) { checkAssignmentOperator(this.numberType); } } return this.numberType; case SyntaxKind.PlusToken: case SyntaxKind.PlusEqualsToken: if (leftType === this.silentNeverType || rightType === this.silentNeverType) { return this.silentNeverType; } if (!this.isTypeAssignableToKind(leftType, TypeFlags.StringLike) && !this.isTypeAssignableToKind(rightType, TypeFlags.StringLike)) { leftType = this.checkNonNullType(leftType, left); rightType = this.checkNonNullType(rightType, right); } let resultType: Type; if (this.isTypeAssignableToKind(leftType, TypeFlags.NumberLike, /*strict*/ true) && this.isTypeAssignableToKind(rightType, TypeFlags.NumberLike, /*strict*/ true)) { // Operands of an enum type are treated as having the primitive type Number. // If both operands are of the Number primitive type, the result is of the Number primitive type. resultType = this.numberType; } else if (this.isTypeAssignableToKind(leftType, TypeFlags.StringLike, /*strict*/ true) || this.isTypeAssignableToKind(rightType, TypeFlags.StringLike, /*strict*/ true)) { // If one or both operands are of the String primitive type, the result is of the String primitive type. resultType = this.stringType; } else if (this.isTypeAny(leftType) || this.isTypeAny(rightType)) { // Otherwise, the result is of type Any. // NOTE: unknown type here denotes error type. Old compiler treated this case as any type so do we. resultType = leftType === this.unknownType || rightType === this.unknownType ? this.unknownType : this.anyType; } // Symbols are not allowed at all in arithmetic expressions if (resultType && !checkForDisallowedESSymbolOperand(operator)) { return resultType; } if (!resultType) { reportOperatorError(); return this.anyType; } if (operator === SyntaxKind.PlusEqualsToken) { checkAssignmentOperator(resultType); } return resultType; case SyntaxKind.LessThanToken: case SyntaxKind.GreaterThanToken: case SyntaxKind.LessThanEqualsToken: case SyntaxKind.GreaterThanEqualsToken: if (checkForDisallowedESSymbolOperand(operator)) { leftType = this.getBaseTypeOfLiteralType(this.checkNonNullType(leftType, left)); rightType = this.getBaseTypeOfLiteralType(this.checkNonNullType(rightType, right)); if (!this.isTypeComparableTo(leftType, rightType) && !this.isTypeComparableTo(rightType, leftType)) { reportOperatorError(); } } return this.booleanType; case SyntaxKind.EqualsEqualsToken: case SyntaxKind.ExclamationEqualsToken: case SyntaxKind.EqualsEqualsEqualsToken: case SyntaxKind.ExclamationEqualsEqualsToken: const leftIsLiteral = this.isLiteralType(leftType); const rightIsLiteral = this.isLiteralType(rightType); if (!leftIsLiteral || !rightIsLiteral) { leftType = leftIsLiteral ? this.getBaseTypeOfLiteralType(leftType) : leftType; rightType = rightIsLiteral ? this.getBaseTypeOfLiteralType(rightType) : rightType; } if (!this.isTypeEqualityComparableTo(leftType, rightType) && !this.isTypeEqualityComparableTo(rightType, leftType)) { reportOperatorError(); } return this.booleanType; case SyntaxKind.InstanceOfKeyword: return this.checkInstanceOfExpression(left, right, leftType, rightType); case SyntaxKind.InKeyword: return this.checkInExpression(left, right, leftType, rightType); case SyntaxKind.AmpersandAmpersandToken: return this.getTypeFacts(leftType) & TypeFacts.Truthy ? this.getUnionType([this.extractDefinitelyFalsyTypes(this.strictNullChecks ? leftType : this.getBaseTypeOfLiteralType(rightType)), rightType]) : leftType; case SyntaxKind.BarBarToken: return this.getTypeFacts(leftType) & TypeFacts.Falsy ? this.getBestChoiceType(this.removeDefinitelyFalsyTypes(leftType), rightType) : leftType; case SyntaxKind.EqualsToken: checkAssignmentOperator(rightType); return this.getRegularTypeOfObjectLiteral(rightType); case SyntaxKind.CommaToken: if (!this.compilerOptions.allowUnreachableCode && this.isSideEffectFree(left) && !isEvalNode(right)) { this.error(left, Diagnostics.Left_side_of_comma_operator_is_unused_and_has_no_side_effects); } return rightType; } function isEvalNode(node: Expression) { return node.kind === SyntaxKind.Identifier && (node as Identifier).escapedText === "eval"; } // Return true if there was no error, false if there was an error. function checkForDisallowedESSymbolOperand(operator: SyntaxKind): boolean { const offendingSymbolOperand = __conv_self__.maybeTypeOfKind(leftType, TypeFlags.ESSymbol) ? left : __conv_self__.maybeTypeOfKind(rightType, TypeFlags.ESSymbol) ? right : undefined; if (offendingSymbolOperand) { __conv_self__.error(offendingSymbolOperand, Diagnostics.The_0_operator_cannot_be_applied_to_type_symbol, tokenToString(operator)); return false; } return true; } function getSuggestedBooleanOperator(operator: SyntaxKind): SyntaxKind { switch (operator) { case SyntaxKind.BarToken: case SyntaxKind.BarEqualsToken: return SyntaxKind.BarBarToken; case SyntaxKind.CaretToken: case SyntaxKind.CaretEqualsToken: return SyntaxKind.ExclamationEqualsEqualsToken; case SyntaxKind.AmpersandToken: case SyntaxKind.AmpersandEqualsToken: return SyntaxKind.AmpersandAmpersandToken; default: return undefined; } } function checkAssignmentOperator(valueType: Type): void { if (__conv_self__.produceDiagnostics && isAssignmentOperator(operator)) { // TypeScript 1.0 spec (April 2014): 4.17 // An assignment of the form // VarExpr = ValueExpr // requires VarExpr to be classified as a reference // A compound assignment furthermore requires VarExpr to be classified as a reference (section 4.1) // and the type of the non - compound operation to be assignable to the type of VarExpr. if (__conv_self__.checkReferenceExpression(left, Diagnostics.The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access)) { // to avoid cascading errors check assignability only if 'isReference' check succeeded and no errors were reported __conv_self__.checkTypeAssignableTo(valueType, leftType, left, /*headMessage*/ undefined); } } } function reportOperatorError() { __conv_self__.error(errorNode || operatorToken, Diagnostics.Operator_0_cannot_be_applied_to_types_1_and_2, tokenToString(operatorToken.kind), __conv_self__.typeToString(leftType), __conv_self__.typeToString(rightType)); } } public isYieldExpressionInClass(node: YieldExpression): boolean { let current: Node = node; let parent = node.parent; while (parent) { if (isFunctionLike(parent) && current === (parent).body) { return false; } else if (isClassLike(current)) { return true; } current = parent; parent = parent.parent; } return false; } public checkYieldExpression(node: YieldExpression): Type { // Grammar checking if (this.produceDiagnostics) { if (!(node.flags & NodeFlags.YieldContext) || this.isYieldExpressionInClass(node)) { this.grammarErrorOnFirstToken(node, Diagnostics.A_yield_expression_is_only_allowed_in_a_generator_body); } if (this.isInParameterInitializerBeforeContainingFunction(node)) { this.error(node, Diagnostics.yield_expressions_cannot_be_used_in_a_parameter_initializer); } } if (node.expression) { const func = getContainingFunction(node); // If the user's code is syntactically correct, the func should always have a star. After all, // we are in a yield context. const functionFlags = func && getFunctionFlags(func); if (node.asteriskToken) { // Async generator functions prior to ESNext require the __await, __asyncDelegator, // and __asyncValues helpers if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.AsyncGenerator && this.languageVersion < ScriptTarget.ESNext) { this.checkExternalEmitHelpers(node, ExternalEmitHelpers.AsyncDelegatorIncludes); } // Generator functions prior to ES2015 require the __values helper if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Generator && this.languageVersion < ScriptTarget.ES2015 && this.compilerOptions.downlevelIteration) { this.checkExternalEmitHelpers(node, ExternalEmitHelpers.Values); } } if (functionFlags & FunctionFlags.Generator) { const expressionType = this.checkExpressionCached(node.expression); let expressionElementType: Type; const nodeIsYieldStar = !!node.asteriskToken; if (nodeIsYieldStar) { expressionElementType = this.checkIteratedTypeOrElementType(expressionType, node.expression, /*allowStringInput*/ false, (functionFlags & FunctionFlags.Async) !== 0); } // There is no point in doing an assignability check if the function // has no explicit return type because the return type is directly computed // from the yield expressions. const returnType = getEffectiveReturnTypeNode(func); if (returnType) { const signatureElementType = this.getIteratedTypeOfGenerator(this.getTypeFromTypeNode(returnType), (functionFlags & FunctionFlags.Async) !== 0) || this.anyType; if (nodeIsYieldStar) { this.checkTypeAssignableTo(functionFlags & FunctionFlags.Async ? this.getAwaitedType(expressionElementType, node.expression, Diagnostics.Type_of_iterated_elements_of_a_yield_Asterisk_operand_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member) : expressionElementType, signatureElementType, node.expression, /*headMessage*/ undefined); } else { this.checkTypeAssignableTo(functionFlags & FunctionFlags.Async ? this.getAwaitedType(expressionType, node.expression, Diagnostics.Type_of_yield_operand_in_an_async_generator_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member) : expressionType, signatureElementType, node.expression, /*headMessage*/ undefined); } } } } // Both yield and yield* expressions have type 'any' return this.anyType; } public checkConditionalExpression(node: ConditionalExpression, checkMode?: CheckMode): Type { this.checkExpression(node.condition); const type1 = this.checkExpression(node.whenTrue, checkMode); const type2 = this.checkExpression(node.whenFalse, checkMode); return this.getBestChoiceType(type1, type2); } public checkLiteralExpression(node: LiteralExpression | Token): Type { switch (node.kind) { case SyntaxKind.NoSubstitutionTemplateLiteral: case SyntaxKind.StringLiteral: return this.getFreshTypeOfLiteralType(this.getLiteralType(node.text)); case SyntaxKind.NumericLiteral: this.checkGrammarNumericLiteral((node)); return this.getFreshTypeOfLiteralType(this.getLiteralType(+node.text)); case SyntaxKind.TrueKeyword: return this.trueType; case SyntaxKind.FalseKeyword: return this.falseType; } } public checkTemplateExpression(node: TemplateExpression): Type { const __conv_self__ = this; // We just want to check each expressions, but we are unconcerned with // the type of each expression, as any value may be coerced into a string. // It is worth asking whether this is what we really want though. // A place where we actually *are* concerned with the expressions' types are // in tagged templates. forEach((node).templateSpans, templateSpan => { __conv_self__.checkExpression(templateSpan.expression); }); return this.stringType; } public checkExpressionWithContextualType(node: Expression, contextualType: Type, contextualMapper: TypeMapper): Type { const saveContextualType = node.contextualType; const saveContextualMapper = node.contextualMapper; node.contextualType = contextualType; node.contextualMapper = contextualMapper; const checkMode = contextualMapper === this.identityMapper ? CheckMode.SkipContextSensitive : contextualMapper ? CheckMode.Inferential : CheckMode.Normal; const result = this.checkExpression(node, checkMode); node.contextualType = saveContextualType; node.contextualMapper = saveContextualMapper; return result; } public checkExpressionCached(node: Expression, checkMode?: CheckMode): Type { const links = this.getNodeLinks(node); if (!links.resolvedType) { // When computing a type that we're going to cache, we need to ignore any ongoing control flow // analysis because variables may have transient types in indeterminable states. Moving flowLoopStart // to the top of the stack ensures all transient types are computed from a known point. const saveFlowLoopStart = this.flowLoopStart; this.flowLoopStart = this.flowLoopCount; links.resolvedType = this.checkExpression(node, checkMode); this.flowLoopStart = saveFlowLoopStart; } return links.resolvedType; } public isTypeAssertion(node: Expression) { node = skipParentheses(node); return node.kind === SyntaxKind.TypeAssertionExpression || node.kind === SyntaxKind.AsExpression; } public checkDeclarationInitializer(declaration: VariableLikeDeclaration) { const type = this.getTypeOfExpression(declaration.initializer, /*cache*/ true); return getCombinedNodeFlags(declaration) & NodeFlags.Const || getCombinedModifierFlags(declaration) & ModifierFlags.Readonly && !isParameterPropertyDeclaration(declaration) || this.isTypeAssertion(declaration.initializer) ? type : this.getWidenedLiteralType(type); } public isLiteralContextualType(contextualType: Type) { if (contextualType) { if (contextualType.flags & TypeFlags.TypeVariable) { const constraint = this.getBaseConstraintOfType(contextualType) || this.emptyObjectType; // If the type parameter is constrained to the base primitive type we're checking for, // consider this a literal context. For example, given a type parameter 'T extends string', // this causes us to infer string literal types for T. if (constraint.flags & (TypeFlags.String | TypeFlags.Number | TypeFlags.Boolean | TypeFlags.Enum)) { return true; } contextualType = constraint; } return this.maybeTypeOfKind(contextualType, (TypeFlags.Literal | TypeFlags.Index)); } return false; } public checkExpressionForMutableLocation(node: Expression, checkMode?: CheckMode): Type { const type = this.checkExpression(node, checkMode); return this.isTypeAssertion(node) || this.isLiteralContextualType(this.getContextualType(node)) ? type : this.getWidenedLiteralType(type); } public checkPropertyAssignment(node: PropertyAssignment, checkMode?: CheckMode): Type { // Do not use hasDynamicName here, because that returns false for well known symbols. // We want to perform checkComputedPropertyName for all computed properties, including // well known symbols. if (node.name.kind === SyntaxKind.ComputedPropertyName) { this.checkComputedPropertyName((node.name)); } return this.checkExpressionForMutableLocation((node).initializer, checkMode); } public checkObjectLiteralMethod(node: MethodDeclaration, checkMode?: CheckMode): Type { // Grammar checking this.checkGrammarMethod(node); // Do not use hasDynamicName here, because that returns false for well known symbols. // We want to perform checkComputedPropertyName for all computed properties, including // well known symbols. if (node.name.kind === SyntaxKind.ComputedPropertyName) { this.checkComputedPropertyName((node.name)); } const uninstantiatedType = this.checkFunctionExpressionOrObjectLiteralMethod(node, checkMode); return this.instantiateTypeWithSingleGenericCallSignature(node, uninstantiatedType, checkMode); } public instantiateTypeWithSingleGenericCallSignature(node: Expression | MethodDeclaration, type: Type, checkMode?: CheckMode) { if (checkMode === CheckMode.Inferential) { const signature = this.getSingleCallSignature(type); if (signature && signature.typeParameters) { const contextualType = this.getApparentTypeOfContextualType((node)); if (contextualType) { const contextualSignature = this.getSingleCallSignature(this.getNonNullableType(contextualType)); if (contextualSignature && !contextualSignature.typeParameters) { return this.getOrCreateTypeFromSignature(this.instantiateSignatureInContextOf(signature, contextualSignature, this.getContextualMapper(node))); } } } } return type; } /** * Returns the type of an expression. Unlike checkExpression, this function is simply concerned * with computing the type and may not fully check all contained sub-expressions for errors. * A cache argument of true indicates that if the function performs a full type check, it is ok * to cache the result. */ public getTypeOfExpression(node: Expression, cache?: boolean) { // Optimize for the common case of a call to a function with a single non-generic call // signature where we can just fetch the return type without checking the arguments. if (node.kind === SyntaxKind.CallExpression && (node).expression.kind !== SyntaxKind.SuperKeyword && !isRequireCall(node, /*checkArgumentIsStringLiteral*/ true)) { const funcType = this.checkNonNullExpression((node).expression); const signature = this.getSingleCallSignature(funcType); if (signature && !signature.typeParameters) { return this.getReturnTypeOfSignature(signature); } } // Otherwise simply call checkExpression. Ideally, the entire family of checkXXX functions // should have a parameter that indicates whether full error checking is required such that // we can perform the optimizations locally. return cache ? this.checkExpressionCached(node) : this.checkExpression(node); } /** * Returns the type of an expression. Unlike checkExpression, this function is simply concerned * with computing the type and may not fully check all contained sub-expressions for errors. * It is intended for uses where you know there is no contextual type, * and requesting the contextual type might cause a circularity or other bad behaviour. * It sets the contextual type of the node to any before calling getTypeOfExpression. */ public getContextFreeTypeOfExpression(node: Expression) { const saveContextualType = node.contextualType; node.contextualType = this.anyType; const type = this.getTypeOfExpression(node); node.contextualType = saveContextualType; return type; } // Checks an expression and returns its type. The contextualMapper parameter serves two purposes: When // contextualMapper is not undefined and not equal to the identityMapper function object it indicates that the // expression is being inferentially typed (section 4.15.2 in spec) and provides the type mapper to use in // conjunction with the generic contextual type. When contextualMapper is equal to the identityMapper function // object, it serves as an indicator that all contained function and arrow expressions should be considered to // have the wildcard function type; this form of type check is used during overload resolution to exclude // contextually typed function and arrow expressions in the initial phase. public checkExpression(node: Expression | QualifiedName, checkMode?: CheckMode): Type { let type: Type; if (node.kind === SyntaxKind.QualifiedName) { type = this.checkQualifiedName((node)); } else { const uninstantiatedType = this.checkExpressionWorker((node), checkMode); type = this.instantiateTypeWithSingleGenericCallSignature((node), uninstantiatedType, checkMode); } if (this.isConstEnumObjectType(type)) { // enum object type for const enums are only permitted in: // - 'left' in property access // - 'object' in indexed access // - target in rhs of import statement const ok = (node.parent.kind === SyntaxKind.PropertyAccessExpression && (node.parent).expression === node) || (node.parent.kind === SyntaxKind.ElementAccessExpression && (node.parent).expression === node) || ((node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.QualifiedName) && this.isInRightSideOfImportOrExportAssignment((node))); if (!ok) { this.error(node, Diagnostics.const_enums_can_only_be_used_in_property_or_index_access_expressions_or_the_right_hand_side_of_an_import_declaration_or_export_assignment); } } return type; } public checkParenthesizedExpression(node: ParenthesizedExpression, checkMode?: CheckMode): Type { if (isInJavaScriptFile(node) && node.jsDoc) { const typecasts = flatMap(node.jsDoc, doc => filter(doc.tags, tag => tag.kind === SyntaxKind.JSDocTypeTag)); if (typecasts && typecasts.length) { // We should have already issued an error if there were multiple type jsdocs const cast = typecasts[0] as JSDocTypeTag; return this.checkAssertionWorker(cast, cast.typeExpression.type, node.expression, checkMode); } } return this.checkExpression(node.expression, checkMode); } public checkExpressionWorker(node: Expression, checkMode: CheckMode): Type { switch (node.kind) { case SyntaxKind.Identifier: return this.checkIdentifier((node)); case SyntaxKind.ThisKeyword: return this.checkThisExpression(node); case SyntaxKind.SuperKeyword: return this.checkSuperExpression(node); case SyntaxKind.NullKeyword: return this.nullWideningType; case SyntaxKind.NoSubstitutionTemplateLiteral: case SyntaxKind.StringLiteral: case SyntaxKind.NumericLiteral: case SyntaxKind.TrueKeyword: case SyntaxKind.FalseKeyword: return this.checkLiteralExpression((node as LiteralExpression)); case SyntaxKind.TemplateExpression: return this.checkTemplateExpression((node)); case SyntaxKind.RegularExpressionLiteral: return this.globalRegExpType; case SyntaxKind.ArrayLiteralExpression: return this.checkArrayLiteral((node), checkMode); case SyntaxKind.ObjectLiteralExpression: return this.checkObjectLiteral((node), checkMode); case SyntaxKind.PropertyAccessExpression: return this.checkPropertyAccessExpression((node)); case SyntaxKind.ElementAccessExpression: return this.checkIndexedAccess((node)); case SyntaxKind.CallExpression: if ((node).expression.kind === SyntaxKind.ImportKeyword) { return this.checkImportCallExpression((node)); } /* falls through */ case SyntaxKind.NewExpression: return this.checkCallExpression((node)); case SyntaxKind.TaggedTemplateExpression: return this.checkTaggedTemplateExpression((node)); case SyntaxKind.ParenthesizedExpression: return this.checkParenthesizedExpression((node), checkMode); case SyntaxKind.ClassExpression: return this.checkClassExpression((node)); case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: return this.checkFunctionExpressionOrObjectLiteralMethod((node), checkMode); case SyntaxKind.TypeOfExpression: return this.checkTypeOfExpression((node)); case SyntaxKind.TypeAssertionExpression: case SyntaxKind.AsExpression: return this.checkAssertion((node)); case SyntaxKind.NonNullExpression: return this.checkNonNullAssertion((node)); case SyntaxKind.MetaProperty: return this.checkMetaProperty((node)); case SyntaxKind.DeleteExpression: return this.checkDeleteExpression((node)); case SyntaxKind.VoidExpression: return this.checkVoidExpression((node)); case SyntaxKind.AwaitExpression: return this.checkAwaitExpression((node)); case SyntaxKind.PrefixUnaryExpression: return this.checkPrefixUnaryExpression((node)); case SyntaxKind.PostfixUnaryExpression: return this.checkPostfixUnaryExpression((node)); case SyntaxKind.BinaryExpression: return this.checkBinaryExpression((node), checkMode); case SyntaxKind.ConditionalExpression: return this.checkConditionalExpression((node), checkMode); case SyntaxKind.SpreadElement: return this.checkSpreadExpression((node), checkMode); case SyntaxKind.OmittedExpression: return this.undefinedWideningType; case SyntaxKind.YieldExpression: return this.checkYieldExpression((node)); case SyntaxKind.JsxExpression: return this.checkJsxExpression((node), checkMode); case SyntaxKind.JsxElement: return this.checkJsxElement((node)); case SyntaxKind.JsxSelfClosingElement: return this.checkJsxSelfClosingElement((node)); case SyntaxKind.JsxAttributes: return this.checkJsxAttributes((node), checkMode); case SyntaxKind.JsxOpeningElement: Debug.fail("Shouldn't ever directly check a JsxOpeningElement"); } return this.unknownType; } // DECLARATION AND STATEMENT TYPE CHECKING public checkTypeParameter(node: TypeParameterDeclaration) { // Grammar Checking if (node.expression) { this.grammarErrorOnFirstToken(node.expression, Diagnostics.Type_expected); } this.checkSourceElement(node.constraint); this.checkSourceElement(node.default); const typeParameter = this.getDeclaredTypeOfTypeParameter(this.getSymbolOfNode(node)); if (!this.hasNonCircularBaseConstraint(typeParameter)) { this.error(node.constraint, Diagnostics.Type_parameter_0_has_a_circular_constraint, this.typeToString(typeParameter)); } const constraintType = this.getConstraintOfTypeParameter(typeParameter); const defaultType = this.getDefaultFromTypeParameter(typeParameter); if (constraintType && defaultType) { this.checkTypeAssignableTo(defaultType, this.getTypeWithThisArgument(constraintType, defaultType), node.default, Diagnostics.Type_0_does_not_satisfy_the_constraint_1); } if (this.produceDiagnostics) { this.checkTypeNameIsReserved(node.name, Diagnostics.Type_parameter_name_cannot_be_0); } } public checkParameter(node: ParameterDeclaration) { // Grammar checking // It is a SyntaxError if the Identifier "eval" or the Identifier "arguments" occurs as the // Identifier in a PropertySetParameterList of a PropertyAssignment that is contained in strict code // or if its FunctionBody is strict code(11.1.5). // Grammar checking this.checkGrammarDecorators(node) || this.checkGrammarModifiers(node); this.checkVariableLikeDeclaration(node); let func = getContainingFunction(node); if (hasModifier(node, ModifierFlags.ParameterPropertyModifier)) { func = getContainingFunction(node); if (!(func.kind === SyntaxKind.Constructor && nodeIsPresent(func.body))) { this.error(node, Diagnostics.A_parameter_property_is_only_allowed_in_a_constructor_implementation); } } if (node.questionToken && isBindingPattern(node.name) && (func as FunctionLikeDeclaration).body) { this.error(node, Diagnostics.A_binding_pattern_parameter_cannot_be_optional_in_an_implementation_signature); } if (node.name && isIdentifier(node.name) && (node.name.escapedText === "this" || node.name.escapedText === "new")) { if (indexOf(func.parameters, node) !== 0) { this.error(node, Diagnostics.A_0_parameter_must_be_the_first_parameter, (node.name.escapedText as string)); } if (func.kind === SyntaxKind.Constructor || func.kind === SyntaxKind.ConstructSignature || func.kind === SyntaxKind.ConstructorType) { this.error(node, Diagnostics.A_constructor_cannot_have_a_this_parameter); } } // Only check rest parameter type if it's not a binding pattern. Since binding patterns are // not allowed in a rest parameter, we already have an error from checkGrammarParameterList. if (node.dotDotDotToken && !isBindingPattern(node.name) && !this.isArrayType(this.getTypeOfSymbol(node.symbol))) { this.error(node, Diagnostics.A_rest_parameter_must_be_of_an_array_type); } } public getTypePredicateParameterIndex(parameterList: NodeArray, parameter: Identifier): number { if (parameterList) { for (let i = 0; i < parameterList.length; i++) { const param = parameterList[i]; if (param.name.kind === SyntaxKind.Identifier && param.name.escapedText === parameter.escapedText) { return i; } } } return -1; } public checkTypePredicate(node: TypePredicateNode): void { const parent = this.getTypePredicateParent(node); if (!parent) { // The parent must not be valid. this.error(node, Diagnostics.A_type_predicate_is_only_allowed_in_return_type_position_for_functions_and_methods); return; } const typePredicate = this.getSignatureFromDeclaration(parent).typePredicate; if (!typePredicate) { return; } this.checkSourceElement(node.type); const { parameterName } = node; if (isThisTypePredicate(typePredicate)) { this.getTypeFromThisTypeNode((parameterName as ThisTypeNode)); } else { if (typePredicate.parameterIndex >= 0) { if (parent.parameters[typePredicate.parameterIndex].dotDotDotToken) { this.error(parameterName, Diagnostics.A_type_predicate_cannot_reference_a_rest_parameter); } else { const leadingError = chainDiagnosticMessages(/*details*/ undefined, Diagnostics.A_type_predicate_s_type_must_be_assignable_to_its_parameter_s_type); this.checkTypeAssignableTo(typePredicate.type, this.getTypeOfNode(parent.parameters[typePredicate.parameterIndex]), node.type, /*headMessage*/ undefined, leadingError); } } else if (parameterName) { let hasReportedError = false; for (const { name } of parent.parameters) { if (isBindingPattern(name) && this.checkIfTypePredicateVariableIsDeclaredInBindingPattern(name, parameterName, typePredicate.parameterName)) { hasReportedError = true; break; } } if (!hasReportedError) { this.error(node.parameterName, Diagnostics.Cannot_find_parameter_0, typePredicate.parameterName); } } } } public getTypePredicateParent(node: Node): SignatureDeclaration { switch (node.parent.kind) { case SyntaxKind.ArrowFunction: case SyntaxKind.CallSignature: case SyntaxKind.FunctionDeclaration: case SyntaxKind.FunctionExpression: case SyntaxKind.FunctionType: case SyntaxKind.MethodDeclaration: case SyntaxKind.MethodSignature: const parent = node.parent; if (node === parent.type) { return parent; } } } public checkIfTypePredicateVariableIsDeclaredInBindingPattern(pattern: BindingPattern, predicateVariableNode: Node, predicateVariableName: string) { for (const element of pattern.elements) { if (isOmittedExpression(element)) { continue; } const name = element.name; if (name.kind === SyntaxKind.Identifier && name.escapedText === predicateVariableName) { this.error(predicateVariableNode, Diagnostics.A_type_predicate_cannot_reference_element_0_in_a_binding_pattern, predicateVariableName); return true; } else if (name.kind === SyntaxKind.ArrayBindingPattern || name.kind === SyntaxKind.ObjectBindingPattern) { if (this.checkIfTypePredicateVariableIsDeclaredInBindingPattern(name, predicateVariableNode, predicateVariableName)) { return true; } } } } public checkSignatureDeclaration(node: SignatureDeclaration) { // Grammar checking if (node.kind === SyntaxKind.IndexSignature) { this.checkGrammarIndexSignature((node)); } else if (node.kind === SyntaxKind.FunctionType || node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.ConstructorType || node.kind === SyntaxKind.CallSignature || node.kind === SyntaxKind.Constructor || node.kind === SyntaxKind.ConstructSignature) { this.checkGrammarFunctionLikeDeclaration((node)); } const functionFlags = getFunctionFlags(node); if (!(functionFlags & FunctionFlags.Invalid)) { // Async generators prior to ESNext require the __await and __asyncGenerator helpers if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.AsyncGenerator && this.languageVersion < ScriptTarget.ESNext) { this.checkExternalEmitHelpers(node, ExternalEmitHelpers.AsyncGeneratorIncludes); } // Async functions prior to ES2017 require the __awaiter helper if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Async && this.languageVersion < ScriptTarget.ES2017) { this.checkExternalEmitHelpers(node, ExternalEmitHelpers.Awaiter); } // Generator functions, Async functions, and Async Generator functions prior to // ES2015 require the __generator helper if ((functionFlags & FunctionFlags.AsyncGenerator) !== FunctionFlags.Normal && this.languageVersion < ScriptTarget.ES2015) { this.checkExternalEmitHelpers(node, ExternalEmitHelpers.Generator); } } this.checkTypeParameters(node.typeParameters); forEach(node.parameters, this.checkParameter); // TODO(rbuckton): Should we start checking JSDoc types? if (node.type) { this.checkSourceElement(node.type); } if (this.produceDiagnostics) { this.checkCollisionWithArgumentsInGeneratedCode(node); const returnTypeNode = getEffectiveReturnTypeNode(node); if (this.noImplicitAny && !returnTypeNode) { switch (node.kind) { case SyntaxKind.ConstructSignature: this.error(node, Diagnostics.Construct_signature_which_lacks_return_type_annotation_implicitly_has_an_any_return_type); break; case SyntaxKind.CallSignature: this.error(node, Diagnostics.Call_signature_which_lacks_return_type_annotation_implicitly_has_an_any_return_type); break; } } if (returnTypeNode) { const functionFlags = getFunctionFlags(node); if ((functionFlags & (FunctionFlags.Invalid | FunctionFlags.Generator)) === FunctionFlags.Generator) { const returnType = this.getTypeFromTypeNode(returnTypeNode); if (returnType === this.voidType) { this.error(returnTypeNode, Diagnostics.A_generator_cannot_have_a_void_type_annotation); } else { const generatorElementType = this.getIteratedTypeOfGenerator(returnType, (functionFlags & FunctionFlags.Async) !== 0) || this.anyType; const iterableIteratorInstantiation = functionFlags & FunctionFlags.Async ? this.createAsyncIterableIteratorType(generatorElementType) // AsyncGenerator function : this.createIterableIteratorType(generatorElementType); // Generator function // Naively, one could check that IterableIterator is assignable to the return type annotation. // However, that would not catch the error in the following case. // // interface BadGenerator extends Iterable, Iterator { } // function* g(): BadGenerator { } // Iterable and Iterator have different types! // this.checkTypeAssignableTo(iterableIteratorInstantiation, returnType, returnTypeNode); } } else if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Async) { this.checkAsyncFunctionReturnType((node)); } } if (this.noUnusedIdentifiers && !(node).body) { this.checkUnusedTypeParameters(node); } } } public checkClassForDuplicateDeclarations(node: ClassLikeDeclaration) { const __conv_self__ = this; const enum Declaration { Getter = 1, Setter = 2, Method = 4, Property = Getter | Setter } const instanceNames = createUnderscoreEscapedMap(); const staticNames = createUnderscoreEscapedMap(); for (const member of node.members) { if (member.kind === SyntaxKind.Constructor) { for (const param of (member as ConstructorDeclaration).parameters) { if (isParameterPropertyDeclaration(param) && !isBindingPattern(param.name)) { addName(instanceNames, param.name, param.name.escapedText, Declaration.Property); } } } else { const isStatic = hasModifier(member, ModifierFlags.Static); const names = isStatic ? staticNames : instanceNames; const memberName = member.name && getPropertyNameForPropertyNameNode(member.name); if (memberName) { switch (member.kind) { case SyntaxKind.GetAccessor: addName(names, member.name, memberName, Declaration.Getter); break; case SyntaxKind.SetAccessor: addName(names, member.name, memberName, Declaration.Setter); break; case SyntaxKind.PropertyDeclaration: addName(names, member.name, memberName, Declaration.Property); break; case SyntaxKind.MethodDeclaration: addName(names, member.name, memberName, Declaration.Method); break; } } } } function addName(names: UnderscoreEscapedMap, location: Node, name: __String, meaning: Declaration) { const prev = names.get(name); if (prev) { if (prev & Declaration.Method) { if (meaning !== Declaration.Method) { __conv_self__.error(location, Diagnostics.Duplicate_identifier_0, getTextOfNode(location)); } } else if (prev & meaning) { __conv_self__.error(location, Diagnostics.Duplicate_identifier_0, getTextOfNode(location)); } else { names.set(name, prev | meaning); } } else { names.set(name, meaning); } } } /** * Static members being set on a constructor function may conflict with built-in properties * of Function. Esp. in ECMAScript 5 there are non-configurable and non-writable * built-in properties. This check issues a transpile error when a class has a static * member with the same name as a non-writable built-in property. * * @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.3.3 * @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.3.5 * @see http://www.ecma-international.org/ecma-262/6.0/#sec-properties-of-the-function-constructor * @see http://www.ecma-international.org/ecma-262/6.0/#sec-function-instances */ public checkClassForStaticPropertyNameConflicts(node: ClassLikeDeclaration) { for (const member of node.members) { const memberNameNode = member.name; const isStatic = hasModifier(member, ModifierFlags.Static); if (isStatic && memberNameNode) { const memberName = getPropertyNameForPropertyNameNode(memberNameNode); switch (memberName) { case "name": case "length": case "caller": case "arguments": case "prototype": const message = Diagnostics.Static_property_0_conflicts_with_built_in_property_Function_0_of_constructor_function_1; const className = this.getNameOfSymbol(this.getSymbolOfNode(node)); this.error(memberNameNode, message, memberName, className); break; } } } } public checkObjectTypeForDuplicateDeclarations(node: TypeLiteralNode | InterfaceDeclaration) { const names = createMap(); for (const member of node.members) { if (member.kind === SyntaxKind.PropertySignature) { let memberName: string; switch (member.name.kind) { case SyntaxKind.StringLiteral: case SyntaxKind.NumericLiteral: memberName = member.name.text; break; case SyntaxKind.Identifier: memberName = unescapeLeadingUnderscores(member.name.escapedText); break; default: continue; } if (names.get(memberName)) { this.error(getNameOfDeclaration(member.symbol.valueDeclaration), Diagnostics.Duplicate_identifier_0, memberName); this.error(member.name, Diagnostics.Duplicate_identifier_0, memberName); } else { names.set(memberName, true); } } } } public checkTypeForDuplicateIndexSignatures(node: Node) { if (node.kind === SyntaxKind.InterfaceDeclaration) { const nodeSymbol = this.getSymbolOfNode(node); // in case of merging interface declaration it is possible that we'll enter this check procedure several times for every declaration // to prevent this run check only for the first declaration of a given kind if (nodeSymbol.declarations.length > 0 && nodeSymbol.declarations[0] !== node) { return; } } // TypeScript 1.0 spec (April 2014) // 3.7.4: An object type can contain at most one string index signature and one numeric index signature. // 8.5: A class declaration can have at most one string index member declaration and one numeric index member declaration const indexSymbol = this.getIndexSymbol(this.getSymbolOfNode(node)); if (indexSymbol) { let seenNumericIndexer = false; let seenStringIndexer = false; for (const decl of indexSymbol.declarations) { const declaration = decl; if (declaration.parameters.length === 1 && declaration.parameters[0].type) { switch (declaration.parameters[0].type.kind) { case SyntaxKind.StringKeyword: if (!seenStringIndexer) { seenStringIndexer = true; } else { this.error(declaration, Diagnostics.Duplicate_string_index_signature); } break; case SyntaxKind.NumberKeyword: if (!seenNumericIndexer) { seenNumericIndexer = true; } else { this.error(declaration, Diagnostics.Duplicate_number_index_signature); } break; } } } } } public checkPropertyDeclaration(node: PropertyDeclaration) { // Grammar checking this.checkGrammarDecorators(node) || this.checkGrammarModifiers(node) || this.checkGrammarProperty(node) || this.checkGrammarComputedPropertyName(node.name); this.checkVariableLikeDeclaration(node); } public checkMethodDeclaration(node: MethodDeclaration) { // Grammar checking this.checkGrammarMethod(node) || this.checkGrammarComputedPropertyName(node.name); // Grammar checking for modifiers is done inside the function checkGrammarFunctionLikeDeclaration this.checkFunctionOrMethodDeclaration(node); // Abstract methods cannot have an implementation. // Extra checks are to avoid reporting multiple errors relating to the "abstractness" of the node. if (hasModifier(node, ModifierFlags.Abstract) && node.body) { this.error(node, Diagnostics.Method_0_cannot_have_an_implementation_because_it_is_marked_abstract, declarationNameToString(node.name)); } } public checkConstructorDeclaration(node: ConstructorDeclaration) { // Grammar check on signature of constructor and modifier of the constructor is done in checkSignatureDeclaration function. this.checkSignatureDeclaration(node); // Grammar check for checking only related to constructorDeclaration this.checkGrammarConstructorTypeParameters(node) || this.checkGrammarConstructorTypeAnnotation(node); this.checkSourceElement(node.body); this.registerForUnusedIdentifiersCheck(node); const symbol = this.getSymbolOfNode(node); const firstDeclaration = getDeclarationOfKind(symbol, node.kind); // Only type check the symbol once if (node === firstDeclaration) { this.checkFunctionOrConstructorSymbol(symbol); } // exit early in the case of signature - super checks are not relevant to them if (nodeIsMissing(node.body)) { return; } if (!this.produceDiagnostics) { return; } function containsSuperCallAsComputedPropertyName(n: Declaration): boolean { const name = getNameOfDeclaration(n); return name && containsSuperCall(name); } function containsSuperCall(n: Node): boolean { if (isSuperCall(n)) { return true; } else if (isFunctionLike(n)) { return false; } else if (isClassLike(n)) { return forEach((n).members, containsSuperCallAsComputedPropertyName); } return forEachChild(n, containsSuperCall); } function isInstancePropertyWithInitializer(n: Node): boolean { return n.kind === SyntaxKind.PropertyDeclaration && !hasModifier(n, ModifierFlags.Static) && !!(n).initializer; } // TS 1.0 spec (April 2014): 8.3.2 // Constructors of classes with no extends clause may not contain super calls, whereas // constructors of derived classes must contain at least one super call somewhere in their function body. const containingClassDecl = node.parent; if (getClassExtendsHeritageClauseElement(containingClassDecl)) { this.captureLexicalThis(node.parent, containingClassDecl); const classExtendsNull = this.classDeclarationExtendsNull(containingClassDecl); const superCall = this.getSuperCallInConstructor(node); if (superCall) { if (classExtendsNull) { this.error(superCall, Diagnostics.A_constructor_cannot_contain_a_super_call_when_its_class_extends_null); } // The first statement in the body of a constructor (excluding prologue directives) must be a super call // if both of the following are true: // - The containing class is a derived class. // - The constructor declares parameter properties // or the containing class declares instance member variables with initializers. const superCallShouldBeFirst = some((node.parent).members, isInstancePropertyWithInitializer) || some(node.parameters, p => hasModifier(p, ModifierFlags.ParameterPropertyModifier)); // Skip past any prologue directives to find the first statement // to ensure that it was a super call. if (superCallShouldBeFirst) { const statements = (node.body).statements; let superCallStatement: ExpressionStatement; for (const statement of statements) { if (statement.kind === SyntaxKind.ExpressionStatement && isSuperCall((statement).expression)) { superCallStatement = statement; break; } if (!isPrologueDirective(statement)) { break; } } if (!superCallStatement) { this.error(node, Diagnostics.A_super_call_must_be_the_first_statement_in_the_constructor_when_a_class_contains_initialized_properties_or_has_parameter_properties); } } } else if (!classExtendsNull) { this.error(node, Diagnostics.Constructors_for_derived_classes_must_contain_a_super_call); } } } public checkAccessorDeclaration(node: AccessorDeclaration) { if (this.produceDiagnostics) { // Grammar checking accessors this.checkGrammarFunctionLikeDeclaration(node) || this.checkGrammarAccessor(node) || this.checkGrammarComputedPropertyName(node.name); this.checkDecorators(node); this.checkSignatureDeclaration(node); if (node.kind === SyntaxKind.GetAccessor) { if (!isInAmbientContext(node) && nodeIsPresent(node.body) && (node.flags & NodeFlags.HasImplicitReturn)) { if (!(node.flags & NodeFlags.HasExplicitReturn)) { this.error(node.name, Diagnostics.A_get_accessor_must_return_a_value); } } } // Do not use hasDynamicName here, because that returns false for well known symbols. // We want to perform checkComputedPropertyName for all computed properties, including // well known symbols. if (node.name.kind === SyntaxKind.ComputedPropertyName) { this.checkComputedPropertyName((node.name)); } if (!hasDynamicName(node)) { // TypeScript 1.0 spec (April 2014): 8.4.3 // Accessors for the same member name must specify the same accessibility. const otherKind = node.kind === SyntaxKind.GetAccessor ? SyntaxKind.SetAccessor : SyntaxKind.GetAccessor; const otherAccessor = getDeclarationOfKind(node.symbol, otherKind); if (otherAccessor) { const nodeFlags = getModifierFlags(node); const otherFlags = getModifierFlags(otherAccessor); if ((nodeFlags & ModifierFlags.AccessibilityModifier) !== (otherFlags & ModifierFlags.AccessibilityModifier)) { this.error(node.name, Diagnostics.Getter_and_setter_accessors_do_not_agree_in_visibility); } if ((nodeFlags & ModifierFlags.Abstract) !== (otherFlags & ModifierFlags.Abstract)) { this.error(node.name, Diagnostics.Accessors_must_both_be_abstract_or_non_abstract); } // TypeScript 1.0 spec (April 2014): 4.5 // If both accessors include type annotations, the specified types must be identical. this.checkAccessorDeclarationTypesIdentical(node, otherAccessor, this.getAnnotatedAccessorType, Diagnostics.get_and_set_accessor_must_have_the_same_type); this.checkAccessorDeclarationTypesIdentical(node, otherAccessor, this.getThisTypeOfDeclaration, Diagnostics.get_and_set_accessor_must_have_the_same_this_type); } } const returnType = this.getTypeOfAccessors(this.getSymbolOfNode(node)); if (node.kind === SyntaxKind.GetAccessor) { this.checkAllCodePathsInNonVoidFunctionReturnOrThrow(node, returnType); } } this.checkSourceElement(node.body); this.registerForUnusedIdentifiersCheck(node); } public checkAccessorDeclarationTypesIdentical(first: AccessorDeclaration, second: AccessorDeclaration, getAnnotatedType: (a: AccessorDeclaration) => Type, message: DiagnosticMessage) { const firstType = getAnnotatedType(first); const secondType = getAnnotatedType(second); if (firstType && secondType && !this.isTypeIdenticalTo(firstType, secondType)) { this.error(first, message); } } public checkMissingDeclaration(node: Node) { this.checkDecorators(node); } public checkTypeArgumentConstraints(typeParameters: TypeParameter[], typeArgumentNodes: ReadonlyArray): boolean { const minTypeArgumentCount = this.getMinTypeArgumentCount(typeParameters); let typeArguments: Type[]; let mapper: TypeMapper; let result = true; for (let i = 0; i < typeParameters.length; i++) { const constraint = this.getConstraintOfTypeParameter(typeParameters[i]); if (constraint) { if (!typeArguments) { typeArguments = this.fillMissingTypeArguments(map(typeArgumentNodes, this.getTypeFromTypeNode), typeParameters, minTypeArgumentCount); mapper = this.createTypeMapper(typeParameters, typeArguments); } const typeArgument = typeArguments[i]; result = result && this.checkTypeAssignableTo(typeArgument, this.getTypeWithThisArgument(this.instantiateType(constraint, mapper), typeArgument), typeArgumentNodes[i], Diagnostics.Type_0_does_not_satisfy_the_constraint_1); } } return result; } public checkTypeReferenceNode(node: TypeReferenceNode | ExpressionWithTypeArguments) { this.checkGrammarTypeArguments(node, node.typeArguments); if (node.kind === SyntaxKind.TypeReference && node.typeName.jsdocDotPos !== undefined && !isInJavaScriptFile(node) && !isInJSDoc(node)) { this.grammarErrorAtPos(getSourceFileOfNode(node), node.typeName.jsdocDotPos, 1, Diagnostics.JSDoc_types_can_only_be_used_inside_documentation_comments); } const type = this.getTypeFromTypeReference(node); if (type !== this.unknownType) { if (node.typeArguments) { // Do type argument local checks only if referenced type is successfully resolved forEach(node.typeArguments, this.checkSourceElement); if (this.produceDiagnostics) { const symbol = this.getNodeLinks(node).resolvedSymbol; if (!symbol) { // There is no resolved symbol cached if the type resolved to a builtin // via JSDoc type reference resolution (eg, Boolean became boolean), none // of which are generic when they have no associated symbol this.error(node, Diagnostics.Type_0_is_not_generic, this.typeToString(type)); return; } let typeParameters = symbol.flags & SymbolFlags.TypeAlias && this.getSymbolLinks(symbol).typeParameters; if (!typeParameters && this.getObjectFlags(type) & ObjectFlags.Reference) { typeParameters = (type).target.localTypeParameters; } this.checkTypeArgumentConstraints(typeParameters, node.typeArguments); } } if (type.flags & TypeFlags.Enum && this.getNodeLinks(node).resolvedSymbol.flags & SymbolFlags.EnumMember) { this.error(node, Diagnostics.Enum_type_0_has_members_with_initializers_that_are_not_literals, this.typeToString(type)); } } } public checkTypeQuery(node: TypeQueryNode) { this.getTypeFromTypeQueryNode(node); } public checkTypeLiteral(node: TypeLiteralNode) { forEach(node.members, this.checkSourceElement); if (this.produceDiagnostics) { const type = this.getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node); this.checkIndexConstraints(type); this.checkTypeForDuplicateIndexSignatures(node); this.checkObjectTypeForDuplicateDeclarations(node); } } public checkArrayType(node: ArrayTypeNode) { this.checkSourceElement(node.elementType); } public checkTupleType(node: TupleTypeNode) { // Grammar checking const hasErrorFromDisallowedTrailingComma = this.checkGrammarForDisallowedTrailingComma(node.elementTypes); if (!hasErrorFromDisallowedTrailingComma && node.elementTypes.length === 0) { this.grammarErrorOnNode(node, Diagnostics.A_tuple_type_element_list_cannot_be_empty); } forEach(node.elementTypes, this.checkSourceElement); } public checkUnionOrIntersectionType(node: UnionOrIntersectionTypeNode) { forEach(node.types, this.checkSourceElement); } public checkIndexedAccessIndexType(type: Type, accessNode: ElementAccessExpression | IndexedAccessTypeNode) { if (!(type.flags & TypeFlags.IndexedAccess)) { return type; } // Check if the index type is assignable to 'keyof T' for the object type. const objectType = (type).objectType; const indexType = (type).indexType; if (this.isTypeAssignableTo(indexType, this.getIndexType(objectType))) { return type; } // Check if we're indexing with a numeric type and the object type is a generic // type with a constraint that has a numeric index signature. if (this.maybeTypeOfKind(objectType, TypeFlags.TypeVariable) && this.isTypeAssignableToKind(indexType, TypeFlags.NumberLike)) { const constraint = this.getBaseConstraintOfType(objectType); if (constraint && this.getIndexInfoOfType(constraint, IndexKind.Number)) { return type; } } this.error(accessNode, Diagnostics.Type_0_cannot_be_used_to_index_type_1, this.typeToString(indexType), this.typeToString(objectType)); return type; } public checkIndexedAccessType(node: IndexedAccessTypeNode) { this.checkSourceElement(node.objectType); this.checkSourceElement(node.indexType); this.checkIndexedAccessIndexType(this.getTypeFromIndexedAccessTypeNode(node), node); } public checkMappedType(node: MappedTypeNode) { this.checkSourceElement(node.typeParameter); this.checkSourceElement(node.type); const type = (this.getTypeFromMappedTypeNode(node)); const constraintType = this.getConstraintTypeFromMappedType(type); this.checkTypeAssignableTo(constraintType, this.stringType, node.typeParameter.constraint); } public isPrivateWithinAmbient(node: Node): boolean { return hasModifier(node, ModifierFlags.Private) && isInAmbientContext(node); } public getEffectiveDeclarationFlags(n: Node, flagsToCheck: ModifierFlags): ModifierFlags { let flags = getCombinedModifierFlags(n); // children of classes (even ambient classes) should not be marked as ambient or export // because those flags have no useful semantics there. if (n.parent.kind !== SyntaxKind.InterfaceDeclaration && n.parent.kind !== SyntaxKind.ClassDeclaration && n.parent.kind !== SyntaxKind.ClassExpression && isInAmbientContext(n)) { if (!(flags & ModifierFlags.Ambient)) { // It is nested in an ambient context, which means it is automatically exported flags |= ModifierFlags.Export; } flags |= ModifierFlags.Ambient; } return flags & flagsToCheck; } public checkFunctionOrConstructorSymbol(symbol: Symbol): void { const __conv_self__ = this; if (!this.produceDiagnostics) { return; } function getCanonicalOverload(overloads: Declaration[], implementation: FunctionLikeDeclaration) { // Consider the canonical set of flags to be the flags of the bodyDeclaration or the first declaration // Error on all deviations from this canonical set of flags // The caveat is that if some overloads are defined in lib.d.ts, we don't want to // report the errors on those. To achieve this, we will say that the implementation is // the canonical signature only if it is in the same container as the first overload const implementationSharesContainerWithFirstOverload = implementation !== undefined && implementation.parent === overloads[0].parent; return implementationSharesContainerWithFirstOverload ? implementation : overloads[0]; } function checkFlagAgreementBetweenOverloads(overloads: Declaration[], implementation: FunctionLikeDeclaration, flagsToCheck: ModifierFlags, someOverloadFlags: ModifierFlags, allOverloadFlags: ModifierFlags): void { // Error if some overloads have a flag that is not shared by all overloads. To find the // deviations, we XOR someOverloadFlags with allOverloadFlags const someButNotAllOverloadFlags = someOverloadFlags ^ allOverloadFlags; if (someButNotAllOverloadFlags !== 0) { const canonicalFlags = __conv_self__.getEffectiveDeclarationFlags(getCanonicalOverload(overloads, implementation), flagsToCheck); forEach(overloads, o => { const deviation = __conv_self__.getEffectiveDeclarationFlags(o, flagsToCheck) ^ canonicalFlags; if (deviation & ModifierFlags.Export) { __conv_self__.error(getNameOfDeclaration(o), Diagnostics.Overload_signatures_must_all_be_exported_or_non_exported); } else if (deviation & ModifierFlags.Ambient) { __conv_self__.error(getNameOfDeclaration(o), Diagnostics.Overload_signatures_must_all_be_ambient_or_non_ambient); } else if (deviation & (ModifierFlags.Private | ModifierFlags.Protected)) { __conv_self__.error(getNameOfDeclaration(o) || o, Diagnostics.Overload_signatures_must_all_be_public_private_or_protected); } else if (deviation & ModifierFlags.Abstract) { __conv_self__.error(getNameOfDeclaration(o), Diagnostics.Overload_signatures_must_all_be_abstract_or_non_abstract); } }); } } function checkQuestionTokenAgreementBetweenOverloads(overloads: Declaration[], implementation: FunctionLikeDeclaration, someHaveQuestionToken: boolean, allHaveQuestionToken: boolean): void { if (someHaveQuestionToken !== allHaveQuestionToken) { const canonicalHasQuestionToken = hasQuestionToken(getCanonicalOverload(overloads, implementation)); forEach(overloads, o => { const deviation = hasQuestionToken(o) !== canonicalHasQuestionToken; if (deviation) { __conv_self__.error(getNameOfDeclaration(o), Diagnostics.Overload_signatures_must_all_be_optional_or_required); } }); } } const flagsToCheck: ModifierFlags = ModifierFlags.Export | ModifierFlags.Ambient | ModifierFlags.Private | ModifierFlags.Protected | ModifierFlags.Abstract; let someNodeFlags: ModifierFlags = ModifierFlags.None; let allNodeFlags = flagsToCheck; let someHaveQuestionToken = false; let allHaveQuestionToken = true; let hasOverloads = false; let bodyDeclaration: FunctionLikeDeclaration; let lastSeenNonAmbientDeclaration: FunctionLikeDeclaration; let previousDeclaration: FunctionLike; const declarations = symbol.declarations; const isConstructor = (symbol.flags & SymbolFlags.Constructor) !== 0; function reportImplementationExpectedError(node: FunctionLike): void { if (node.name && nodeIsMissing(node.name)) { return; } let seen = false; const subsequentNode = forEachChild(node.parent, c => { if (seen) { return c; } else { seen = c === node; } }); // We may be here because of some extra nodes between overloads that could not be parsed into a valid node. // In this case the subsequent node is not really consecutive (.pos !== node.end), and we must ignore it here. if (subsequentNode && subsequentNode.pos === node.end) { if (subsequentNode.kind === node.kind) { const errorNode: Node = (subsequentNode).name || subsequentNode; // TODO: GH#17345: These are methods, so handle computed name case. (`Always allowing computed property names is *not* the correct behavior!) const subsequentName = (subsequentNode).name; if (node.name && subsequentName && (isComputedPropertyName(node.name) && isComputedPropertyName(subsequentName) || !isComputedPropertyName(node.name) && !isComputedPropertyName(subsequentName) && getEscapedTextOfIdentifierOrLiteral(node.name) === getEscapedTextOfIdentifierOrLiteral(subsequentName))) { const reportError = (node.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.MethodSignature) && hasModifier(node, ModifierFlags.Static) !== hasModifier(subsequentNode, ModifierFlags.Static); // we can get here in two cases // 1. mixed static and instance class members // 2. something with the same name was defined before the set of overloads that prevents them from merging // here we'll report error only for the first case since for second we should already report error in binder if (reportError) { const diagnostic = hasModifier(node, ModifierFlags.Static) ? Diagnostics.Function_overload_must_be_static : Diagnostics.Function_overload_must_not_be_static; __conv_self__.error(errorNode, diagnostic); } return; } else if (nodeIsPresent((subsequentNode).body)) { __conv_self__.error(errorNode, Diagnostics.Function_implementation_name_must_be_0, declarationNameToString(node.name)); return; } } } const errorNode: Node = node.name || node; if (isConstructor) { __conv_self__.error(errorNode, Diagnostics.Constructor_implementation_is_missing); } else { // Report different errors regarding non-consecutive blocks of declarations depending on whether // the node in question is abstract. if (hasModifier(node, ModifierFlags.Abstract)) { __conv_self__.error(errorNode, Diagnostics.All_declarations_of_an_abstract_method_must_be_consecutive); } else { __conv_self__.error(errorNode, Diagnostics.Function_implementation_is_missing_or_not_immediately_following_the_declaration); } } } let duplicateFunctionDeclaration = false; let multipleConstructorImplementation = false; for (const current of declarations) { const node = current; const inAmbientContext = isInAmbientContext(node); const inAmbientContextOrInterface = node.parent.kind === SyntaxKind.InterfaceDeclaration || node.parent.kind === SyntaxKind.TypeLiteral || inAmbientContext; if (inAmbientContextOrInterface) { // check if declarations are consecutive only if they are non-ambient // 1. ambient declarations can be interleaved // i.e. this is legal // declare function foo(); // declare function bar(); // declare function foo(); // 2. mixing ambient and non-ambient declarations is a separate error that will be reported - do not want to report an extra one previousDeclaration = undefined; } if (node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.MethodSignature || node.kind === SyntaxKind.Constructor) { const currentNodeFlags = this.getEffectiveDeclarationFlags(node, flagsToCheck); someNodeFlags |= currentNodeFlags; allNodeFlags &= currentNodeFlags; someHaveQuestionToken = someHaveQuestionToken || hasQuestionToken(node); allHaveQuestionToken = allHaveQuestionToken && hasQuestionToken(node); if (nodeIsPresent((node as FunctionLikeDeclaration).body) && bodyDeclaration) { if (isConstructor) { multipleConstructorImplementation = true; } else { duplicateFunctionDeclaration = true; } } else if (previousDeclaration && previousDeclaration.parent === node.parent && previousDeclaration.end !== node.pos) { reportImplementationExpectedError(previousDeclaration); } if (nodeIsPresent((node as FunctionLikeDeclaration).body)) { if (!bodyDeclaration) { bodyDeclaration = node as FunctionLikeDeclaration; } } else { hasOverloads = true; } previousDeclaration = node; if (!inAmbientContextOrInterface) { lastSeenNonAmbientDeclaration = node as FunctionLikeDeclaration; } } } if (multipleConstructorImplementation) { forEach(declarations, declaration => { __conv_self__.error(declaration, Diagnostics.Multiple_constructor_implementations_are_not_allowed); }); } if (duplicateFunctionDeclaration) { forEach(declarations, declaration => { __conv_self__.error(getNameOfDeclaration(declaration), Diagnostics.Duplicate_function_implementation); }); } // Abstract methods can't have an implementation -- in particular, they don't need one. if (lastSeenNonAmbientDeclaration && !lastSeenNonAmbientDeclaration.body && !hasModifier(lastSeenNonAmbientDeclaration, ModifierFlags.Abstract) && !lastSeenNonAmbientDeclaration.questionToken) { reportImplementationExpectedError(lastSeenNonAmbientDeclaration); } if (hasOverloads) { checkFlagAgreementBetweenOverloads(declarations, bodyDeclaration, flagsToCheck, someNodeFlags, allNodeFlags); checkQuestionTokenAgreementBetweenOverloads(declarations, bodyDeclaration, someHaveQuestionToken, allHaveQuestionToken); if (bodyDeclaration) { const signatures = this.getSignaturesOfSymbol(symbol); const bodySignature = this.getSignatureFromDeclaration(bodyDeclaration); for (const signature of signatures) { if (!this.isImplementationCompatibleWithOverload(bodySignature, signature)) { this.error(signature.declaration, Diagnostics.Overload_signature_is_not_compatible_with_function_implementation); break; } } } } } public checkExportsOnMergedDeclarations(node: Node): void { const __conv_self__ = this; if (!this.produceDiagnostics) { return; } // if localSymbol is defined on node then node itself is exported - check is required let symbol = node.localSymbol; if (!symbol) { // local symbol is undefined => this declaration is non-exported. // however symbol might contain other declarations that are exported symbol = this.getSymbolOfNode(node); if (!symbol.exportSymbol) { // this is a pure local symbol (all declarations are non-exported) - no need to check anything return; } } // run the check only for the first declaration in the list if (getDeclarationOfKind(symbol, node.kind) !== node) { return; } let exportedDeclarationSpaces = DeclarationSpaces.None; let nonExportedDeclarationSpaces = DeclarationSpaces.None; let defaultExportedDeclarationSpaces = DeclarationSpaces.None; for (const d of symbol.declarations) { const declarationSpaces = getDeclarationSpaces(d); const effectiveDeclarationFlags = this.getEffectiveDeclarationFlags(d, ModifierFlags.Export | ModifierFlags.Default); if (effectiveDeclarationFlags & ModifierFlags.Export) { if (effectiveDeclarationFlags & ModifierFlags.Default) { defaultExportedDeclarationSpaces |= declarationSpaces; } else { exportedDeclarationSpaces |= declarationSpaces; } } else { nonExportedDeclarationSpaces |= declarationSpaces; } } // Spaces for anything not declared a 'default export'. const nonDefaultExportedDeclarationSpaces = exportedDeclarationSpaces | nonExportedDeclarationSpaces; const commonDeclarationSpacesForExportsAndLocals = exportedDeclarationSpaces & nonExportedDeclarationSpaces; const commonDeclarationSpacesForDefaultAndNonDefault = defaultExportedDeclarationSpaces & nonDefaultExportedDeclarationSpaces; if (commonDeclarationSpacesForExportsAndLocals || commonDeclarationSpacesForDefaultAndNonDefault) { // declaration spaces for exported and non-exported declarations intersect for (const d of symbol.declarations) { const declarationSpaces = getDeclarationSpaces(d); const name = getNameOfDeclaration(d); // Only error on the declarations that contributed to the intersecting spaces. if (declarationSpaces & commonDeclarationSpacesForDefaultAndNonDefault) { this.error(name, Diagnostics.Merged_declaration_0_cannot_include_a_default_export_declaration_Consider_adding_a_separate_export_default_0_declaration_instead, declarationNameToString(name)); } else if (declarationSpaces & commonDeclarationSpacesForExportsAndLocals) { this.error(name, Diagnostics.Individual_declarations_in_merged_declaration_0_must_be_all_exported_or_all_local, declarationNameToString(name)); } } } const enum DeclarationSpaces { None = 0, ExportValue = 1 << 0, ExportType = 1 << 1, ExportNamespace = 1 << 2 } function getDeclarationSpaces(d: Declaration): DeclarationSpaces { switch (d.kind) { case SyntaxKind.InterfaceDeclaration: case SyntaxKind.TypeAliasDeclaration: return DeclarationSpaces.ExportType; case SyntaxKind.ModuleDeclaration: return isAmbientModule(d) || getModuleInstanceState(d) !== ModuleInstanceState.NonInstantiated ? DeclarationSpaces.ExportNamespace | DeclarationSpaces.ExportValue : DeclarationSpaces.ExportNamespace; case SyntaxKind.ClassDeclaration: case SyntaxKind.EnumDeclaration: return DeclarationSpaces.ExportType | DeclarationSpaces.ExportValue; case SyntaxKind.ImportEqualsDeclaration: let result = DeclarationSpaces.None; const target = __conv_self__.resolveAlias(__conv_self__.getSymbolOfNode(d)); forEach(target.declarations, d => { result |= getDeclarationSpaces(d); }); return result; case SyntaxKind.VariableDeclaration: case SyntaxKind.BindingElement: case SyntaxKind.FunctionDeclaration: case SyntaxKind.ImportSpecifier:// https://github.com/Microsoft/TypeScript/pull/7591 return DeclarationSpaces.ExportValue; default: Debug.fail((ts as any).SyntaxKind[d.kind]); } } } public getAwaitedTypeOfPromise(type: Type, errorNode?: Node, diagnosticMessage?: DiagnosticMessage): Type | undefined { const promisedType = this.getPromisedTypeOfPromise(type, errorNode); return promisedType && this.getAwaitedType(promisedType, errorNode, diagnosticMessage); } /** * Gets the "promised type" of a promise. * @param type The type of the promise. * @remarks The "promised type" of a type is the type of the "value" parameter of the "onfulfilled" callback. */ public getPromisedTypeOfPromise(promise: Type, errorNode?: Node): Type { // // { // promise // then( // thenFunction // onfulfilled: ( // onfulfilledParameterType // value: T // valueParameterType // ) => any // ): any; // } // if (this.isTypeAny(promise)) { return undefined; } const typeAsPromise = promise; if (typeAsPromise.promisedTypeOfPromise) { return typeAsPromise.promisedTypeOfPromise; } if (this.isReferenceToType(promise, this.getGlobalPromiseType(/*reportErrors*/ false))) { return typeAsPromise.promisedTypeOfPromise = (promise).typeArguments[0]; } const thenFunction = this.getTypeOfPropertyOfType(promise, ("then" as __String)); if (this.isTypeAny(thenFunction)) { return undefined; } const thenSignatures = thenFunction ? this.getSignaturesOfType(thenFunction, SignatureKind.Call) : emptyArray; if (thenSignatures.length === 0) { if (errorNode) { this.error(errorNode, Diagnostics.A_promise_must_have_a_then_method); } return undefined; } const onfulfilledParameterType = this.getTypeWithFacts(this.getUnionType(map(thenSignatures, this.getTypeOfFirstParameterOfSignature)), TypeFacts.NEUndefinedOrNull); if (this.isTypeAny(onfulfilledParameterType)) { return undefined; } const onfulfilledParameterSignatures = this.getSignaturesOfType(onfulfilledParameterType, SignatureKind.Call); if (onfulfilledParameterSignatures.length === 0) { if (errorNode) { this.error(errorNode, Diagnostics.The_first_parameter_of_the_then_method_of_a_promise_must_be_a_callback); } return undefined; } return typeAsPromise.promisedTypeOfPromise = this.getUnionType(map(onfulfilledParameterSignatures, this.getTypeOfFirstParameterOfSignature), /*subtypeReduction*/ true); } /** * Gets the "awaited type" of a type. * @param type The type to await. * @remarks The "awaited type" of an expression is its "promised type" if the expression is a * Promise-like type; otherwise, it is the type of the expression. This is used to reflect * The runtime behavior of the `await` keyword. */ public checkAwaitedType(type: Type, errorNode: Node, diagnosticMessage: DiagnosticMessage): Type { return this.getAwaitedType(type, errorNode, diagnosticMessage) || this.unknownType; } public getAwaitedType(type: Type, errorNode?: Node, diagnosticMessage?: DiagnosticMessage): Type | undefined { const typeAsAwaitable = type; if (typeAsAwaitable.awaitedTypeOfType) { return typeAsAwaitable.awaitedTypeOfType; } if (this.isTypeAny(type)) { return typeAsAwaitable.awaitedTypeOfType = type; } if (type.flags & TypeFlags.Union) { let types: Type[]; for (const constituentType of (type).types) { types = append(types, this.getAwaitedType(constituentType, errorNode, diagnosticMessage)); } if (!types) { return undefined; } return typeAsAwaitable.awaitedTypeOfType = this.getUnionType(types, /*subtypeReduction*/ true); } const promisedType = this.getPromisedTypeOfPromise(type); if (promisedType) { if (type.id === promisedType.id || indexOf(this.awaitedTypeStack, promisedType.id) >= 0) { // Verify that we don't have a bad actor in the form of a promise whose // promised type is the same as the promise type, or a mutually recursive // promise. If so, we return undefined as we cannot guess the shape. If this // were the actual case in the JavaScript, this Promise would never resolve. // // An example of a bad actor with a singly-recursive promise type might // be: // // interface BadPromise { // then( // onfulfilled: (value: BadPromise) => any, // onrejected: (error: any) => any): BadPromise; // } // The above interface will pass the PromiseLike check, and return a // promised type of `BadPromise`. Since this is a self reference, we // don't want to keep recursing ad infinitum. // // An example of a bad actor in the form of a mutually-recursive // promise type might be: // // interface BadPromiseA { // then( // onfulfilled: (value: BadPromiseB) => any, // onrejected: (error: any) => any): BadPromiseB; // } // // interface BadPromiseB { // then( // onfulfilled: (value: BadPromiseA) => any, // onrejected: (error: any) => any): BadPromiseA; // } // if (errorNode) { this.error(errorNode, Diagnostics.Type_is_referenced_directly_or_indirectly_in_the_fulfillment_callback_of_its_own_then_method); } return undefined; } // Keep track of the type we're about to unwrap to avoid bad recursive promise types. // See the comments above for more information. this.awaitedTypeStack.push(type.id); const awaitedType = this.getAwaitedType(promisedType, errorNode, diagnosticMessage); this.awaitedTypeStack.pop(); if (!awaitedType) { return undefined; } return typeAsAwaitable.awaitedTypeOfType = awaitedType; } // The type was not a promise, so it could not be unwrapped any further. // As long as the type does not have a callable "then" property, it is // safe to return the type; otherwise, an error will be reported in // the call to getNonThenableType and we will return undefined. // // An example of a non-promise "thenable" might be: // // await { then(): void {} } // // The "thenable" does not match the minimal definition for a promise. When // a Promise/A+-compatible or ES6 promise tries to adopt this value, the promise // will never settle. We treat this as an error to help flag an early indicator // of a runtime problem. If the user wants to return this value from an async // function, they would need to wrap it in some other value. If they want it to // be treated as a promise, they can cast to . const thenFunction = this.getTypeOfPropertyOfType(type, ("then" as __String)); if (thenFunction && this.getSignaturesOfType(thenFunction, SignatureKind.Call).length > 0) { if (errorNode) { Debug.assert(!!diagnosticMessage); this.error(errorNode, diagnosticMessage); } return undefined; } return typeAsAwaitable.awaitedTypeOfType = type; } /** * Checks the return type of an async function to ensure it is a compatible * Promise implementation. * * This checks that an async function has a valid Promise-compatible return type, * and returns the *awaited type* of the promise. An async function has a valid * Promise-compatible return type if the resolved value of the return type has a * construct signature that takes in an `initializer` function that in turn supplies * a `resolve` function as one of its arguments and results in an object with a * callable `then` signature. * * @param node The signature to check */ public checkAsyncFunctionReturnType(node: FunctionLikeDeclaration): Type { // As part of our emit for an async function, we will need to emit the entity name of // the return type annotation as an expression. To meet the necessary runtime semantics // for __awaiter, we must also check that the type of the declaration (e.g. the static // side or "constructor" of the promise type) is compatible `PromiseConstructorLike`. // // An example might be (from lib.es6.d.ts): // // interface Promise { ... } // interface PromiseConstructor { // new (...): Promise; // } // declare var Promise: PromiseConstructor; // // When an async function declares a return type annotation of `Promise`, we // need to get the type of the `Promise` variable declaration above, which would // be `PromiseConstructor`. // // The same case applies to a class: // // declare class Promise { // constructor(...); // then(...): Promise; // } // const returnTypeNode = getEffectiveReturnTypeNode(node); const returnType = this.getTypeFromTypeNode(returnTypeNode); if (this.languageVersion >= ScriptTarget.ES2015) { if (returnType === this.unknownType) { return this.unknownType; } const globalPromiseType = this.getGlobalPromiseType(/*reportErrors*/ true); if (globalPromiseType !== this.emptyGenericType && !this.isReferenceToType(returnType, globalPromiseType)) { // The promise type was not a valid type reference to the global promise type, so we // report an error and return the unknown type. this.error(returnTypeNode, Diagnostics.The_return_type_of_an_async_function_or_method_must_be_the_global_Promise_T_type); return this.unknownType; } } else { // Always mark the type node as referenced if it points to a value this.markTypeNodeAsReferenced(returnTypeNode); if (returnType === this.unknownType) { return this.unknownType; } const promiseConstructorName = getEntityNameFromTypeNode(returnTypeNode); if (promiseConstructorName === undefined) { this.error(returnTypeNode, Diagnostics.Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value, this.typeToString(returnType)); return this.unknownType; } const promiseConstructorSymbol = this.resolveEntityName(promiseConstructorName, SymbolFlags.Value, /*ignoreErrors*/ true); const promiseConstructorType = promiseConstructorSymbol ? this.getTypeOfSymbol(promiseConstructorSymbol) : this.unknownType; if (promiseConstructorType === this.unknownType) { if (promiseConstructorName.kind === SyntaxKind.Identifier && promiseConstructorName.escapedText === "Promise" && this.getTargetType(returnType) === this.getGlobalPromiseType(/*reportErrors*/ false)) { this.error(returnTypeNode, Diagnostics.An_async_function_or_method_in_ES5_SlashES3_requires_the_Promise_constructor_Make_sure_you_have_a_declaration_for_the_Promise_constructor_or_include_ES2015_in_your_lib_option); } else { this.error(returnTypeNode, Diagnostics.Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value, entityNameToString(promiseConstructorName)); } return this.unknownType; } const globalPromiseConstructorLikeType = this.getGlobalPromiseConstructorLikeType(/*reportErrors*/ true); if (globalPromiseConstructorLikeType === this.emptyObjectType) { // If we couldn't resolve the global PromiseConstructorLike type we cannot verify // compatibility with __awaiter. this.error(returnTypeNode, Diagnostics.Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value, entityNameToString(promiseConstructorName)); return this.unknownType; } if (!this.checkTypeAssignableTo(promiseConstructorType, globalPromiseConstructorLikeType, returnTypeNode, Diagnostics.Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value)) { return this.unknownType; } // Verify there is no local declaration that could collide with the promise constructor. const rootName = promiseConstructorName && this.getFirstIdentifier(promiseConstructorName); const collidingSymbol = this.getSymbol(node.locals, rootName.escapedText, SymbolFlags.Value); if (collidingSymbol) { this.error(collidingSymbol.valueDeclaration, Diagnostics.Duplicate_identifier_0_Compiler_uses_declaration_1_to_support_async_functions, unescapeLeadingUnderscores(rootName.escapedText), entityNameToString(promiseConstructorName)); return this.unknownType; } } // Get and return the awaited type of the return type. return this.checkAwaitedType(returnType, node, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member); } /** Check a decorator */ public checkDecorator(node: Decorator): void { const signature = this.getResolvedSignature(node); const returnType = this.getReturnTypeOfSignature(signature); if (returnType.flags & TypeFlags.Any) { return; } let expectedReturnType: Type; const headMessage = this.getDiagnosticHeadMessageForDecoratorResolution(node); let errorInfo: DiagnosticMessageChain; switch (node.parent.kind) { case SyntaxKind.ClassDeclaration: const classSymbol = this.getSymbolOfNode(node.parent); const classConstructorType = this.getTypeOfSymbol(classSymbol); expectedReturnType = this.getUnionType([classConstructorType, this.voidType]); break; case SyntaxKind.Parameter: expectedReturnType = this.voidType; errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.The_return_type_of_a_parameter_decorator_function_must_be_either_void_or_any); break; case SyntaxKind.PropertyDeclaration: expectedReturnType = this.voidType; errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.The_return_type_of_a_property_decorator_function_must_be_either_void_or_any); break; case SyntaxKind.MethodDeclaration: case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: const methodType = this.getTypeOfNode(node.parent); const descriptorType = this.createTypedPropertyDescriptorType(methodType); expectedReturnType = this.getUnionType([descriptorType, this.voidType]); break; } this.checkTypeAssignableTo(returnType, expectedReturnType, node, headMessage, errorInfo); } /** * If a TypeNode can be resolved to a value symbol imported from an external module, it is * marked as referenced to prevent import elision. */ public markTypeNodeAsReferenced(node: TypeNode) { this.markEntityNameOrEntityExpressionAsReference(node && getEntityNameFromTypeNode(node)); } public markEntityNameOrEntityExpressionAsReference(typeName: EntityNameOrEntityNameExpression) { const rootName = typeName && this.getFirstIdentifier(typeName); const rootSymbol = rootName && this.resolveName(rootName, rootName.escapedText, (typeName.kind === SyntaxKind.Identifier ? SymbolFlags.Type : SymbolFlags.Namespace) | SymbolFlags.Alias, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined); if (rootSymbol && rootSymbol.flags & SymbolFlags.Alias && this.symbolIsValue(rootSymbol) && !this.isConstEnumOrConstEnumOnlyModule(this.resolveAlias(rootSymbol))) { this.markAliasSymbolAsReferenced(rootSymbol); } } /** * This function marks the type used for metadata decorator as referenced if it is import * from external module. * This is different from markTypeNodeAsReferenced because it tries to simplify type nodes in * union and intersection type * @param node */ public markDecoratorMedataDataTypeNodeAsReferenced(node: TypeNode): void { const entityName = this.getEntityNameForDecoratorMetadata(node); if (entityName && isEntityName(entityName)) { this.markEntityNameOrEntityExpressionAsReference(entityName); } } public getEntityNameForDecoratorMetadata(node: TypeNode): EntityName { if (node) { switch (node.kind) { case SyntaxKind.IntersectionType: case SyntaxKind.UnionType: let commonEntityName: EntityName; for (const typeNode of (node).types) { const individualEntityName = this.getEntityNameForDecoratorMetadata(typeNode); if (!individualEntityName) { // Individual is something like string number // So it would be serialized to either that type or object // Safe to return here return undefined; } if (commonEntityName) { // Note this is in sync with the transformation that happens for type node. // Keep this in sync with serializeUnionOrIntersectionType // Verify if they refer to same entity and is identifier // return undefined if they dont match because we would emit object if (!isIdentifier(commonEntityName) || !isIdentifier(individualEntityName) || commonEntityName.escapedText !== individualEntityName.escapedText) { return undefined; } } else { commonEntityName = individualEntityName; } } return commonEntityName; case SyntaxKind.ParenthesizedType: return this.getEntityNameForDecoratorMetadata((node).type); case SyntaxKind.TypeReference: return (node).typeName; } } } public getParameterTypeNodeForDecoratorCheck(node: ParameterDeclaration): TypeNode { const typeNode = getEffectiveTypeAnnotationNode(node); return isRestParameter(node) ? getRestParameterElementType(typeNode) : typeNode; } /** Check the decorators of a node */ public checkDecorators(node: Node): void { if (!node.decorators) { return; } // skip this check for nodes that cannot have decorators. These should have already had an error reported by // checkGrammarDecorators. if (!nodeCanBeDecorated(node)) { return; } if (!this.compilerOptions.experimentalDecorators) { this.error(node, Diagnostics.Experimental_support_for_decorators_is_a_feature_that_is_subject_to_change_in_a_future_release_Set_the_experimentalDecorators_option_to_remove_this_warning); } const firstDecorator = node.decorators[0]; this.checkExternalEmitHelpers(firstDecorator, ExternalEmitHelpers.Decorate); if (node.kind === SyntaxKind.Parameter) { this.checkExternalEmitHelpers(firstDecorator, ExternalEmitHelpers.Param); } if (this.compilerOptions.emitDecoratorMetadata) { this.checkExternalEmitHelpers(firstDecorator, ExternalEmitHelpers.Metadata); // we only need to perform these checks if we are emitting serialized type metadata for the target of a decorator. switch (node.kind) { case SyntaxKind.ClassDeclaration: const constructor = getFirstConstructorWithBody(node); if (constructor) { for (const parameter of constructor.parameters) { this.markDecoratorMedataDataTypeNodeAsReferenced(this.getParameterTypeNodeForDecoratorCheck(parameter)); } } break; case SyntaxKind.MethodDeclaration: case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: for (const parameter of (node).parameters) { this.markDecoratorMedataDataTypeNodeAsReferenced(this.getParameterTypeNodeForDecoratorCheck(parameter)); } this.markDecoratorMedataDataTypeNodeAsReferenced(getEffectiveReturnTypeNode(node)); break; case SyntaxKind.PropertyDeclaration: this.markDecoratorMedataDataTypeNodeAsReferenced(getEffectiveTypeAnnotationNode(node)); break; case SyntaxKind.Parameter: this.markDecoratorMedataDataTypeNodeAsReferenced(this.getParameterTypeNodeForDecoratorCheck((node))); break; } } forEach(node.decorators, this.checkDecorator); } public checkFunctionDeclaration(node: FunctionDeclaration): void { if (this.produceDiagnostics) { this.checkFunctionOrMethodDeclaration(node); this.checkGrammarForGenerator(node); this.checkCollisionWithCapturedSuperVariable(node, node.name); this.checkCollisionWithCapturedThisVariable(node, node.name); this.checkCollisionWithCapturedNewTargetVariable(node, node.name); this.checkCollisionWithRequireExportsInGeneratedCode(node, node.name); this.checkCollisionWithGlobalPromiseInGeneratedCode(node, node.name); } } public checkJSDoc(node: FunctionDeclaration | MethodDeclaration) { if (!isInJavaScriptFile(node)) { return; } forEach(node.jsDoc, this.checkSourceElement); } public checkJSDocComment(node: JSDoc) { if ((node as JSDoc).tags) { for (const tag of (node as JSDoc).tags) { this.checkSourceElement(tag); } } } public checkFunctionOrMethodDeclaration(node: FunctionDeclaration | MethodDeclaration): void { this.checkJSDoc(node); this.checkDecorators(node); this.checkSignatureDeclaration(node); const functionFlags = getFunctionFlags(node); // Do not use hasDynamicName here, because that returns false for well known symbols. // We want to perform checkComputedPropertyName for all computed properties, including // well known symbols. if (node.name && node.name.kind === SyntaxKind.ComputedPropertyName) { // This check will account for methods in class/interface declarations, // as well as accessors in classes/object literals this.checkComputedPropertyName((node.name)); } if (!hasDynamicName(node)) { // first we want to check the local symbol that contain this declaration // - if node.localSymbol !== undefined - this is current declaration is exported and localSymbol points to the local symbol // - if node.localSymbol === undefined - this node is non-exported so we can just pick the result of getSymbolOfNode const symbol = this.getSymbolOfNode(node); const localSymbol = node.localSymbol || symbol; // Since the javascript won't do semantic analysis like typescript, // if the javascript file comes before the typescript file and both contain same name functions, // checkFunctionOrConstructorSymbol wouldn't be called if we didnt ignore javascript function. const firstDeclaration = find(localSymbol.declarations, // Get first non javascript function declaration declaration => declaration.kind === node.kind && !isSourceFileJavaScript(getSourceFileOfNode(declaration))); // Only type check the symbol once if (node === firstDeclaration) { this.checkFunctionOrConstructorSymbol(localSymbol); } if (symbol.parent) { // run check once for the first declaration if (getDeclarationOfKind(symbol, node.kind) === node) { // run check on export symbol to check that modifiers agree across all exported declarations this.checkFunctionOrConstructorSymbol(symbol); } } } this.checkSourceElement(node.body); const returnTypeNode = getEffectiveReturnTypeNode(node); if ((functionFlags & FunctionFlags.Generator) === 0) { const returnOrPromisedType = returnTypeNode && (functionFlags & FunctionFlags.Async ? this.checkAsyncFunctionReturnType(node) // Async function : this.getTypeFromTypeNode(returnTypeNode)); // normal function this.checkAllCodePathsInNonVoidFunctionReturnOrThrow(node, returnOrPromisedType); } if (this.produceDiagnostics && !returnTypeNode) { // Report an implicit any error if there is no body, no explicit return type, and node is not a private method // in an ambient context if (this.noImplicitAny && nodeIsMissing(node.body) && !this.isPrivateWithinAmbient(node)) { this.reportImplicitAnyError(node, this.anyType); } if (functionFlags & FunctionFlags.Generator && nodeIsPresent(node.body)) { // A generator with a body and no type annotation can still cause errors. It can error if the // yielded values have no common supertype, or it can give an implicit any error if it has no // yielded values. The only way to trigger these errors is to try checking its return type. this.getReturnTypeOfSignature(this.getSignatureFromDeclaration(node)); } } this.registerForUnusedIdentifiersCheck(node); } public registerForUnusedIdentifiersCheck(node: Node) { if (this.deferredUnusedIdentifierNodes) { this.deferredUnusedIdentifierNodes.push(node); } } public checkUnusedIdentifiers() { if (this.deferredUnusedIdentifierNodes) { for (const node of this.deferredUnusedIdentifierNodes) { switch (node.kind) { case SyntaxKind.SourceFile: case SyntaxKind.ModuleDeclaration: this.checkUnusedModuleMembers((node)); break; case SyntaxKind.ClassDeclaration: case SyntaxKind.ClassExpression: this.checkUnusedClassMembers((node)); this.checkUnusedTypeParameters((node)); break; case SyntaxKind.InterfaceDeclaration: this.checkUnusedTypeParameters((node)); break; case SyntaxKind.Block: case SyntaxKind.CaseBlock: case SyntaxKind.ForStatement: case SyntaxKind.ForInStatement: case SyntaxKind.ForOfStatement: this.checkUnusedLocalsAndParameters(node); break; case SyntaxKind.Constructor: case SyntaxKind.FunctionExpression: case SyntaxKind.FunctionDeclaration: case SyntaxKind.ArrowFunction: case SyntaxKind.MethodDeclaration: case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: if ((node).body) { this.checkUnusedLocalsAndParameters((node)); } this.checkUnusedTypeParameters((node)); break; case SyntaxKind.MethodSignature: case SyntaxKind.CallSignature: case SyntaxKind.ConstructSignature: case SyntaxKind.IndexSignature: case SyntaxKind.FunctionType: case SyntaxKind.ConstructorType: this.checkUnusedTypeParameters((node)); break; case SyntaxKind.TypeAliasDeclaration: this.checkUnusedTypeParameters((node)); break; } } } } public checkUnusedLocalsAndParameters(node: Node): void { const __conv_self__ = this; if (node.parent.kind !== SyntaxKind.InterfaceDeclaration && this.noUnusedIdentifiers && !isInAmbientContext(node)) { node.locals.forEach(local => { if (!local.isReferenced) { if (local.valueDeclaration && getRootDeclaration(local.valueDeclaration).kind === SyntaxKind.Parameter) { const parameter = getRootDeclaration(local.valueDeclaration); const name = getNameOfDeclaration(local.valueDeclaration); if (__conv_self__.compilerOptions.noUnusedParameters && !isParameterPropertyDeclaration(parameter) && !parameterIsThisKeyword(parameter) && !__conv_self__.parameterNameStartsWithUnderscore(name)) { __conv_self__.error(name, Diagnostics._0_is_declared_but_never_used, unescapeLeadingUnderscores(local.escapedName)); } } else if (__conv_self__.compilerOptions.noUnusedLocals) { forEach(local.declarations, d => __conv_self__.errorUnusedLocal(getNameOfDeclaration(d) || d, unescapeLeadingUnderscores(local.escapedName))); } } }); } } public isRemovedPropertyFromObjectSpread(node: Node) { if (isBindingElement(node) && isObjectBindingPattern(node.parent)) { const lastElement = lastOrUndefined(node.parent.elements); return lastElement !== node && !!lastElement.dotDotDotToken; } return false; } public errorUnusedLocal(node: Node, name: string) { if (this.isIdentifierThatStartsWithUnderScore(node)) { const declaration = getRootDeclaration(node.parent); if (declaration.kind === SyntaxKind.VariableDeclaration && isForInOrOfStatement(declaration.parent.parent)) { return; } } if (!this.isRemovedPropertyFromObjectSpread(node.kind === SyntaxKind.Identifier ? node.parent : node)) { this.error(node, Diagnostics._0_is_declared_but_never_used, name); } } public parameterNameStartsWithUnderscore(parameterName: DeclarationName) { return parameterName && this.isIdentifierThatStartsWithUnderScore(parameterName); } public isIdentifierThatStartsWithUnderScore(node: Node) { return node.kind === SyntaxKind.Identifier && unescapeLeadingUnderscores((node).escapedText).charCodeAt(0) === CharacterCodes._; } public checkUnusedClassMembers(node: ClassDeclaration | ClassExpression): void { if (this.compilerOptions.noUnusedLocals && !isInAmbientContext(node)) { if (node.members) { for (const member of node.members) { if (member.kind === SyntaxKind.MethodDeclaration || member.kind === SyntaxKind.PropertyDeclaration) { if (!member.symbol.isReferenced && hasModifier(member, ModifierFlags.Private)) { this.error(member.name, Diagnostics._0_is_declared_but_never_used, unescapeLeadingUnderscores(member.symbol.escapedName)); } } else if (member.kind === SyntaxKind.Constructor) { for (const parameter of (member).parameters) { if (!parameter.symbol.isReferenced && hasModifier(parameter, ModifierFlags.Private)) { this.error(parameter.name, Diagnostics.Property_0_is_declared_but_never_used, unescapeLeadingUnderscores(parameter.symbol.escapedName)); } } } } } } } public checkUnusedTypeParameters(node: ClassDeclaration | ClassExpression | FunctionDeclaration | MethodDeclaration | FunctionExpression | ArrowFunction | ConstructorDeclaration | SignatureDeclaration | InterfaceDeclaration | TypeAliasDeclaration) { if (this.compilerOptions.noUnusedLocals && !isInAmbientContext(node)) { if (node.typeParameters) { // Only report errors on the last declaration for the type parameter container; // this ensures that all uses have been accounted for. const symbol = this.getSymbolOfNode(node); const lastDeclaration = symbol && symbol.declarations && lastOrUndefined(symbol.declarations); if (lastDeclaration !== node) { return; } for (const typeParameter of node.typeParameters) { if (!this.getMergedSymbol(typeParameter.symbol).isReferenced) { this.error(typeParameter.name, Diagnostics._0_is_declared_but_never_used, unescapeLeadingUnderscores(typeParameter.symbol.escapedName)); } } } } } public checkUnusedModuleMembers(node: ModuleDeclaration | SourceFile): void { const __conv_self__ = this; if (this.compilerOptions.noUnusedLocals && !isInAmbientContext(node)) { node.locals.forEach(local => { if (!local.isReferenced && !local.exportSymbol) { for (const declaration of local.declarations) { if (!isAmbientModule(declaration)) { __conv_self__.errorUnusedLocal(getNameOfDeclaration(declaration), unescapeLeadingUnderscores(local.escapedName)); } } } }); } } public checkBlock(node: Block) { // Grammar checking for SyntaxKind.Block if (node.kind === SyntaxKind.Block) { this.checkGrammarStatementInAmbientContext(node); } forEach(node.statements, this.checkSourceElement); if (node.locals) { this.registerForUnusedIdentifiersCheck(node); } } public checkCollisionWithArgumentsInGeneratedCode(node: SignatureDeclaration) { const __conv_self__ = this; // no rest parameters \ declaration context \ overload - no codegen impact if (!hasDeclaredRestParameter(node) || isInAmbientContext(node) || nodeIsMissing((node).body)) { return; } forEach(node.parameters, p => { if (p.name && !isBindingPattern(p.name) && p.name.escapedText === __conv_self__.argumentsSymbol.escapedName) { __conv_self__.error(p, Diagnostics.Duplicate_identifier_arguments_Compiler_uses_arguments_to_initialize_rest_parameters); } }); } public needCollisionCheckForIdentifier(node: Node, identifier: Identifier, name: string): boolean { if (!(identifier && identifier.escapedText === name)) { return false; } if (node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.PropertySignature || node.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.MethodSignature || node.kind === SyntaxKind.GetAccessor || node.kind === SyntaxKind.SetAccessor) { // it is ok to have member named '_super' or '_this' - member access is always qualified return false; } if (isInAmbientContext(node)) { // ambient context - no codegen impact return false; } const root = getRootDeclaration(node); if (root.kind === SyntaxKind.Parameter && nodeIsMissing((root.parent).body)) { // just an overload - no codegen impact return false; } return true; } public checkCollisionWithCapturedThisVariable(node: Node, name: Identifier): void { if (this.needCollisionCheckForIdentifier(node, name, "_this")) { this.potentialThisCollisions.push(node); } } public checkCollisionWithCapturedNewTargetVariable(node: Node, name: Identifier): void { if (this.needCollisionCheckForIdentifier(node, name, "_newTarget")) { this.potentialNewTargetCollisions.push(node); } } // this function will run after checking the source file so 'CaptureThis' is correct for all nodes public checkIfThisIsCapturedInEnclosingScope(node: Node): void { const __conv_self__ = this; findAncestor(node, current => { if (__conv_self__.getNodeCheckFlags(current) & NodeCheckFlags.CaptureThis) { const isDeclaration = node.kind !== SyntaxKind.Identifier; if (isDeclaration) { __conv_self__.error(getNameOfDeclaration(node), Diagnostics.Duplicate_identifier_this_Compiler_uses_variable_declaration_this_to_capture_this_reference); } else { __conv_self__.error(node, Diagnostics.Expression_resolves_to_variable_declaration_this_that_compiler_uses_to_capture_this_reference); } return true; } }); } public checkIfNewTargetIsCapturedInEnclosingScope(node: Node): void { const __conv_self__ = this; findAncestor(node, current => { if (__conv_self__.getNodeCheckFlags(current) & NodeCheckFlags.CaptureNewTarget) { const isDeclaration = node.kind !== SyntaxKind.Identifier; if (isDeclaration) { __conv_self__.error(getNameOfDeclaration(node), Diagnostics.Duplicate_identifier_newTarget_Compiler_uses_variable_declaration_newTarget_to_capture_new_target_meta_property_reference); } else { __conv_self__.error(node, Diagnostics.Expression_resolves_to_variable_declaration_newTarget_that_compiler_uses_to_capture_new_target_meta_property_reference); } return true; } }); } public checkCollisionWithCapturedSuperVariable(node: Node, name: Identifier) { if (!this.needCollisionCheckForIdentifier(node, name, "_super")) { return; } // bubble up and find containing type const enclosingClass = getContainingClass(node); // if containing type was not found or it is ambient - exit (no codegen) if (!enclosingClass || isInAmbientContext(enclosingClass)) { return; } if (getClassExtendsHeritageClauseElement(enclosingClass)) { const isDeclaration = node.kind !== SyntaxKind.Identifier; if (isDeclaration) { this.error(node, Diagnostics.Duplicate_identifier_super_Compiler_uses_super_to_capture_base_class_reference); } else { this.error(node, Diagnostics.Expression_resolves_to_super_that_compiler_uses_to_capture_base_class_reference); } } } public checkCollisionWithRequireExportsInGeneratedCode(node: Node, name: Identifier) { // No need to check for require or exports for ES6 modules and later if (this.modulekind >= ModuleKind.ES2015) { return; } if (!this.needCollisionCheckForIdentifier(node, name, "require") && !this.needCollisionCheckForIdentifier(node, name, "exports")) { return; } // Uninstantiated modules shouldnt do this check if (node.kind === SyntaxKind.ModuleDeclaration && getModuleInstanceState(node) !== ModuleInstanceState.Instantiated) { return; } // In case of variable declaration, node.parent is variable statement so look at the variable statement's parent const parent = this.getDeclarationContainer(node); if (parent.kind === SyntaxKind.SourceFile && isExternalOrCommonJsModule(parent)) { // If the declaration happens to be in external module, report error that require and exports are reserved keywords this.error(name, Diagnostics.Duplicate_identifier_0_Compiler_reserves_name_1_in_top_level_scope_of_a_module, declarationNameToString(name), declarationNameToString(name)); } } public checkCollisionWithGlobalPromiseInGeneratedCode(node: Node, name: Identifier): void { if (this.languageVersion >= ScriptTarget.ES2017 || !this.needCollisionCheckForIdentifier(node, name, "Promise")) { return; } // Uninstantiated modules shouldnt do this check if (node.kind === SyntaxKind.ModuleDeclaration && getModuleInstanceState(node) !== ModuleInstanceState.Instantiated) { return; } // In case of variable declaration, node.parent is variable statement so look at the variable statement's parent const parent = this.getDeclarationContainer(node); if (parent.kind === SyntaxKind.SourceFile && isExternalOrCommonJsModule(parent) && parent.flags & NodeFlags.HasAsyncFunctions) { // If the declaration happens to be in external module, report error that Promise is a reserved identifier. this.error(name, Diagnostics.Duplicate_identifier_0_Compiler_reserves_name_1_in_top_level_scope_of_a_module_containing_async_functions, declarationNameToString(name), declarationNameToString(name)); } } public checkVarDeclaredNamesNotShadowed(node: VariableDeclaration | BindingElement) { // - ScriptBody : StatementList // It is a Syntax Error if any element of the LexicallyDeclaredNames of StatementList // also occurs in the VarDeclaredNames of StatementList. // - Block : { StatementList } // It is a Syntax Error if any element of the LexicallyDeclaredNames of StatementList // also occurs in the VarDeclaredNames of StatementList. // Variable declarations are hoisted to the top of their function scope. They can shadow // block scoped declarations, which bind tighter. this will not be flagged as duplicate definition // by the binder as the declaration scope is different. // A non-initialized declaration is a no-op as the block declaration will resolve before the var // declaration. the problem is if the declaration has an initializer. this will act as a write to the // block declared value. this is fine for let, but not const. // Only consider declarations with initializers, uninitialized const declarations will not // step on a let/const variable. // Do not consider const and const declarations, as duplicate block-scoped declarations // are handled by the binder. // We are only looking for const declarations that step on let\const declarations from a // different scope. e.g.: // { // const x = 0; // localDeclarationSymbol obtained after name resolution will correspond to this declaration // const x = 0; // symbol for this declaration will be 'symbol' // } // skip block-scoped variables and parameters if ((getCombinedNodeFlags(node) & NodeFlags.BlockScoped) !== 0 || isParameterDeclaration(node)) { return; } // skip variable declarations that don't have initializers // NOTE: in ES6 spec initializer is required in variable declarations where name is binding pattern // so we'll always treat binding elements as initialized if (node.kind === SyntaxKind.VariableDeclaration && !node.initializer) { return; } const symbol = this.getSymbolOfNode(node); if (symbol.flags & SymbolFlags.FunctionScopedVariable) { if (!isIdentifier(node.name)) throw Debug.fail(); const localDeclarationSymbol = this.resolveName(node, node.name.escapedText, SymbolFlags.Variable, /*nodeNotFoundErrorMessage*/ undefined, /*nameArg*/ undefined); if (localDeclarationSymbol && localDeclarationSymbol !== symbol && localDeclarationSymbol.flags & SymbolFlags.BlockScopedVariable) { if (this.getDeclarationNodeFlagsFromSymbol(localDeclarationSymbol) & NodeFlags.BlockScoped) { const varDeclList = getAncestor(localDeclarationSymbol.valueDeclaration, SyntaxKind.VariableDeclarationList); const container = varDeclList.parent.kind === SyntaxKind.VariableStatement && varDeclList.parent.parent ? varDeclList.parent.parent : undefined; // names of block-scoped and function scoped variables can collide only // if block scoped variable is defined in the function\module\source file scope (because of variable hoisting) const namesShareScope = container && (container.kind === SyntaxKind.Block && isFunctionLike(container.parent) || container.kind === SyntaxKind.ModuleBlock || container.kind === SyntaxKind.ModuleDeclaration || container.kind === SyntaxKind.SourceFile); // here we know that function scoped variable is shadowed by block scoped one // if they are defined in the same scope - binder has already reported redeclaration error // otherwise if variable has an initializer - show error that initialization will fail // since LHS will be block scoped name instead of function scoped if (!namesShareScope) { const name = this.symbolToString(localDeclarationSymbol); this.error(node, Diagnostics.Cannot_initialize_outer_scoped_variable_0_in_the_same_scope_as_block_scoped_declaration_1, name, name); } } } } } // Check that a parameter initializer contains no references to parameters declared to the right of itself public checkParameterInitializer(node: VariableLikeDeclaration): void { const __conv_self__ = this; if (getRootDeclaration(node).kind !== SyntaxKind.Parameter) { return; } const func = getContainingFunction(node); visit(node.initializer); function visit(n: Node): void { if (isTypeNode(n) || isDeclarationName(n)) { // do not dive in types // skip declaration names (i.e. in object literal expressions) return; } if (n.kind === SyntaxKind.PropertyAccessExpression) { // skip property names in property access expression return visit((n).expression); } else if (n.kind === SyntaxKind.Identifier) { // check FunctionLikeDeclaration.locals (stores parameters\function local variable) // if it contains entry with a specified name const symbol = __conv_self__.resolveName(n, (n).escapedText, SymbolFlags.Value | SymbolFlags.Alias, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined); if (!symbol || symbol === __conv_self__.unknownSymbol || !symbol.valueDeclaration) { return; } if (symbol.valueDeclaration === node) { __conv_self__.error(n, Diagnostics.Parameter_0_cannot_be_referenced_in_its_initializer, declarationNameToString(node.name)); return; } // locals map for function contain both parameters and function locals // so we need to do a bit of extra work to check if reference is legal const enclosingContainer = getEnclosingBlockScopeContainer(symbol.valueDeclaration); if (enclosingContainer === func) { if (symbol.valueDeclaration.kind === SyntaxKind.Parameter || symbol.valueDeclaration.kind === SyntaxKind.BindingElement) { // it is ok to reference parameter in initializer if either // - parameter is located strictly on the left of current parameter declaration if (symbol.valueDeclaration.pos < node.pos) { return; } // - parameter is wrapped in function-like entity if (findAncestor(n, current => { if (current === node.initializer) { return "quit"; } return isFunctionLike(current.parent) || // computed property names/initializers in instance property declaration of class like entities // are executed in constructor and thus deferred (current.parent.kind === SyntaxKind.PropertyDeclaration && !(hasModifier(current.parent, ModifierFlags.Static)) && isClassLike(current.parent.parent)); })) { return; } // fall through to report error } __conv_self__.error(n, Diagnostics.Initializer_of_parameter_0_cannot_reference_identifier_1_declared_after_it, declarationNameToString(node.name), declarationNameToString(n)); } } else { return forEachChild(n, visit); } } } public convertAutoToAny(type: Type) { return type === this.autoType ? this.anyType : type === this.autoArrayType ? this.anyArrayType : type; } // Check variable, parameter, or property declaration public checkVariableLikeDeclaration(node: VariableLikeDeclaration) { this.checkDecorators(node); this.checkSourceElement(node.type); // JSDoc `function(string, string): string` syntax results in parameters with no name if (!node.name) { return; } // For a computed property, just check the initializer and exit // Do not use hasDynamicName here, because that returns false for well known symbols. // We want to perform checkComputedPropertyName for all computed properties, including // well known symbols. if (node.name.kind === SyntaxKind.ComputedPropertyName) { this.checkComputedPropertyName((node.name)); if (node.initializer) { this.checkExpressionCached(node.initializer); } } if (node.kind === SyntaxKind.BindingElement) { if (node.parent.kind === SyntaxKind.ObjectBindingPattern && this.languageVersion < ScriptTarget.ESNext) { this.checkExternalEmitHelpers(node, ExternalEmitHelpers.Rest); } // check computed properties inside property names of binding elements if (node.propertyName && node.propertyName.kind === SyntaxKind.ComputedPropertyName) { this.checkComputedPropertyName((node.propertyName)); } // check private/protected variable access const parent = (node.parent).parent; const parentType = this.getTypeForBindingElementParent(parent); const name = node.propertyName || node.name; const property = this.getPropertyOfType(parentType, getTextOfPropertyName(name)); this.markPropertyAsReferenced(property); if (parent.initializer && property) { this.checkPropertyAccessibility(parent, parent.initializer, parentType, property); } } // For a binding pattern, check contained binding elements if (isBindingPattern(node.name)) { if (node.name.kind === SyntaxKind.ArrayBindingPattern && this.languageVersion < ScriptTarget.ES2015 && this.compilerOptions.downlevelIteration) { this.checkExternalEmitHelpers(node, ExternalEmitHelpers.Read); } forEach((node.name).elements, this.checkSourceElement); } // For a parameter declaration with an initializer, error and exit if the containing function doesn't have a body if (node.initializer && getRootDeclaration(node).kind === SyntaxKind.Parameter && nodeIsMissing((getContainingFunction(node) as FunctionLikeDeclaration).body)) { this.error(node, Diagnostics.A_parameter_initializer_is_only_allowed_in_a_function_or_constructor_implementation); return; } // For a binding pattern, validate the initializer and exit if (isBindingPattern(node.name)) { // Don't validate for-in initializer as it is already an error if (node.initializer && node.parent.parent.kind !== SyntaxKind.ForInStatement) { this.checkTypeAssignableTo(this.checkExpressionCached(node.initializer), this.getWidenedTypeForVariableLikeDeclaration(node), node, /*headMessage*/ undefined); this.checkParameterInitializer(node); } return; } const symbol = this.getSymbolOfNode(node); const type = this.convertAutoToAny(this.getTypeOfVariableOrParameterOrProperty(symbol)); if (node === symbol.valueDeclaration) { // Node is the primary declaration of the symbol, just validate the initializer // Don't validate for-in initializer as it is already an error if (node.initializer && node.parent.parent.kind !== SyntaxKind.ForInStatement) { this.checkTypeAssignableTo(this.checkExpressionCached(node.initializer), type, node, /*headMessage*/ undefined); this.checkParameterInitializer(node); } } else { // Node is a secondary declaration, check that type is identical to primary declaration and check that // initializer is consistent with type associated with the node const declarationType = this.convertAutoToAny(this.getWidenedTypeForVariableLikeDeclaration(node)); if (type !== this.unknownType && declarationType !== this.unknownType && !this.isTypeIdenticalTo(type, declarationType)) { this.error(node.name, Diagnostics.Subsequent_variable_declarations_must_have_the_same_type_Variable_0_must_be_of_type_1_but_here_has_type_2, declarationNameToString(node.name), this.typeToString(type), this.typeToString(declarationType)); } if (node.initializer) { this.checkTypeAssignableTo(this.checkExpressionCached(node.initializer), declarationType, node, /*headMessage*/ undefined); } if (!this.areDeclarationFlagsIdentical(node, symbol.valueDeclaration)) { this.error(getNameOfDeclaration(symbol.valueDeclaration), Diagnostics.All_declarations_of_0_must_have_identical_modifiers, declarationNameToString(node.name)); this.error(node.name, Diagnostics.All_declarations_of_0_must_have_identical_modifiers, declarationNameToString(node.name)); } } if (node.kind !== SyntaxKind.PropertyDeclaration && node.kind !== SyntaxKind.PropertySignature) { // We know we don't have a binding pattern or computed name here this.checkExportsOnMergedDeclarations(node); if (node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.BindingElement) { this.checkVarDeclaredNamesNotShadowed((node)); } this.checkCollisionWithCapturedSuperVariable(node, (node.name)); this.checkCollisionWithCapturedThisVariable(node, (node.name)); this.checkCollisionWithCapturedNewTargetVariable(node, (node.name)); this.checkCollisionWithRequireExportsInGeneratedCode(node, (node.name)); this.checkCollisionWithGlobalPromiseInGeneratedCode(node, (node.name)); } } public areDeclarationFlagsIdentical(left: Declaration, right: Declaration) { if ((left.kind === SyntaxKind.Parameter && right.kind === SyntaxKind.VariableDeclaration) || (left.kind === SyntaxKind.VariableDeclaration && right.kind === SyntaxKind.Parameter)) { // Differences in optionality between parameters and variables are allowed. return true; } if (hasQuestionToken(left) !== hasQuestionToken(right)) { return false; } const interestingFlags = ModifierFlags.Private | ModifierFlags.Protected | ModifierFlags.Async | ModifierFlags.Abstract | ModifierFlags.Readonly | ModifierFlags.Static; return getSelectedModifierFlags(left, interestingFlags) === getSelectedModifierFlags(right, interestingFlags); } public checkVariableDeclaration(node: VariableDeclaration) { this.checkGrammarVariableDeclaration(node); return this.checkVariableLikeDeclaration(node); } public checkBindingElement(node: BindingElement) { this.checkGrammarBindingElement((node)); return this.checkVariableLikeDeclaration(node); } public checkVariableStatement(node: VariableStatement) { // Grammar checking this.checkGrammarDecorators(node) || this.checkGrammarModifiers(node) || this.checkGrammarVariableDeclarationList(node.declarationList) || this.checkGrammarForDisallowedLetOrConstStatement(node); forEach(node.declarationList.declarations, this.checkSourceElement); } public checkGrammarDisallowedModifiersOnObjectLiteralExpressionMethod(node: MethodDeclaration) { // We only disallow modifier on a method declaration if it is a property of object-literal-expression if (node.modifiers && node.parent.kind === SyntaxKind.ObjectLiteralExpression) { if (getFunctionFlags(node) & FunctionFlags.Async) { if (node.modifiers.length > 1) { return this.grammarErrorOnFirstToken(node, Diagnostics.Modifiers_cannot_appear_here); } } else { return this.grammarErrorOnFirstToken(node, Diagnostics.Modifiers_cannot_appear_here); } } } public checkExpressionStatement(node: ExpressionStatement) { // Grammar checking this.checkGrammarStatementInAmbientContext(node); this.checkExpression(node.expression); } public checkIfStatement(node: IfStatement) { // Grammar checking this.checkGrammarStatementInAmbientContext(node); this.checkExpression(node.expression); this.checkSourceElement(node.thenStatement); if (node.thenStatement.kind === SyntaxKind.EmptyStatement) { this.error(node.thenStatement, Diagnostics.The_body_of_an_if_statement_cannot_be_the_empty_statement); } this.checkSourceElement(node.elseStatement); } public checkDoStatement(node: DoStatement) { // Grammar checking this.checkGrammarStatementInAmbientContext(node); this.checkSourceElement(node.statement); this.checkExpression(node.expression); } public checkWhileStatement(node: WhileStatement) { // Grammar checking this.checkGrammarStatementInAmbientContext(node); this.checkExpression(node.expression); this.checkSourceElement(node.statement); } public checkForStatement(node: ForStatement) { // Grammar checking if (!this.checkGrammarStatementInAmbientContext(node)) { if (node.initializer && node.initializer.kind === SyntaxKind.VariableDeclarationList) { this.checkGrammarVariableDeclarationList((node.initializer)); } } if (node.initializer) { if (node.initializer.kind === SyntaxKind.VariableDeclarationList) { forEach((node.initializer).declarations, this.checkVariableDeclaration); } else { this.checkExpression((node.initializer)); } } if (node.condition) this.checkExpression(node.condition); if (node.incrementor) this.checkExpression(node.incrementor); this.checkSourceElement(node.statement); if (node.locals) { this.registerForUnusedIdentifiersCheck(node); } } public checkForOfStatement(node: ForOfStatement): void { this.checkGrammarForInOrForOfStatement(node); if (node.kind === SyntaxKind.ForOfStatement) { if ((node).awaitModifier) { const functionFlags = getFunctionFlags(getContainingFunction(node)); if ((functionFlags & (FunctionFlags.Invalid | FunctionFlags.Async)) === FunctionFlags.Async && this.languageVersion < ScriptTarget.ESNext) { // for..await..of in an async function or async generator function prior to ESNext requires the __asyncValues helper this.checkExternalEmitHelpers(node, ExternalEmitHelpers.ForAwaitOfIncludes); } } else if (this.compilerOptions.downlevelIteration && this.languageVersion < ScriptTarget.ES2015) { // for..of prior to ES2015 requires the __values helper when downlevelIteration is enabled this.checkExternalEmitHelpers(node, ExternalEmitHelpers.ForOfIncludes); } } // Check the LHS and RHS // If the LHS is a declaration, just check it as a variable declaration, which will in turn check the RHS // via checkRightHandSideOfForOf. // If the LHS is an expression, check the LHS, as a destructuring assignment or as a reference. // Then check that the RHS is assignable to it. if (node.initializer.kind === SyntaxKind.VariableDeclarationList) { this.checkForInOrForOfVariableDeclaration(node); } else { const varExpr = node.initializer; const iteratedType = this.checkRightHandSideOfForOf(node.expression, node.awaitModifier); // There may be a destructuring assignment on the left side if (varExpr.kind === SyntaxKind.ArrayLiteralExpression || varExpr.kind === SyntaxKind.ObjectLiteralExpression) { // iteratedType may be undefined. In this case, we still want to check the structure of // varExpr, in particular making sure it's a valid LeftHandSideExpression. But we'd like // to short circuit the type relation checking as much as possible, so we pass the unknownType. this.checkDestructuringAssignment(varExpr, iteratedType || this.unknownType); } else { const leftType = this.checkExpression(varExpr); this.checkReferenceExpression(varExpr, Diagnostics.The_left_hand_side_of_a_for_of_statement_must_be_a_variable_or_a_property_access); // iteratedType will be undefined if the rightType 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. if (iteratedType) { this.checkTypeAssignableTo(iteratedType, leftType, varExpr, /*headMessage*/ undefined); } } } this.checkSourceElement(node.statement); if (node.locals) { this.registerForUnusedIdentifiersCheck(node); } } public checkForInStatement(node: ForInStatement) { // Grammar checking this.checkGrammarForInOrForOfStatement(node); const rightType = this.checkNonNullExpression(node.expression); // TypeScript 1.0 spec (April 2014): 5.4 // In a 'for-in' statement of the form // for (let VarDecl in Expr) Statement // VarDecl must be a variable declaration without a type annotation that declares a variable of type Any, // and Expr must be an expression of type Any, an object type, or a type parameter type. if (node.initializer.kind === SyntaxKind.VariableDeclarationList) { const variable = (node.initializer).declarations[0]; if (variable && isBindingPattern(variable.name)) { this.error(variable.name, Diagnostics.The_left_hand_side_of_a_for_in_statement_cannot_be_a_destructuring_pattern); } this.checkForInOrForOfVariableDeclaration(node); } else { // In a 'for-in' statement of the form // for (Var in Expr) Statement // Var must be an expression classified as a reference of type Any or the String primitive type, // and Expr must be an expression of type Any, an object type, or a type parameter type. const varExpr = node.initializer; const leftType = this.checkExpression(varExpr); if (varExpr.kind === SyntaxKind.ArrayLiteralExpression || varExpr.kind === SyntaxKind.ObjectLiteralExpression) { this.error(varExpr, Diagnostics.The_left_hand_side_of_a_for_in_statement_cannot_be_a_destructuring_pattern); } else if (!this.isTypeAssignableTo(this.getIndexTypeOrString(rightType), leftType)) { this.error(varExpr, Diagnostics.The_left_hand_side_of_a_for_in_statement_must_be_of_type_string_or_any); } else { // run check only former check succeeded to avoid cascading errors this.checkReferenceExpression(varExpr, Diagnostics.The_left_hand_side_of_a_for_in_statement_must_be_a_variable_or_a_property_access); } } // unknownType is returned i.e. if node.expression is identifier whose name cannot be resolved // in this case error about missing name is already reported - do not report extra one if (!this.isTypeAssignableToKind(rightType, TypeFlags.NonPrimitive | TypeFlags.TypeVariable)) { this.error(node.expression, Diagnostics.The_right_hand_side_of_a_for_in_statement_must_be_of_type_any_an_object_type_or_a_type_parameter); } this.checkSourceElement(node.statement); if (node.locals) { this.registerForUnusedIdentifiersCheck(node); } } public checkForInOrForOfVariableDeclaration(iterationStatement: ForInOrOfStatement): void { const variableDeclarationList = iterationStatement.initializer; // checkGrammarForInOrForOfStatement will check that there is exactly one declaration. if (variableDeclarationList.declarations.length >= 1) { const decl = variableDeclarationList.declarations[0]; this.checkVariableDeclaration(decl); } } public checkRightHandSideOfForOf(rhsExpression: Expression, awaitModifier: AwaitKeywordToken | undefined): Type { const expressionType = this.checkNonNullExpression(rhsExpression); return this.checkIteratedTypeOrElementType(expressionType, rhsExpression, /*allowStringInput*/ true, awaitModifier !== undefined); } public checkIteratedTypeOrElementType(inputType: Type, errorNode: Node, allowStringInput: boolean, allowAsyncIterables: boolean): Type { if (this.isTypeAny(inputType)) { return inputType; } return this.getIteratedTypeOrElementType(inputType, errorNode, allowStringInput, allowAsyncIterables, /*checkAssignability*/ true) || this.anyType; } /** * When consuming an iterable type in a for..of, spread, or iterator destructuring assignment * we want to get the iterated type of an iterable for ES2015 or later, or the iterated type * of a iterable (if defined globally) or element type of an array like for ES2015 or earlier. */ public getIteratedTypeOrElementType(inputType: Type, errorNode: Node, allowStringInput: boolean, allowAsyncIterables: boolean, checkAssignability: boolean): Type { const uplevelIteration = this.languageVersion >= ScriptTarget.ES2015; const downlevelIteration = !uplevelIteration && this.compilerOptions.downlevelIteration; // Get the iterated type of an `Iterable` or `IterableIterator` only in ES2015 // or higher, when inside of an async generator or for-await-if, or when // downlevelIteration is requested. if (uplevelIteration || downlevelIteration || allowAsyncIterables) { // We only report errors for an invalid iterable type in ES2015 or higher. const iteratedType = this.getIteratedTypeOfIterable(inputType, uplevelIteration ? errorNode : undefined, allowAsyncIterables, /*allowSyncIterables*/ true, checkAssignability); if (iteratedType || uplevelIteration) { return iteratedType; } } let arrayType = inputType; let reportedError = false; let hasStringConstituent = false; // If strings are permitted, remove any string-like constituents from the array type. // This allows us to find other non-string element types from an array unioned with // a string. if (allowStringInput) { if (arrayType.flags & TypeFlags.Union) { // After we remove all types that are StringLike, we will know if there was a string constituent // based on whether the result of filter is a new array. const arrayTypes = (inputType).types; const filteredTypes = filter(arrayTypes, t => !(t.flags & TypeFlags.StringLike)); if (filteredTypes !== arrayTypes) { arrayType = this.getUnionType(filteredTypes, /*subtypeReduction*/ true); } } else if (arrayType.flags & TypeFlags.StringLike) { arrayType = this.neverType; } hasStringConstituent = arrayType !== inputType; if (hasStringConstituent) { if (this.languageVersion < ScriptTarget.ES5) { if (errorNode) { this.error(errorNode, Diagnostics.Using_a_string_in_a_for_of_statement_is_only_supported_in_ECMAScript_5_and_higher); reportedError = true; } } // Now that we've removed all the StringLike types, if no constituents remain, then the entire // arrayOrStringType was a string. if (arrayType.flags & TypeFlags.Never) { return this.stringType; } } } if (!this.isArrayLikeType(arrayType)) { if (errorNode && !reportedError) { // Which error we report depends on whether we allow strings or if there was a // string constituent. For example, if the input type is number | string, we // want to say that number is not an array type. But if the input was just // number and string input is allowed, we want to say that number is not an // array type or a string type. const diagnostic = !allowStringInput || hasStringConstituent ? downlevelIteration ? Diagnostics.Type_0_is_not_an_array_type_or_does_not_have_a_Symbol_iterator_method_that_returns_an_iterator : Diagnostics.Type_0_is_not_an_array_type : downlevelIteration ? Diagnostics.Type_0_is_not_an_array_type_or_a_string_type_or_does_not_have_a_Symbol_iterator_method_that_returns_an_iterator : Diagnostics.Type_0_is_not_an_array_type_or_a_string_type; this.error(errorNode, diagnostic, this.typeToString(arrayType)); } return hasStringConstituent ? this.stringType : undefined; } const arrayElementType = this.getIndexTypeOfType(arrayType, IndexKind.Number); if (hasStringConstituent && arrayElementType) { // This is just an optimization for the case where arrayOrStringType is string | string[] if (arrayElementType.flags & TypeFlags.StringLike) { return this.stringType; } return this.getUnionType([arrayElementType, this.stringType], /*subtypeReduction*/ true); } return arrayElementType; } /** * We want to treat type as an iterable, and get the type it is an iterable of. The iterable * must have the following structure (annotated with the names of the variables below): * * { // iterable * [Symbol.iterator]: { // iteratorMethod * (): Iterator * } * } * * For an async iterable, we expect the following structure: * * { // iterable * [Symbol.asyncIterator]: { // iteratorMethod * (): AsyncIterator * } * } * * T is the type we are after. At every level that involves analyzing return types * of signatures, we union the return types of all the signatures. * * Another thing to note is that at any step of this process, we could run into a dead end, * meaning either the property is missing, or we run into the anyType. If either of these things * happens, we return undefined to signal that we could not find the iterated type. If a property * is missing, and the previous step did not result in 'any', then we also give an error if the * caller requested it. Then the caller can decide what to do in the case where there is no iterated * type. This is different from returning anyType, because that would signify that we have matched the * whole pattern and that T (above) is 'any'. * * For a **for-of** statement, `yield*` (in a normal generator), spread, array * destructuring, or normal generator we will only ever look for a `[Symbol.iterator]()` * method. * * For an async generator we will only ever look at the `[Symbol.asyncIterator]()` method. * * For a **for-await-of** statement or a `yield*` in an async generator we will look for * the `[Symbol.asyncIterator]()` method first, and then the `[Symbol.iterator]()` method. */ public getIteratedTypeOfIterable(type: Type, errorNode: Node | undefined, allowAsyncIterables: boolean, allowSyncIterables: boolean, checkAssignability: boolean): Type | undefined { const __conv_self__ = this; if (this.isTypeAny(type)) { return undefined; } return this.mapType(type, getIteratedType); function getIteratedType(type: Type) { const typeAsIterable = type; if (allowAsyncIterables) { if (typeAsIterable.iteratedTypeOfAsyncIterable) { return typeAsIterable.iteratedTypeOfAsyncIterable; } // As an optimization, if the type is an instantiation of the global `AsyncIterable` // or the global `AsyncIterableIterator` then just grab its type argument. if (__conv_self__.isReferenceToType(type, __conv_self__.getGlobalAsyncIterableType(/*reportErrors*/ false)) || __conv_self__.isReferenceToType(type, __conv_self__.getGlobalAsyncIterableIteratorType(/*reportErrors*/ false))) { return typeAsIterable.iteratedTypeOfAsyncIterable = (type).typeArguments[0]; } } if (allowSyncIterables) { if (typeAsIterable.iteratedTypeOfIterable) { return typeAsIterable.iteratedTypeOfIterable; } // As an optimization, if the type is an instantiation of the global `Iterable` or // `IterableIterator` then just grab its type argument. if (__conv_self__.isReferenceToType(type, __conv_self__.getGlobalIterableType(/*reportErrors*/ false)) || __conv_self__.isReferenceToType(type, __conv_self__.getGlobalIterableIteratorType(/*reportErrors*/ false))) { return typeAsIterable.iteratedTypeOfIterable = (type).typeArguments[0]; } } const asyncMethodType = allowAsyncIterables && __conv_self__.getTypeOfPropertyOfType(type, getPropertyNameForKnownSymbolName("asyncIterator")); const methodType = asyncMethodType || (allowSyncIterables && __conv_self__.getTypeOfPropertyOfType(type, getPropertyNameForKnownSymbolName("iterator"))); if (__conv_self__.isTypeAny(methodType)) { return undefined; } const signatures = methodType && __conv_self__.getSignaturesOfType(methodType, SignatureKind.Call); if (!some(signatures)) { if (errorNode) { __conv_self__.error(errorNode, allowAsyncIterables ? Diagnostics.Type_must_have_a_Symbol_asyncIterator_method_that_returns_an_async_iterator : Diagnostics.Type_must_have_a_Symbol_iterator_method_that_returns_an_iterator); // only report on the first error errorNode = undefined; } return undefined; } const returnType = __conv_self__.getUnionType(map(signatures, __conv_self__.getReturnTypeOfSignature), /*subtypeReduction*/ true); const iteratedType = __conv_self__.getIteratedTypeOfIterator(returnType, errorNode, /*isAsyncIterator*/ !!asyncMethodType); if (checkAssignability && errorNode && iteratedType) { // If `checkAssignability` was specified, we were called from // `checkIteratedTypeOrElementType`. As such, we need to validate that // the type passed in is actually an Iterable. __conv_self__.checkTypeAssignableTo(type, asyncMethodType ? __conv_self__.createAsyncIterableType(iteratedType) : __conv_self__.createIterableType(iteratedType), errorNode); } return asyncMethodType ? typeAsIterable.iteratedTypeOfAsyncIterable = iteratedType : typeAsIterable.iteratedTypeOfIterable = iteratedType; } } /** * This function has very similar logic as getIteratedTypeOfIterable, except that it operates on * Iterators instead of Iterables. Here is the structure: * * { // iterator * next: { // nextMethod * (): { // nextResult * value: T // nextValue * } * } * } * * For an async iterator, we expect the following structure: * * { // iterator * next: { // nextMethod * (): PromiseLike<{ // nextResult * value: T // nextValue * }> * } * } */ public getIteratedTypeOfIterator(type: Type, errorNode: Node | undefined, isAsyncIterator: boolean): Type | undefined { if (this.isTypeAny(type)) { return undefined; } const typeAsIterator = type; if (isAsyncIterator ? typeAsIterator.iteratedTypeOfAsyncIterator : typeAsIterator.iteratedTypeOfIterator) { return isAsyncIterator ? typeAsIterator.iteratedTypeOfAsyncIterator : typeAsIterator.iteratedTypeOfIterator; } // As an optimization, if the type is an instantiation of the global `Iterator` (for // a non-async iterator) or the global `AsyncIterator` (for an async-iterator) then // just grab its type argument. const getIteratorType = isAsyncIterator ? this.getGlobalAsyncIteratorType : this.getGlobalIteratorType; if (this.isReferenceToType(type, getIteratorType(/*reportErrors*/ false))) { return isAsyncIterator ? typeAsIterator.iteratedTypeOfAsyncIterator = (type).typeArguments[0] : typeAsIterator.iteratedTypeOfIterator = (type).typeArguments[0]; } // Both async and non-async iterators must have a `next` method. const nextMethod = this.getTypeOfPropertyOfType(type, ("next" as __String)); if (this.isTypeAny(nextMethod)) { return undefined; } const nextMethodSignatures = nextMethod ? this.getSignaturesOfType(nextMethod, SignatureKind.Call) : emptyArray; if (nextMethodSignatures.length === 0) { if (errorNode) { this.error(errorNode, isAsyncIterator ? Diagnostics.An_async_iterator_must_have_a_next_method : Diagnostics.An_iterator_must_have_a_next_method); } return undefined; } let nextResult = this.getUnionType(map(nextMethodSignatures, this.getReturnTypeOfSignature), /*subtypeReduction*/ true); if (this.isTypeAny(nextResult)) { return undefined; } // For an async iterator, we must get the awaited type of the return type. if (isAsyncIterator) { nextResult = this.getAwaitedTypeOfPromise(nextResult, errorNode, Diagnostics.The_type_returned_by_the_next_method_of_an_async_iterator_must_be_a_promise_for_a_type_with_a_value_property); if (this.isTypeAny(nextResult)) { return undefined; } } const nextValue = nextResult && this.getTypeOfPropertyOfType(nextResult, ("value" as __String)); if (!nextValue) { if (errorNode) { this.error(errorNode, isAsyncIterator ? Diagnostics.The_type_returned_by_the_next_method_of_an_async_iterator_must_be_a_promise_for_a_type_with_a_value_property : Diagnostics.The_type_returned_by_the_next_method_of_an_iterator_must_have_a_value_property); } return undefined; } return isAsyncIterator ? typeAsIterator.iteratedTypeOfAsyncIterator = nextValue : typeAsIterator.iteratedTypeOfIterator = nextValue; } /** * A generator may have a return type of `Iterator`, `Iterable`, or * `IterableIterator`. An async generator may have a return type of `AsyncIterator`, * `AsyncIterable`, or `AsyncIterableIterator`. This function can be used to extract * the iterated type from this return type for contextual typing and verifying signatures. */ public getIteratedTypeOfGenerator(returnType: Type, isAsyncGenerator: boolean): Type { if (this.isTypeAny(returnType)) { return undefined; } return this.getIteratedTypeOfIterable(returnType, /*errorNode*/ undefined, /*allowAsyncIterables*/ isAsyncGenerator, /*allowSyncIterables*/ !isAsyncGenerator, /*checkAssignability*/ false) || this.getIteratedTypeOfIterator(returnType, /*errorNode*/ undefined, isAsyncGenerator); } public checkBreakOrContinueStatement(node: BreakOrContinueStatement) { // Grammar checking this.checkGrammarStatementInAmbientContext(node) || this.checkGrammarBreakOrContinueStatement(node); // TODO: Check that target label is valid } public isGetAccessorWithAnnotatedSetAccessor(node: FunctionLike) { return node.kind === SyntaxKind.GetAccessor && getEffectiveSetAccessorTypeAnnotationNode(getDeclarationOfKind(node.symbol, SyntaxKind.SetAccessor)) !== undefined; } public isUnwrappedReturnTypeVoidOrAny(func: FunctionLike, returnType: Type): boolean { const unwrappedReturnType = (getFunctionFlags(func) & FunctionFlags.AsyncGenerator) === FunctionFlags.Async ? this.getPromisedTypeOfPromise(returnType) // Async function : returnType; // AsyncGenerator function, Generator function, or normal function return unwrappedReturnType && this.maybeTypeOfKind(unwrappedReturnType, TypeFlags.Void | TypeFlags.Any); } public checkReturnStatement(node: ReturnStatement) { // Grammar checking if (!this.checkGrammarStatementInAmbientContext(node)) { const functionBlock = getContainingFunction(node); if (!functionBlock) { this.grammarErrorOnFirstToken(node, Diagnostics.A_return_statement_can_only_be_used_within_a_function_body); } } const func = getContainingFunction(node); if (func) { const signature = this.getSignatureFromDeclaration(func); const returnType = this.getReturnTypeOfSignature(signature); if (this.strictNullChecks || node.expression || returnType.flags & TypeFlags.Never) { const exprType = node.expression ? this.checkExpressionCached(node.expression) : this.undefinedType; const functionFlags = getFunctionFlags(func); if (functionFlags & FunctionFlags.Generator) { // A generator does not need its return expressions checked against its return type. // Instead, the yield expressions are checked against the element type. // TODO: Check return expressions of generators when return type tracking is added // for generators. return; } if (func.kind === SyntaxKind.SetAccessor) { if (node.expression) { this.error(node, Diagnostics.Setters_cannot_return_a_value); } } else if (func.kind === SyntaxKind.Constructor) { if (node.expression && !this.checkTypeAssignableTo(exprType, returnType, node)) { this.error(node, Diagnostics.Return_type_of_constructor_signature_must_be_assignable_to_the_instance_type_of_the_class); } } else if (getEffectiveReturnTypeNode(func) || this.isGetAccessorWithAnnotatedSetAccessor(func)) { if (functionFlags & FunctionFlags.Async) { const promisedType = this.getPromisedTypeOfPromise(returnType); const awaitedType = this.checkAwaitedType(exprType, node, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member); if (promisedType) { // If the function has a return type, but promisedType is // undefined, an error will be reported in checkAsyncFunctionReturnType // so we don't need to report one here. this.checkTypeAssignableTo(awaitedType, promisedType, node); } } else { this.checkTypeAssignableTo(exprType, returnType, node); } } } else if (func.kind !== SyntaxKind.Constructor && this.compilerOptions.noImplicitReturns && !this.isUnwrappedReturnTypeVoidOrAny(func, returnType)) { // The function has a return type, but the return statement doesn't have an expression. this.error(node, Diagnostics.Not_all_code_paths_return_a_value); } } } public checkWithStatement(node: WithStatement) { // Grammar checking for withStatement if (!this.checkGrammarStatementInAmbientContext(node)) { if (node.flags & NodeFlags.AwaitContext) { this.grammarErrorOnFirstToken(node, Diagnostics.with_statements_are_not_allowed_in_an_async_function_block); } } this.checkExpression(node.expression); const sourceFile = getSourceFileOfNode(node); if (!this.hasParseDiagnostics(sourceFile)) { const start = getSpanOfTokenAtPosition(sourceFile, node.pos).start; const end = node.statement.pos; this.grammarErrorAtPos(sourceFile, start, end - start, Diagnostics.The_with_statement_is_not_supported_All_symbols_in_a_with_block_will_have_type_any); } } public checkSwitchStatement(node: SwitchStatement) { const __conv_self__ = this; // Grammar checking this.checkGrammarStatementInAmbientContext(node); let firstDefaultClause: CaseOrDefaultClause; let hasDuplicateDefaultClause = false; const expressionType = this.checkExpression(node.expression); const expressionIsLiteral = this.isLiteralType(expressionType); forEach(node.caseBlock.clauses, clause => { // Grammar check for duplicate default clauses, skip if we already report duplicate default clause if (clause.kind === SyntaxKind.DefaultClause && !hasDuplicateDefaultClause) { if (firstDefaultClause === undefined) { firstDefaultClause = clause; } else { const sourceFile = getSourceFileOfNode(node); const start = skipTrivia(sourceFile.text, clause.pos); const end = clause.statements.length > 0 ? clause.statements[0].pos : clause.end; __conv_self__.grammarErrorAtPos(sourceFile, start, end - start, Diagnostics.A_default_clause_cannot_appear_more_than_once_in_a_switch_statement); hasDuplicateDefaultClause = true; } } if (__conv_self__.produceDiagnostics && clause.kind === SyntaxKind.CaseClause) { const caseClause = clause; // TypeScript 1.0 spec (April 2014): 5.9 // In a 'switch' statement, each 'case' expression must be of a type that is comparable // to or from the type of the 'switch' expression. let caseType = __conv_self__.checkExpression(caseClause.expression); const caseIsLiteral = __conv_self__.isLiteralType(caseType); let comparedExpressionType = expressionType; if (!caseIsLiteral || !expressionIsLiteral) { caseType = caseIsLiteral ? __conv_self__.getBaseTypeOfLiteralType(caseType) : caseType; comparedExpressionType = __conv_self__.getBaseTypeOfLiteralType(expressionType); } if (!__conv_self__.isTypeEqualityComparableTo(comparedExpressionType, caseType)) { // expressionType is not comparable to caseType, try the reversed check and report errors if it fails __conv_self__.checkTypeComparableTo(caseType, comparedExpressionType, caseClause.expression, /*headMessage*/ undefined); } } forEach(clause.statements, __conv_self__.checkSourceElement); }); if (node.caseBlock.locals) { this.registerForUnusedIdentifiersCheck(node.caseBlock); } } public checkLabeledStatement(node: LabeledStatement) { const __conv_self__ = this; // Grammar checking if (!this.checkGrammarStatementInAmbientContext(node)) { findAncestor(node.parent, current => { if (isFunctionLike(current)) { return "quit"; } if (current.kind === SyntaxKind.LabeledStatement && (current).label.escapedText === node.label.escapedText) { const sourceFile = getSourceFileOfNode(node); __conv_self__.grammarErrorOnNode(node.label, Diagnostics.Duplicate_label_0, getTextOfNodeFromSourceText(sourceFile.text, node.label)); return true; } }); } // ensure that label is unique this.checkSourceElement(node.statement); } public checkThrowStatement(node: ThrowStatement) { // Grammar checking if (!this.checkGrammarStatementInAmbientContext(node)) { if (node.expression === undefined) { this.grammarErrorAfterFirstToken(node, Diagnostics.Line_break_not_permitted_here); } } if (node.expression) { this.checkExpression(node.expression); } } public checkTryStatement(node: TryStatement) { const __conv_self__ = this; // Grammar checking this.checkGrammarStatementInAmbientContext(node); this.checkBlock(node.tryBlock); const catchClause = node.catchClause; if (catchClause) { // Grammar checking if (catchClause.variableDeclaration) { if (catchClause.variableDeclaration.type) { this.grammarErrorOnFirstToken(catchClause.variableDeclaration.type, Diagnostics.Catch_clause_variable_cannot_have_a_type_annotation); } else if (catchClause.variableDeclaration.initializer) { this.grammarErrorOnFirstToken(catchClause.variableDeclaration.initializer, Diagnostics.Catch_clause_variable_cannot_have_an_initializer); } else { const blockLocals = catchClause.block.locals; if (blockLocals) { forEachKey(catchClause.locals, caughtName => { const blockLocal = blockLocals.get(caughtName); if (blockLocal && (blockLocal.flags & SymbolFlags.BlockScopedVariable) !== 0) { __conv_self__.grammarErrorOnNode(blockLocal.valueDeclaration, Diagnostics.Cannot_redeclare_identifier_0_in_catch_clause, caughtName); } }); } } } this.checkBlock(catchClause.block); } if (node.finallyBlock) { this.checkBlock(node.finallyBlock); } } public checkIndexConstraints(type: Type) { const __conv_self__ = this; const declaredNumberIndexer = this.getIndexDeclarationOfSymbol(type.symbol, IndexKind.Number); const declaredStringIndexer = this.getIndexDeclarationOfSymbol(type.symbol, IndexKind.String); const stringIndexType = this.getIndexTypeOfType(type, IndexKind.String); const numberIndexType = this.getIndexTypeOfType(type, IndexKind.Number); if (stringIndexType || numberIndexType) { forEach(this.getPropertiesOfObjectType(type), prop => { const propType = __conv_self__.getTypeOfSymbol(prop); checkIndexConstraintForProperty(prop, propType, type, declaredStringIndexer, stringIndexType, IndexKind.String); checkIndexConstraintForProperty(prop, propType, type, declaredNumberIndexer, numberIndexType, IndexKind.Number); }); if (this.getObjectFlags(type) & ObjectFlags.Class && isClassLike(type.symbol.valueDeclaration)) { const classDeclaration = type.symbol.valueDeclaration; for (const member of classDeclaration.members) { // Only process instance properties with computed names here. // Static properties cannot be in conflict with indexers, // and properties with literal names were already checked. if (!hasModifier(member, ModifierFlags.Static) && hasDynamicName(member)) { const propType = this.getTypeOfSymbol(member.symbol); checkIndexConstraintForProperty(member.symbol, propType, type, declaredStringIndexer, stringIndexType, IndexKind.String); checkIndexConstraintForProperty(member.symbol, propType, type, declaredNumberIndexer, numberIndexType, IndexKind.Number); } } } } let errorNode: Node; if (stringIndexType && numberIndexType) { errorNode = declaredNumberIndexer || declaredStringIndexer; // condition 'errorNode === undefined' may appear if types does not declare nor string neither number indexer if (!errorNode && (this.getObjectFlags(type) & ObjectFlags.Interface)) { const someBaseTypeHasBothIndexers = forEach(this.getBaseTypes((type)), base => __conv_self__.getIndexTypeOfType(base, IndexKind.String) && __conv_self__.getIndexTypeOfType(base, IndexKind.Number)); errorNode = someBaseTypeHasBothIndexers ? undefined : type.symbol.declarations[0]; } } if (errorNode && !this.isTypeAssignableTo(numberIndexType, stringIndexType)) { this.error(errorNode, Diagnostics.Numeric_index_type_0_is_not_assignable_to_string_index_type_1, this.typeToString(numberIndexType), this.typeToString(stringIndexType)); } function checkIndexConstraintForProperty(prop: Symbol, propertyType: Type, containingType: Type, indexDeclaration: Declaration, indexType: Type, indexKind: IndexKind): void { if (!indexType) { return; } const propDeclaration = prop.valueDeclaration; // index is numeric and property name is not valid numeric literal if (indexKind === IndexKind.Number && !(propDeclaration ? __conv_self__.isNumericName(getNameOfDeclaration(propDeclaration)) : __conv_self__.isNumericLiteralName(prop.escapedName))) { return; } // perform property check if property or indexer is declared in 'type' // this allows us to rule out cases when both property and indexer are inherited from the base class let errorNode: Node; if (propDeclaration && (propDeclaration.kind === SyntaxKind.BinaryExpression || getNameOfDeclaration(propDeclaration).kind === SyntaxKind.ComputedPropertyName || prop.parent === containingType.symbol)) { errorNode = propDeclaration; } else if (indexDeclaration) { errorNode = indexDeclaration; } else if (__conv_self__.getObjectFlags(containingType) & ObjectFlags.Interface) { // for interfaces property and indexer might be inherited from different bases // check if any base class already has both property and indexer. // check should be performed only if 'type' is the first type that brings property\indexer together const someBaseClassHasBothPropertyAndIndexer = forEach(__conv_self__.getBaseTypes((containingType)), base => __conv_self__.getPropertyOfObjectType(base, prop.escapedName) && __conv_self__.getIndexTypeOfType(base, indexKind)); errorNode = someBaseClassHasBothPropertyAndIndexer ? undefined : containingType.symbol.declarations[0]; } if (errorNode && !__conv_self__.isTypeAssignableTo(propertyType, indexType)) { const errorMessage = indexKind === IndexKind.String ? Diagnostics.Property_0_of_type_1_is_not_assignable_to_string_index_type_2 : Diagnostics.Property_0_of_type_1_is_not_assignable_to_numeric_index_type_2; __conv_self__.error(errorNode, errorMessage, __conv_self__.symbolToString(prop), __conv_self__.typeToString(propertyType), __conv_self__.typeToString(indexType)); } } } public checkTypeNameIsReserved(name: Identifier, message: DiagnosticMessage): void { // TS 1.0 spec (April 2014): 3.6.1 // The predefined type keywords are reserved and cannot be used as names of user defined types. switch (name.escapedText) { case "any": case "number": case "boolean": case "string": case "symbol": case "void": case "object": this.error(name, message, ((name).escapedText as string)); } } /** * Check each type parameter and check that type parameters have no duplicate type parameter declarations */ public checkTypeParameters(typeParameterDeclarations: ReadonlyArray) { if (typeParameterDeclarations) { let seenDefault = false; for (let i = 0; i < typeParameterDeclarations.length; i++) { const node = typeParameterDeclarations[i]; this.checkTypeParameter(node); if (this.produceDiagnostics) { if (node.default) { seenDefault = true; } else if (seenDefault) { this.error(node, Diagnostics.Required_type_parameters_may_not_follow_optional_type_parameters); } for (let j = 0; j < i; j++) { if (typeParameterDeclarations[j].symbol === node.symbol) { this.error(node.name, Diagnostics.Duplicate_identifier_0, declarationNameToString(node.name)); } } } } } } /** Check that type parameter lists are identical across multiple declarations */ public checkTypeParameterListsIdentical(symbol: Symbol) { if (symbol.declarations.length === 1) { return; } const links = this.getSymbolLinks(symbol); if (!links.typeParametersChecked) { links.typeParametersChecked = true; const declarations = this.getClassOrInterfaceDeclarationsOfSymbol(symbol); if (declarations.length <= 1) { return; } const type = (this.getDeclaredTypeOfSymbol(symbol)); if (!this.areTypeParametersIdentical(declarations, type.localTypeParameters)) { // Report an error on every conflicting declaration. const name = this.symbolToString(symbol); for (const declaration of declarations) { this.error(declaration.name, Diagnostics.All_declarations_of_0_must_have_identical_type_parameters, name); } } } } public areTypeParametersIdentical(declarations: ReadonlyArray, typeParameters: TypeParameter[]) { const maxTypeArgumentCount = length(typeParameters); const minTypeArgumentCount = this.getMinTypeArgumentCount(typeParameters); for (const declaration of declarations) { // If this declaration has too few or too many type parameters, we report an error const numTypeParameters = length(declaration.typeParameters); if (numTypeParameters < minTypeArgumentCount || numTypeParameters > maxTypeArgumentCount) { return false; } for (let i = 0; i < numTypeParameters; i++) { const source = declaration.typeParameters[i]; const target = typeParameters[i]; // If the type parameter node does not have the same as the resolved type // parameter at this position, we report an error. if (source.name.escapedText !== target.symbol.escapedName) { return false; } // If the type parameter node does not have an identical constraint as the resolved // type parameter at this position, we report an error. const sourceConstraint = source.constraint && this.getTypeFromTypeNode(source.constraint); const targetConstraint = this.getConstraintFromTypeParameter(target); if ((sourceConstraint || targetConstraint) && (!sourceConstraint || !targetConstraint || !this.isTypeIdenticalTo(sourceConstraint, targetConstraint))) { return false; } // If the type parameter node has a default and it is not identical to the default // for the type parameter at this position, we report an error. const sourceDefault = source.default && this.getTypeFromTypeNode(source.default); const targetDefault = this.getDefaultFromTypeParameter(target); if (sourceDefault && targetDefault && !this.isTypeIdenticalTo(sourceDefault, targetDefault)) { return false; } } } return true; } public checkClassExpression(node: ClassExpression): Type { this.checkClassLikeDeclaration(node); this.checkNodeDeferred(node); return this.getTypeOfSymbol(this.getSymbolOfNode(node)); } public checkClassExpressionDeferred(node: ClassExpression) { forEach(node.members, this.checkSourceElement); this.registerForUnusedIdentifiersCheck(node); } public checkClassDeclaration(node: ClassDeclaration) { if (!node.name && !hasModifier(node, ModifierFlags.Default)) { this.grammarErrorOnFirstToken(node, Diagnostics.A_class_declaration_without_the_default_modifier_must_have_a_name); } this.checkClassLikeDeclaration(node); forEach(node.members, this.checkSourceElement); this.registerForUnusedIdentifiersCheck(node); } public checkClassLikeDeclaration(node: ClassLikeDeclaration) { const __conv_self__ = this; this.checkGrammarClassLikeDeclaration(node); this.checkDecorators(node); if (node.name) { this.checkTypeNameIsReserved(node.name, Diagnostics.Class_name_cannot_be_0); this.checkCollisionWithCapturedThisVariable(node, node.name); this.checkCollisionWithCapturedNewTargetVariable(node, node.name); this.checkCollisionWithRequireExportsInGeneratedCode(node, node.name); this.checkCollisionWithGlobalPromiseInGeneratedCode(node, node.name); } this.checkTypeParameters(node.typeParameters); this.checkExportsOnMergedDeclarations(node); const symbol = this.getSymbolOfNode(node); const type = (this.getDeclaredTypeOfSymbol(symbol)); const typeWithThis = this.getTypeWithThisArgument(type); const staticType = (this.getTypeOfSymbol(symbol)); this.checkTypeParameterListsIdentical(symbol); this.checkClassForDuplicateDeclarations(node); // Only check for reserved static identifiers on non-ambient context. if (!isInAmbientContext(node)) { this.checkClassForStaticPropertyNameConflicts(node); } const baseTypeNode = getClassExtendsHeritageClauseElement(node); if (baseTypeNode) { if (this.languageVersion < ScriptTarget.ES2015) { this.checkExternalEmitHelpers(baseTypeNode.parent, ExternalEmitHelpers.Extends); } const baseTypes = this.getBaseTypes(type); if (baseTypes.length && this.produceDiagnostics) { const baseType = baseTypes[0]; const baseConstructorType = this.getBaseConstructorTypeOfClass(type); const staticBaseType = this.getApparentType(baseConstructorType); this.checkBaseTypeAccessibility(staticBaseType, baseTypeNode); this.checkSourceElement(baseTypeNode.expression); if (some(baseTypeNode.typeArguments)) { forEach(baseTypeNode.typeArguments, this.checkSourceElement); for (const constructor of this.getConstructorsForTypeArguments(staticBaseType, baseTypeNode.typeArguments, baseTypeNode)) { if (!this.checkTypeArgumentConstraints(constructor.typeParameters, baseTypeNode.typeArguments)) { break; } } } this.checkTypeAssignableTo(typeWithThis, this.getTypeWithThisArgument(baseType, type.thisType), node.name || node, Diagnostics.Class_0_incorrectly_extends_base_class_1); this.checkTypeAssignableTo(staticType, this.getTypeWithoutSignatures(staticBaseType), node.name || node, Diagnostics.Class_static_side_0_incorrectly_extends_base_class_static_side_1); if (baseConstructorType.flags & TypeFlags.TypeVariable && !this.isMixinConstructorType(staticType)) { this.error(node.name || node, Diagnostics.A_mixin_class_must_have_a_constructor_with_a_single_rest_parameter_of_type_any); } if (!(staticBaseType.symbol && staticBaseType.symbol.flags & SymbolFlags.Class) && !(baseConstructorType.flags & TypeFlags.TypeVariable)) { // When the static base type is a "class-like" constructor function (but not actually a class), we verify // that all instantiated base constructor signatures return the same type. We can simply compare the type // references (as opposed to checking the structure of the types) because elsewhere we have already checked // that the base type is a class or interface type (and not, for example, an anonymous object type). const constructors = this.getInstantiatedConstructorsForTypeArguments(staticBaseType, baseTypeNode.typeArguments, baseTypeNode); if (forEach(constructors, sig => __conv_self__.getReturnTypeOfSignature(sig) !== baseType)) { this.error(baseTypeNode.expression, Diagnostics.Base_constructors_must_all_have_the_same_return_type); } } this.checkKindsOfPropertyMemberOverrides(type, baseType); } } const implementedTypeNodes = getClassImplementsHeritageClauseElements(node); if (implementedTypeNodes) { for (const typeRefNode of implementedTypeNodes) { if (!isEntityNameExpression(typeRefNode.expression)) { this.error(typeRefNode.expression, Diagnostics.A_class_can_only_implement_an_identifier_Slashqualified_name_with_optional_type_arguments); } this.checkTypeReferenceNode(typeRefNode); if (this.produceDiagnostics) { const t = this.getTypeFromTypeNode(typeRefNode); if (t !== this.unknownType) { if (this.isValidBaseType(t)) { this.checkTypeAssignableTo(typeWithThis, this.getTypeWithThisArgument(t, type.thisType), node.name || node, Diagnostics.Class_0_incorrectly_implements_interface_1); } else { this.error(typeRefNode, Diagnostics.A_class_may_only_implement_another_class_or_interface); } } } } } if (this.produceDiagnostics) { this.checkIndexConstraints(type); this.checkTypeForDuplicateIndexSignatures(node); } } public checkBaseTypeAccessibility(type: Type, node: ExpressionWithTypeArguments) { const signatures = this.getSignaturesOfType(type, SignatureKind.Construct); if (signatures.length) { const declaration = signatures[0].declaration; if (declaration && hasModifier(declaration, ModifierFlags.Private)) { const typeClassDeclaration = (this.getClassLikeDeclarationOfSymbol(type.symbol)); if (!this.isNodeWithinClass(node, typeClassDeclaration)) { this.error(node, Diagnostics.Cannot_extend_a_class_0_Class_constructor_is_marked_as_private, this.getFullyQualifiedName(type.symbol)); } } } } public getTargetSymbol(s: Symbol) { // if symbol is instantiated its flags are not copied from the 'target' // so we'll need to get back original 'target' symbol to work with correct set of flags return getCheckFlags(s) & CheckFlags.Instantiated ? (s).target : s; } public getClassLikeDeclarationOfSymbol(symbol: Symbol): Declaration { return forEach(symbol.declarations, d => isClassLike(d) ? d : undefined); } public getClassOrInterfaceDeclarationsOfSymbol(symbol: Symbol) { return filter(symbol.declarations, (d: Declaration): d is ClassDeclaration | InterfaceDeclaration => d.kind === SyntaxKind.ClassDeclaration || d.kind === SyntaxKind.InterfaceDeclaration); } public checkKindsOfPropertyMemberOverrides(type: InterfaceType, baseType: BaseType): void { // TypeScript 1.0 spec (April 2014): 8.2.3 // A derived class inherits all members from its base class it doesn't override. // Inheritance means that a derived class implicitly contains all non - overridden members of the base class. // Both public and private property members are inherited, but only public property members can be overridden. // A property member in a derived class is said to override a property member in a base class // when the derived class property member has the same name and kind(instance or static) // as the base class property member. // The type of an overriding property member must be assignable(section 3.8.4) // to the type of the overridden property member, or otherwise a compile - time error occurs. // Base class instance member functions can be overridden by derived class instance member functions, // but not by other kinds of members. // Base class instance member variables and accessors can be overridden by // derived class instance member variables and accessors, but not by other kinds of members. // NOTE: assignability is checked in checkClassDeclaration const baseProperties = this.getPropertiesOfType(baseType); for (const baseProperty of baseProperties) { const base = this.getTargetSymbol(baseProperty); if (base.flags & SymbolFlags.Prototype) { continue; } const derived = this.getTargetSymbol(this.getPropertyOfObjectType(type, base.escapedName)); const baseDeclarationFlags = getDeclarationModifierFlagsFromSymbol(base); Debug.assert(!!derived, "derived should point to something, even if it is the base class' declaration."); if (derived) { // In order to resolve whether the inherited method was overridden in the base class or not, // we compare the Symbols obtained. Since getTargetSymbol returns the symbol on the *uninstantiated* // type declaration, derived and base resolve to the same symbol even in the case of generic classes. if (derived === base) { // derived class inherits base without override/redeclaration const derivedClassDecl = this.getClassLikeDeclarationOfSymbol(type.symbol); // It is an error to inherit an abstract member without implementing it or being declared abstract. // If there is no declaration for the derived class (as in the case of class expressions), // then the class cannot be declared abstract. if (baseDeclarationFlags & ModifierFlags.Abstract && (!derivedClassDecl || !hasModifier(derivedClassDecl, ModifierFlags.Abstract))) { if (derivedClassDecl.kind === SyntaxKind.ClassExpression) { this.error(derivedClassDecl, Diagnostics.Non_abstract_class_expression_does_not_implement_inherited_abstract_member_0_from_class_1, this.symbolToString(baseProperty), this.typeToString(baseType)); } else { this.error(derivedClassDecl, Diagnostics.Non_abstract_class_0_does_not_implement_inherited_abstract_member_1_from_class_2, this.typeToString(type), this.symbolToString(baseProperty), this.typeToString(baseType)); } } } else { // derived overrides base. const derivedDeclarationFlags = getDeclarationModifierFlagsFromSymbol(derived); if (baseDeclarationFlags & ModifierFlags.Private || derivedDeclarationFlags & ModifierFlags.Private) { // either base or derived property is private - not override, skip it continue; } if (this.isMethodLike(base) && this.isMethodLike(derived) || base.flags & SymbolFlags.PropertyOrAccessor && derived.flags & SymbolFlags.PropertyOrAccessor) { // method is overridden with method or property/accessor is overridden with property/accessor - correct case continue; } let errorMessage: DiagnosticMessage; if (this.isMethodLike(base)) { if (derived.flags & SymbolFlags.Accessor) { errorMessage = Diagnostics.Class_0_defines_instance_member_function_1_but_extended_class_2_defines_it_as_instance_member_accessor; } else { errorMessage = Diagnostics.Class_0_defines_instance_member_function_1_but_extended_class_2_defines_it_as_instance_member_property; } } else if (base.flags & SymbolFlags.Property) { errorMessage = Diagnostics.Class_0_defines_instance_member_property_1_but_extended_class_2_defines_it_as_instance_member_function; } else { errorMessage = Diagnostics.Class_0_defines_instance_member_accessor_1_but_extended_class_2_defines_it_as_instance_member_function; } this.error(getNameOfDeclaration(derived.valueDeclaration) || derived.valueDeclaration, errorMessage, this.typeToString(baseType), this.symbolToString(base), this.typeToString(type)); } } } } public checkInheritedPropertiesAreIdentical(type: InterfaceType, typeNode: Node): boolean { const baseTypes = this.getBaseTypes(type); if (baseTypes.length < 2) { return true; } type InheritanceInfoMap = { prop: Symbol; containingType: Type; }; const seen = createUnderscoreEscapedMap(); forEach(this.resolveDeclaredMembers(type).declaredProperties, p => { seen.set(p.escapedName, { prop: p, containingType: type }); }); let ok = true; for (const base of baseTypes) { const properties = this.getPropertiesOfType(this.getTypeWithThisArgument(base, type.thisType)); for (const prop of properties) { const existing = seen.get(prop.escapedName); if (!existing) { seen.set(prop.escapedName, { prop, containingType: base }); } else { const isInheritedProperty = existing.containingType !== type; if (isInheritedProperty && !this.isPropertyIdenticalTo(existing.prop, prop)) { ok = false; const typeName1 = this.typeToString(existing.containingType); const typeName2 = this.typeToString(base); let errorInfo = chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Named_property_0_of_types_1_and_2_are_not_identical, this.symbolToString(prop), typeName1, typeName2); errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Interface_0_cannot_simultaneously_extend_types_1_and_2, this.typeToString(type), typeName1, typeName2); this.diagnostics.add(createDiagnosticForNodeFromMessageChain(typeNode, errorInfo)); } } } } return ok; } public checkInterfaceDeclaration(node: InterfaceDeclaration) { const __conv_self__ = this; // Grammar checking this.checkGrammarDecorators(node) || this.checkGrammarModifiers(node) || this.checkGrammarInterfaceDeclaration(node); this.checkTypeParameters(node.typeParameters); if (this.produceDiagnostics) { this.checkTypeNameIsReserved(node.name, Diagnostics.Interface_name_cannot_be_0); this.checkExportsOnMergedDeclarations(node); const symbol = this.getSymbolOfNode(node); this.checkTypeParameterListsIdentical(symbol); // Only check this symbol once const firstInterfaceDecl = getDeclarationOfKind(symbol, SyntaxKind.InterfaceDeclaration); if (node === firstInterfaceDecl) { const type = (this.getDeclaredTypeOfSymbol(symbol)); const typeWithThis = this.getTypeWithThisArgument(type); // run subsequent checks only if first set succeeded if (this.checkInheritedPropertiesAreIdentical(type, node.name)) { for (const baseType of this.getBaseTypes(type)) { this.checkTypeAssignableTo(typeWithThis, this.getTypeWithThisArgument(baseType, type.thisType), node.name, Diagnostics.Interface_0_incorrectly_extends_interface_1); } this.checkIndexConstraints(type); } } this.checkObjectTypeForDuplicateDeclarations(node); } forEach(getInterfaceBaseTypeNodes(node), heritageElement => { if (!isEntityNameExpression(heritageElement.expression)) { __conv_self__.error(heritageElement.expression, Diagnostics.An_interface_can_only_extend_an_identifier_Slashqualified_name_with_optional_type_arguments); } __conv_self__.checkTypeReferenceNode(heritageElement); }); forEach(node.members, this.checkSourceElement); if (this.produceDiagnostics) { this.checkTypeForDuplicateIndexSignatures(node); this.registerForUnusedIdentifiersCheck(node); } } public checkTypeAliasDeclaration(node: TypeAliasDeclaration) { // Grammar checking this.checkGrammarDecorators(node) || this.checkGrammarModifiers(node); this.checkTypeNameIsReserved(node.name, Diagnostics.Type_alias_name_cannot_be_0); this.checkTypeParameters(node.typeParameters); this.checkSourceElement(node.type); this.registerForUnusedIdentifiersCheck(node); } public computeEnumMemberValues(node: EnumDeclaration) { const nodeLinks = this.getNodeLinks(node); if (!(nodeLinks.flags & NodeCheckFlags.EnumValuesComputed)) { nodeLinks.flags |= NodeCheckFlags.EnumValuesComputed; let autoValue = 0; for (const member of node.members) { const value = this.computeMemberValue(member, autoValue); this.getNodeLinks(member).enumMemberValue = value; autoValue = typeof value === "number" ? value + 1 : undefined; } } } public computeMemberValue(member: EnumMember, autoValue: number) { if (this.isComputedNonLiteralName((member.name))) { this.error(member.name, Diagnostics.Computed_property_names_are_not_allowed_in_enums); } else { const text = getTextOfPropertyName(member.name); if (this.isNumericLiteralName(text) && !this.isInfinityOrNaNString(text)) { this.error(member.name, Diagnostics.An_enum_member_cannot_have_a_numeric_name); } } if (member.initializer) { return this.computeConstantValue(member); } // In ambient enum declarations that specify no const modifier, enum member declarations that omit // a value are considered computed members (as opposed to having auto-incremented values). if (isInAmbientContext(member.parent) && !isConst(member.parent)) { return undefined; } // If the member declaration specifies no value, the member is considered a constant enum member. // If the member is the first member in the enum declaration, it is assigned the value zero. // Otherwise, it is assigned the value of the immediately preceding member plus one, and an error // occurs if the immediately preceding member is not a constant enum member. if (autoValue !== undefined) { return autoValue; } this.error(member.name, Diagnostics.Enum_member_must_have_initializer); return undefined; } public computeConstantValue(member: EnumMember): string | number { const __conv_self__ = this; const enumKind = this.getEnumKind(this.getSymbolOfNode(member.parent)); const isConstEnum = isConst(member.parent); const initializer = member.initializer; const value = enumKind === EnumKind.Literal && !this.isLiteralEnumMember(member) ? undefined : evaluate(initializer); if (value !== undefined) { if (isConstEnum && typeof value === "number" && !isFinite(value)) { this.error(initializer, isNaN(value) ? Diagnostics.const_enum_member_initializer_was_evaluated_to_disallowed_value_NaN : Diagnostics.const_enum_member_initializer_was_evaluated_to_a_non_finite_value); } } else if (enumKind === EnumKind.Literal) { this.error(initializer, Diagnostics.Computed_values_are_not_permitted_in_an_enum_with_string_valued_members); return 0; } else if (isConstEnum) { this.error(initializer, Diagnostics.In_const_enum_declarations_member_initializer_must_be_constant_expression); } else if (isInAmbientContext(member.parent)) { this.error(initializer, Diagnostics.In_ambient_enum_declarations_member_initializer_must_be_constant_expression); } else { // Only here do we need to check that the initializer is assignable to the enum type. this.checkTypeAssignableTo(this.checkExpression(initializer), this.getDeclaredTypeOfSymbol(this.getSymbolOfNode(member.parent)), initializer, /*headMessage*/ undefined); } return value; function evaluate(expr: Expression): string | number { switch (expr.kind) { case SyntaxKind.PrefixUnaryExpression: const value = evaluate((expr).operand); if (typeof value === "number") { switch ((expr).operator) { case SyntaxKind.PlusToken: return value; case SyntaxKind.MinusToken: return -value; case SyntaxKind.TildeToken: return ~value; } } break; case SyntaxKind.BinaryExpression: const left = evaluate((expr).left); const right = evaluate((expr).right); if (typeof left === "number" && typeof right === "number") { switch ((expr).operatorToken.kind) { case SyntaxKind.BarToken: return left | right; case SyntaxKind.AmpersandToken: return left & right; case SyntaxKind.GreaterThanGreaterThanToken: return left >> right; case SyntaxKind.GreaterThanGreaterThanGreaterThanToken: return left >>> right; case SyntaxKind.LessThanLessThanToken: return left << right; case SyntaxKind.CaretToken: return left ^ right; case SyntaxKind.AsteriskToken: return left * right; case SyntaxKind.SlashToken: return left / right; case SyntaxKind.PlusToken: return left + right; case SyntaxKind.MinusToken: return left - right; case SyntaxKind.PercentToken: return left % right; } } break; case SyntaxKind.StringLiteral: return (expr).text; case SyntaxKind.NumericLiteral: __conv_self__.checkGrammarNumericLiteral((expr)); return +(expr).text; case SyntaxKind.ParenthesizedExpression: return evaluate((expr).expression); case SyntaxKind.Identifier: return nodeIsMissing(expr) ? 0 : evaluateEnumMember(expr, __conv_self__.getSymbolOfNode(member.parent), (expr).escapedText); case SyntaxKind.ElementAccessExpression: case SyntaxKind.PropertyAccessExpression: const ex = expr; if (__conv_self__.isConstantMemberAccess(ex)) { const type = __conv_self__.getTypeOfExpression(ex.expression); if (type.symbol && type.symbol.flags & SymbolFlags.Enum) { let name: __String; if (ex.kind === SyntaxKind.PropertyAccessExpression) { name = ex.name.escapedText; } else { const argument = ex.argumentExpression; Debug.assert(isLiteralExpression(argument)); name = escapeLeadingUnderscores((argument as LiteralExpression).text); } return evaluateEnumMember(expr, type.symbol, name); } } break; } return undefined; } function evaluateEnumMember(expr: Expression, enumSymbol: Symbol, name: __String) { const memberSymbol = enumSymbol.exports.get(name); if (memberSymbol) { const declaration = memberSymbol.valueDeclaration; if (declaration !== member) { if (__conv_self__.isBlockScopedNameDeclaredBeforeUse(declaration, member)) { return __conv_self__.getNodeLinks(declaration).enumMemberValue; } __conv_self__.error(expr, Diagnostics.A_member_initializer_in_a_enum_declaration_cannot_reference_members_declared_after_it_including_members_defined_in_other_enums); return 0; } } return undefined; } } public isConstantMemberAccess(node: Expression): boolean { return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.PropertyAccessExpression && this.isConstantMemberAccess((node).expression) || node.kind === SyntaxKind.ElementAccessExpression && this.isConstantMemberAccess((node).expression) && (node).argumentExpression.kind === SyntaxKind.StringLiteral; } public checkEnumDeclaration(node: EnumDeclaration) { const __conv_self__ = this; if (!this.produceDiagnostics) { return; } // Grammar checking this.checkGrammarDecorators(node) || this.checkGrammarModifiers(node); this.checkTypeNameIsReserved(node.name, Diagnostics.Enum_name_cannot_be_0); this.checkCollisionWithCapturedThisVariable(node, node.name); this.checkCollisionWithCapturedNewTargetVariable(node, node.name); this.checkCollisionWithRequireExportsInGeneratedCode(node, node.name); this.checkCollisionWithGlobalPromiseInGeneratedCode(node, node.name); this.checkExportsOnMergedDeclarations(node); this.computeEnumMemberValues(node); const enumIsConst = isConst(node); if (this.compilerOptions.isolatedModules && enumIsConst && isInAmbientContext(node)) { this.error(node.name, Diagnostics.Ambient_const_enums_are_not_allowed_when_the_isolatedModules_flag_is_provided); } // Spec 2014 - Section 9.3: // It isn't possible for one enum declaration to continue the automatic numbering sequence of another, // and when an enum type has multiple declarations, only one declaration is permitted to omit a value // for the first member. // // Only perform this check once per symbol const enumSymbol = this.getSymbolOfNode(node); const firstDeclaration = getDeclarationOfKind(enumSymbol, node.kind); if (node === firstDeclaration) { if (enumSymbol.declarations.length > 1) { // check that const is placed\omitted on all enum declarations forEach(enumSymbol.declarations, decl => { if (isConstEnumDeclaration(decl) !== enumIsConst) { __conv_self__.error(getNameOfDeclaration(decl), Diagnostics.Enum_declarations_must_all_be_const_or_non_const); } }); } let seenEnumMissingInitialInitializer = false; forEach(enumSymbol.declarations, declaration => { // return true if we hit a violation of the rule, false otherwise if (declaration.kind !== SyntaxKind.EnumDeclaration) { return false; } const enumDeclaration = declaration; if (!enumDeclaration.members.length) { return false; } const firstEnumMember = enumDeclaration.members[0]; if (!firstEnumMember.initializer) { if (seenEnumMissingInitialInitializer) { __conv_self__.error(firstEnumMember.name, Diagnostics.In_an_enum_with_multiple_declarations_only_one_declaration_can_omit_an_initializer_for_its_first_enum_element); } else { seenEnumMissingInitialInitializer = true; } } }); } } public getFirstNonAmbientClassOrFunctionDeclaration(symbol: Symbol): Declaration { const declarations = symbol.declarations; for (const declaration of declarations) { if ((declaration.kind === SyntaxKind.ClassDeclaration || (declaration.kind === SyntaxKind.FunctionDeclaration && nodeIsPresent((declaration).body))) && !isInAmbientContext(declaration)) { return declaration; } } return undefined; } public inSameLexicalScope(node1: Node, node2: Node) { const container1 = getEnclosingBlockScopeContainer(node1); const container2 = getEnclosingBlockScopeContainer(node2); if (this.isGlobalSourceFile(container1)) { return this.isGlobalSourceFile(container2); } else if (this.isGlobalSourceFile(container2)) { return false; } else { return container1 === container2; } } public checkModuleDeclaration(node: ModuleDeclaration) { if (this.produceDiagnostics) { // Grammar checking const isGlobalAugmentation = isGlobalScopeAugmentation(node); const inAmbientContext = isInAmbientContext(node); if (isGlobalAugmentation && !inAmbientContext) { this.error(node.name, Diagnostics.Augmentations_for_the_global_scope_should_have_declare_modifier_unless_they_appear_in_already_ambient_context); } const isAmbientExternalModule = isAmbientModule(node); const contextErrorMessage = isAmbientExternalModule ? Diagnostics.An_ambient_module_declaration_is_only_allowed_at_the_top_level_in_a_file : Diagnostics.A_namespace_declaration_is_only_allowed_in_a_namespace_or_module; if (this.checkGrammarModuleElementContext(node, contextErrorMessage)) { // If we hit a module declaration in an illegal context, just bail out to avoid cascading errors. return; } if (!this.checkGrammarDecorators(node) && !this.checkGrammarModifiers(node)) { if (!inAmbientContext && node.name.kind === SyntaxKind.StringLiteral) { this.grammarErrorOnNode(node.name, Diagnostics.Only_ambient_modules_can_use_quoted_names); } } if (isIdentifier(node.name)) { this.checkCollisionWithCapturedThisVariable(node, node.name); this.checkCollisionWithRequireExportsInGeneratedCode(node, node.name); this.checkCollisionWithGlobalPromiseInGeneratedCode(node, node.name); } this.checkExportsOnMergedDeclarations(node); const symbol = this.getSymbolOfNode(node); // The following checks only apply on a non-ambient instantiated module declaration. if (symbol.flags & SymbolFlags.ValueModule && symbol.declarations.length > 1 && !inAmbientContext && isInstantiatedModule(node, this.compilerOptions.preserveConstEnums || this.compilerOptions.isolatedModules)) { const firstNonAmbientClassOrFunc = this.getFirstNonAmbientClassOrFunctionDeclaration(symbol); if (firstNonAmbientClassOrFunc) { if (getSourceFileOfNode(node) !== getSourceFileOfNode(firstNonAmbientClassOrFunc)) { this.error(node.name, Diagnostics.A_namespace_declaration_cannot_be_in_a_different_file_from_a_class_or_function_with_which_it_is_merged); } else if (node.pos < firstNonAmbientClassOrFunc.pos) { this.error(node.name, Diagnostics.A_namespace_declaration_cannot_be_located_prior_to_a_class_or_function_with_which_it_is_merged); } } // if the module merges with a class declaration in the same lexical scope, // we need to track this to ensure the correct emit. const mergedClass = getDeclarationOfKind(symbol, SyntaxKind.ClassDeclaration); if (mergedClass && this.inSameLexicalScope(node, mergedClass)) { this.getNodeLinks(node).flags |= NodeCheckFlags.LexicalModuleMergesWithClass; } } if (isAmbientExternalModule) { if (isExternalModuleAugmentation(node)) { // body of the augmentation should be checked for consistency only if augmentation was applied to its target (either global scope or module) // otherwise we'll be swamped in cascading errors. // We can detect if augmentation was applied using following rules: // - augmentation for a global scope is always applied // - augmentation for some external module is applied if symbol for augmentation is merged (it was combined with target module). const checkBody = isGlobalAugmentation || (this.getSymbolOfNode(node).flags & SymbolFlags.Transient); if (checkBody && node.body) { // body of ambient external module is always a module block for (const statement of (node.body).statements) { this.checkModuleAugmentationElement(statement, isGlobalAugmentation); } } } else if (this.isGlobalSourceFile(node.parent)) { if (isGlobalAugmentation) { this.error(node.name, Diagnostics.Augmentations_for_the_global_scope_can_only_be_directly_nested_in_external_modules_or_ambient_module_declarations); } else if (isExternalModuleNameRelative(getTextOfIdentifierOrLiteral(node.name))) { this.error(node.name, Diagnostics.Ambient_module_declaration_cannot_specify_relative_module_name); } } else { if (isGlobalAugmentation) { this.error(node.name, Diagnostics.Augmentations_for_the_global_scope_can_only_be_directly_nested_in_external_modules_or_ambient_module_declarations); } else { // Node is not an augmentation and is not located on the script level. // This means that this is declaration of ambient module that is located in other module or namespace which is prohibited. this.error(node.name, Diagnostics.Ambient_modules_cannot_be_nested_in_other_modules_or_namespaces); } } } } if (node.body) { this.checkSourceElement(node.body); if (!isGlobalScopeAugmentation(node)) { this.registerForUnusedIdentifiersCheck(node); } } } public checkModuleAugmentationElement(node: Node, isGlobalAugmentation: boolean): void { switch (node.kind) { case SyntaxKind.VariableStatement: // error each individual name in variable statement instead of marking the entire variable statement for (const decl of (node).declarationList.declarations) { this.checkModuleAugmentationElement(decl, isGlobalAugmentation); } break; case SyntaxKind.ExportAssignment: case SyntaxKind.ExportDeclaration: this.grammarErrorOnFirstToken(node, Diagnostics.Exports_and_export_assignments_are_not_permitted_in_module_augmentations); break; case SyntaxKind.ImportEqualsDeclaration: case SyntaxKind.ImportDeclaration: this.grammarErrorOnFirstToken(node, Diagnostics.Imports_are_not_permitted_in_module_augmentations_Consider_moving_them_to_the_enclosing_external_module); break; case SyntaxKind.BindingElement: case SyntaxKind.VariableDeclaration: const name = (node).name; if (isBindingPattern(name)) { for (const el of name.elements) { // mark individual names in binding pattern this.checkModuleAugmentationElement(el, isGlobalAugmentation); } break; } // falls through case SyntaxKind.ClassDeclaration: case SyntaxKind.EnumDeclaration: case SyntaxKind.FunctionDeclaration: case SyntaxKind.InterfaceDeclaration: case SyntaxKind.ModuleDeclaration: case SyntaxKind.TypeAliasDeclaration: if (isGlobalAugmentation) { return; } const symbol = this.getSymbolOfNode(node); if (symbol) { // module augmentations cannot introduce new names on the top level scope of the module // this is done it two steps // 1. quick check - if symbol for node is not merged - this is local symbol to this augmentation - report error // 2. main check - report error if value declaration of the parent symbol is module augmentation) let reportError = !(symbol.flags & SymbolFlags.Transient); if (!reportError) { // symbol should not originate in augmentation reportError = isExternalModuleAugmentation(symbol.parent.declarations[0]); } } break; } } public getFirstIdentifier(node: EntityNameOrEntityNameExpression): Identifier { switch (node.kind) { case SyntaxKind.Identifier: return node; case SyntaxKind.QualifiedName: do { node = (node).left; } while (node.kind !== SyntaxKind.Identifier); return node; case SyntaxKind.PropertyAccessExpression: do { node = (node).expression; } while (node.kind !== SyntaxKind.Identifier); return node; } } public checkExternalImportOrExportDeclaration(node: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration): boolean { const moduleName = getExternalModuleName(node); if (!nodeIsMissing(moduleName) && moduleName.kind !== SyntaxKind.StringLiteral) { this.error(moduleName, Diagnostics.String_literal_expected); return false; } const inAmbientExternalModule = node.parent.kind === SyntaxKind.ModuleBlock && isAmbientModule(node.parent.parent); if (node.parent.kind !== SyntaxKind.SourceFile && !inAmbientExternalModule) { this.error(moduleName, node.kind === SyntaxKind.ExportDeclaration ? Diagnostics.Export_declarations_are_not_permitted_in_a_namespace : Diagnostics.Import_declarations_in_a_namespace_cannot_reference_a_module); return false; } if (inAmbientExternalModule && isExternalModuleNameRelative(getTextOfIdentifierOrLiteral(moduleName))) { // we have already reported errors on top level imports\exports in external module augmentations in checkModuleDeclaration // no need to do this again. if (!this.isTopLevelInExternalModuleAugmentation(node)) { // TypeScript 1.0 spec (April 2013): 12.1.6 // An ExternalImportDeclaration in an AmbientExternalModuleDeclaration may reference // other external modules only through top - level external module names. // Relative external module names are not permitted. this.error(node, Diagnostics.Import_or_export_declaration_in_an_ambient_module_declaration_cannot_reference_module_through_relative_module_name); return false; } } return true; } public checkAliasSymbol(node: ImportEqualsDeclaration | ImportClause | NamespaceImport | ImportSpecifier | ExportSpecifier) { const symbol = this.getSymbolOfNode(node); const target = this.resolveAlias(symbol); if (target !== this.unknownSymbol) { // For external modules symbol represent local symbol for an alias. // This local symbol will merge any other local declarations (excluding other aliases) // and symbol.flags will contains combined representation for all merged declaration. // Based on symbol.flags we can compute a set of excluded meanings (meaning that resolved alias should not have, // otherwise it will conflict with some local declaration). Note that in addition to normal flags we include matching SymbolFlags.Export* // in order to prevent collisions with declarations that were exported from the current module (they still contribute to local names). const excludedMeanings = (symbol.flags & (SymbolFlags.Value | SymbolFlags.ExportValue) ? SymbolFlags.Value : 0) | (symbol.flags & SymbolFlags.Type ? SymbolFlags.Type : 0) | (symbol.flags & SymbolFlags.Namespace ? SymbolFlags.Namespace : 0); if (target.flags & excludedMeanings) { const message = node.kind === SyntaxKind.ExportSpecifier ? Diagnostics.Export_declaration_conflicts_with_exported_declaration_of_0 : Diagnostics.Import_declaration_conflicts_with_local_declaration_of_0; this.error(node, message, this.symbolToString(symbol)); } // Don't allow to re-export something with no value side when `--isolatedModules` is set. if (this.compilerOptions.isolatedModules && node.kind === SyntaxKind.ExportSpecifier && !(target.flags & SymbolFlags.Value) && !isInAmbientContext(node)) { this.error(node, Diagnostics.Cannot_re_export_a_type_when_the_isolatedModules_flag_is_provided); } } } public checkImportBinding(node: ImportEqualsDeclaration | ImportClause | NamespaceImport | ImportSpecifier) { this.checkCollisionWithCapturedThisVariable(node, node.name); this.checkCollisionWithRequireExportsInGeneratedCode(node, node.name); this.checkCollisionWithGlobalPromiseInGeneratedCode(node, node.name); this.checkAliasSymbol(node); } public checkImportDeclaration(node: ImportDeclaration) { if (this.checkGrammarModuleElementContext(node, Diagnostics.An_import_declaration_can_only_be_used_in_a_namespace_or_module)) { // If we hit an import declaration in an illegal context, just bail out to avoid cascading errors. return; } if (!this.checkGrammarDecorators(node) && !this.checkGrammarModifiers(node) && hasModifiers(node)) { this.grammarErrorOnFirstToken(node, Diagnostics.An_import_declaration_cannot_have_modifiers); } if (this.checkExternalImportOrExportDeclaration(node)) { const importClause = node.importClause; if (importClause) { if (importClause.name) { this.checkImportBinding(importClause); } if (importClause.namedBindings) { if (importClause.namedBindings.kind === SyntaxKind.NamespaceImport) { this.checkImportBinding((importClause.namedBindings)); } else { forEach((importClause.namedBindings).elements, this.checkImportBinding); } } } } } public checkImportEqualsDeclaration(node: ImportEqualsDeclaration) { if (this.checkGrammarModuleElementContext(node, Diagnostics.An_import_declaration_can_only_be_used_in_a_namespace_or_module)) { // If we hit an import declaration in an illegal context, just bail out to avoid cascading errors. return; } this.checkGrammarDecorators(node) || this.checkGrammarModifiers(node); if (isInternalModuleImportEqualsDeclaration(node) || this.checkExternalImportOrExportDeclaration(node)) { this.checkImportBinding(node); if (hasModifier(node, ModifierFlags.Export)) { this.markExportAsReferenced(node); } if (isInternalModuleImportEqualsDeclaration(node)) { const target = this.resolveAlias(this.getSymbolOfNode(node)); if (target !== this.unknownSymbol) { if (target.flags & SymbolFlags.Value) { // Target is a value symbol, check that it is not hidden by a local declaration with the same name const moduleName = this.getFirstIdentifier((node.moduleReference)); if (!(this.resolveEntityName(moduleName, SymbolFlags.Value | SymbolFlags.Namespace).flags & SymbolFlags.Namespace)) { this.error(moduleName, Diagnostics.Module_0_is_hidden_by_a_local_declaration_with_the_same_name, declarationNameToString(moduleName)); } } if (target.flags & SymbolFlags.Type) { this.checkTypeNameIsReserved(node.name, Diagnostics.Import_name_cannot_be_0); } } } else { if (this.modulekind === ModuleKind.ES2015 && !isInAmbientContext(node)) { // Import equals declaration is deprecated in es6 or above this.grammarErrorOnNode(node, Diagnostics.Import_assignment_cannot_be_used_when_targeting_ECMAScript_2015_modules_Consider_using_import_Asterisk_as_ns_from_mod_import_a_from_mod_import_d_from_mod_or_another_module_format_instead); } } } } public checkExportDeclaration(node: ExportDeclaration) { if (this.checkGrammarModuleElementContext(node, Diagnostics.An_export_declaration_can_only_be_used_in_a_module)) { // If we hit an export in an illegal context, just bail out to avoid cascading errors. return; } if (!this.checkGrammarDecorators(node) && !this.checkGrammarModifiers(node) && hasModifiers(node)) { this.grammarErrorOnFirstToken(node, Diagnostics.An_export_declaration_cannot_have_modifiers); } if (!node.moduleSpecifier || this.checkExternalImportOrExportDeclaration(node)) { if (node.exportClause) { // export { x, y } // export { x, y } from "foo" forEach(node.exportClause.elements, this.checkExportSpecifier); const inAmbientExternalModule = node.parent.kind === SyntaxKind.ModuleBlock && isAmbientModule(node.parent.parent); const inAmbientNamespaceDeclaration = !inAmbientExternalModule && node.parent.kind === SyntaxKind.ModuleBlock && !node.moduleSpecifier && isInAmbientContext(node); if (node.parent.kind !== SyntaxKind.SourceFile && !inAmbientExternalModule && !inAmbientNamespaceDeclaration) { this.error(node, Diagnostics.Export_declarations_are_not_permitted_in_a_namespace); } } else { // export * from "foo" const moduleSymbol = this.resolveExternalModuleName(node, node.moduleSpecifier); if (moduleSymbol && this.hasExportAssignmentSymbol(moduleSymbol)) { this.error(node.moduleSpecifier, Diagnostics.Module_0_uses_export_and_cannot_be_used_with_export_Asterisk, this.symbolToString(moduleSymbol)); } if (this.modulekind !== ModuleKind.System && this.modulekind !== ModuleKind.ES2015) { this.checkExternalEmitHelpers(node, ExternalEmitHelpers.ExportStar); } } } } public checkGrammarModuleElementContext(node: Statement, errorMessage: DiagnosticMessage): boolean { const isInAppropriateContext = node.parent.kind === SyntaxKind.SourceFile || node.parent.kind === SyntaxKind.ModuleBlock || node.parent.kind === SyntaxKind.ModuleDeclaration; if (!isInAppropriateContext) { this.grammarErrorOnFirstToken(node, errorMessage); } return !isInAppropriateContext; } public checkExportSpecifier(node: ExportSpecifier) { this.checkAliasSymbol(node); if (!(node.parent.parent).moduleSpecifier) { const exportedName = node.propertyName || node.name; // find immediate value referenced by exported name (SymbolFlags.Alias is set so we don't chase down aliases) const symbol = this.resolveName(exportedName, exportedName.escapedText, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined); if (symbol && (symbol === this.undefinedSymbol || this.isGlobalSourceFile(this.getDeclarationContainer(symbol.declarations[0])))) { this.error(exportedName, Diagnostics.Cannot_export_0_Only_local_declarations_can_be_exported_from_a_module, unescapeLeadingUnderscores(exportedName.escapedText)); } else { this.markExportAsReferenced(node); } } } public checkExportAssignment(node: ExportAssignment) { if (this.checkGrammarModuleElementContext(node, Diagnostics.An_export_assignment_can_only_be_used_in_a_module)) { // If we hit an export assignment in an illegal context, just bail out to avoid cascading errors. return; } const container = node.parent.kind === SyntaxKind.SourceFile ? node.parent : node.parent.parent; if (container.kind === SyntaxKind.ModuleDeclaration && !isAmbientModule(container)) { if (node.isExportEquals) { this.error(node, Diagnostics.An_export_assignment_cannot_be_used_in_a_namespace); } else { this.error(node, Diagnostics.A_default_export_can_only_be_used_in_an_ECMAScript_style_module); } return; } // Grammar checking if (!this.checkGrammarDecorators(node) && !this.checkGrammarModifiers(node) && hasModifiers(node)) { this.grammarErrorOnFirstToken(node, Diagnostics.An_export_assignment_cannot_have_modifiers); } if (node.expression.kind === SyntaxKind.Identifier) { this.markExportAsReferenced(node); } else { this.checkExpressionCached(node.expression); } this.checkExternalModuleExports(container); if (node.isExportEquals && !isInAmbientContext(node)) { if (this.modulekind === ModuleKind.ES2015) { // export assignment is not supported in es6 modules this.grammarErrorOnNode(node, Diagnostics.Export_assignment_cannot_be_used_when_targeting_ECMAScript_2015_modules_Consider_using_export_default_or_another_module_format_instead); } else if (this.modulekind === ModuleKind.System) { // system modules does not support export assignment this.grammarErrorOnNode(node, Diagnostics.Export_assignment_is_not_supported_when_module_flag_is_system); } } } public hasExportedMembers(moduleSymbol: Symbol) { return forEachEntry(moduleSymbol.exports, (_, id) => id !== "export="); } public checkExternalModuleExports(node: SourceFile | ModuleDeclaration) { const __conv_self__ = this; const moduleSymbol = this.getSymbolOfNode(node); const links = this.getSymbolLinks(moduleSymbol); if (!links.exportsChecked) { const exportEqualsSymbol = moduleSymbol.exports.get("export=" as __String); if (exportEqualsSymbol && this.hasExportedMembers(moduleSymbol)) { const declaration = this.getDeclarationOfAliasSymbol(exportEqualsSymbol) || exportEqualsSymbol.valueDeclaration; if (!this.isTopLevelInExternalModuleAugmentation(declaration)) { this.error(declaration, Diagnostics.An_export_assignment_cannot_be_used_in_a_module_with_other_exported_elements); } } // Checks for export * conflicts const exports = this.getExportsOfModule(moduleSymbol); exports && exports.forEach(({ declarations, flags }, id) => { if (id === "__export") { return; } // ECMA262: 15.2.1.1 It is a Syntax Error if the ExportedNames of ModuleItemList contains any duplicate entries. // (TS Exceptions: namespaces, function overloads, enums, and interfaces) if (flags & (SymbolFlags.Namespace | SymbolFlags.Interface | SymbolFlags.Enum)) { return; } const exportedDeclarationsCount = countWhere(declarations, isNotOverload); if (flags & SymbolFlags.TypeAlias && exportedDeclarationsCount <= 2) { // it is legal to merge type alias with other values // so count should be either 1 (just type alias) or 2 (type alias + merged value) return; } if (exportedDeclarationsCount > 1) { for (const declaration of declarations) { if (isNotOverload(declaration)) { __conv_self__.diagnostics.add(createDiagnosticForNode(declaration, Diagnostics.Cannot_redeclare_exported_variable_0, unescapeLeadingUnderscores(id))); } } } }); links.exportsChecked = true; } function isNotOverload(declaration: Declaration): boolean { return (declaration.kind !== SyntaxKind.FunctionDeclaration && declaration.kind !== SyntaxKind.MethodDeclaration) || !!(declaration as FunctionDeclaration).body; } } public checkSourceElement(node: Node): void { if (!node) { return; } const kind = node.kind; if (this.cancellationToken) { // Only bother checking on a few construct kinds. We don't want to be excessively // hitting the cancellation token on every node we check. switch (kind) { case SyntaxKind.ModuleDeclaration: case SyntaxKind.ClassDeclaration: case SyntaxKind.InterfaceDeclaration: case SyntaxKind.FunctionDeclaration: this.cancellationToken.throwIfCancellationRequested(); } } switch (kind) { case SyntaxKind.TypeParameter: return this.checkTypeParameter((node)); case SyntaxKind.Parameter: return this.checkParameter((node)); case SyntaxKind.PropertyDeclaration: case SyntaxKind.PropertySignature: return this.checkPropertyDeclaration((node)); case SyntaxKind.FunctionType: case SyntaxKind.ConstructorType: case SyntaxKind.CallSignature: case SyntaxKind.ConstructSignature: return this.checkSignatureDeclaration((node)); case SyntaxKind.IndexSignature: return this.checkSignatureDeclaration((node)); case SyntaxKind.MethodDeclaration: case SyntaxKind.MethodSignature: return this.checkMethodDeclaration((node)); case SyntaxKind.Constructor: return this.checkConstructorDeclaration((node)); case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: return this.checkAccessorDeclaration((node)); case SyntaxKind.TypeReference: return this.checkTypeReferenceNode((node)); case SyntaxKind.TypePredicate: return this.checkTypePredicate((node)); case SyntaxKind.TypeQuery: return this.checkTypeQuery((node)); case SyntaxKind.TypeLiteral: return this.checkTypeLiteral((node)); case SyntaxKind.ArrayType: return this.checkArrayType((node)); case SyntaxKind.TupleType: return this.checkTupleType((node)); case SyntaxKind.UnionType: case SyntaxKind.IntersectionType: return this.checkUnionOrIntersectionType((node)); case SyntaxKind.ParenthesizedType: case SyntaxKind.TypeOperator: return this.checkSourceElement((node).type); case SyntaxKind.JSDocComment: return this.checkJSDocComment((node as JSDoc)); case SyntaxKind.JSDocParameterTag: return this.checkSourceElement((node as JSDocParameterTag).typeExpression); case SyntaxKind.JSDocFunctionType: this.checkSignatureDeclaration((node as JSDocFunctionType)); // falls through case SyntaxKind.JSDocVariadicType: case SyntaxKind.JSDocNonNullableType: case SyntaxKind.JSDocNullableType: case SyntaxKind.JSDocAllType: case SyntaxKind.JSDocUnknownType: if (!isInJavaScriptFile(node) && !isInJSDoc(node)) { this.grammarErrorOnNode(node, Diagnostics.JSDoc_types_can_only_be_used_inside_documentation_comments); } return; case SyntaxKind.JSDocTypeExpression: return this.checkSourceElement((node as JSDocTypeExpression).type); case SyntaxKind.IndexedAccessType: return this.checkIndexedAccessType((node)); case SyntaxKind.MappedType: return this.checkMappedType((node)); case SyntaxKind.FunctionDeclaration: return this.checkFunctionDeclaration((node)); case SyntaxKind.Block: case SyntaxKind.ModuleBlock: return this.checkBlock((node)); case SyntaxKind.VariableStatement: return this.checkVariableStatement((node)); case SyntaxKind.ExpressionStatement: return this.checkExpressionStatement((node)); case SyntaxKind.IfStatement: return this.checkIfStatement((node)); case SyntaxKind.DoStatement: return this.checkDoStatement((node)); case SyntaxKind.WhileStatement: return this.checkWhileStatement((node)); case SyntaxKind.ForStatement: return this.checkForStatement((node)); case SyntaxKind.ForInStatement: return this.checkForInStatement((node)); case SyntaxKind.ForOfStatement: return this.checkForOfStatement((node)); case SyntaxKind.ContinueStatement: case SyntaxKind.BreakStatement: return this.checkBreakOrContinueStatement((node)); case SyntaxKind.ReturnStatement: return this.checkReturnStatement((node)); case SyntaxKind.WithStatement: return this.checkWithStatement((node)); case SyntaxKind.SwitchStatement: return this.checkSwitchStatement((node)); case SyntaxKind.LabeledStatement: return this.checkLabeledStatement((node)); case SyntaxKind.ThrowStatement: return this.checkThrowStatement((node)); case SyntaxKind.TryStatement: return this.checkTryStatement((node)); case SyntaxKind.VariableDeclaration: return this.checkVariableDeclaration((node)); case SyntaxKind.BindingElement: return this.checkBindingElement((node)); case SyntaxKind.ClassDeclaration: return this.checkClassDeclaration((node)); case SyntaxKind.InterfaceDeclaration: return this.checkInterfaceDeclaration((node)); case SyntaxKind.TypeAliasDeclaration: return this.checkTypeAliasDeclaration((node)); case SyntaxKind.EnumDeclaration: return this.checkEnumDeclaration((node)); case SyntaxKind.ModuleDeclaration: return this.checkModuleDeclaration((node)); case SyntaxKind.ImportDeclaration: return this.checkImportDeclaration((node)); case SyntaxKind.ImportEqualsDeclaration: return this.checkImportEqualsDeclaration((node)); case SyntaxKind.ExportDeclaration: return this.checkExportDeclaration((node)); case SyntaxKind.ExportAssignment: return this.checkExportAssignment((node)); case SyntaxKind.EmptyStatement: this.checkGrammarStatementInAmbientContext(node); return; case SyntaxKind.DebuggerStatement: this.checkGrammarStatementInAmbientContext(node); return; case SyntaxKind.MissingDeclaration: return this.checkMissingDeclaration(node); } } // Function and class expression bodies are checked after all statements in the enclosing body. This is // to ensure constructs like the following are permitted: // const foo = function () { // const s = foo(); // return "hello"; // } // Here, performing a full type check of the body of the function expression whilst in the process of // determining the type of foo would cause foo to be given type any because of the recursive reference. // Delaying the type check of the body ensures foo has been assigned a type. public checkNodeDeferred(node: Node) { if (this.deferredNodes) { this.deferredNodes.push(node); } } public checkDeferredNodes() { for (const node of this.deferredNodes) { switch (node.kind) { case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: case SyntaxKind.MethodDeclaration: case SyntaxKind.MethodSignature: this.checkFunctionExpressionOrObjectLiteralMethodDeferred((node)); break; case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: this.checkAccessorDeclaration((node)); break; case SyntaxKind.ClassExpression: this.checkClassExpressionDeferred((node)); break; } } } public checkSourceFile(node: SourceFile) { performance.mark("beforeCheck"); this.checkSourceFileWorker(node); performance.mark("afterCheck"); performance.measure("Check", "beforeCheck", "afterCheck"); } // Fully type check a source file and collect the relevant diagnostics. public checkSourceFileWorker(node: SourceFile) { const links = this.getNodeLinks(node); if (!(links.flags & NodeCheckFlags.TypeChecked)) { // If skipLibCheck is enabled, skip type checking if file is a declaration file. // If skipDefaultLibCheck is enabled, skip type checking if file contains a // '/// ' directive. if (this.compilerOptions.skipLibCheck && node.isDeclarationFile || this.compilerOptions.skipDefaultLibCheck && node.hasNoDefaultLib) { return; } // Grammar checking this.checkGrammarSourceFile(node); clear(this.potentialThisCollisions); clear(this.potentialNewTargetCollisions); this.deferredNodes = []; this.deferredUnusedIdentifierNodes = this.produceDiagnostics && this.noUnusedIdentifiers ? [] : undefined; forEach(node.statements, this.checkSourceElement); this.checkDeferredNodes(); if (isExternalModule(node)) { this.registerForUnusedIdentifiersCheck(node); } if (!node.isDeclarationFile) { this.checkUnusedIdentifiers(); } this.deferredNodes = undefined; this.deferredUnusedIdentifierNodes = undefined; if (isExternalOrCommonJsModule(node)) { this.checkExternalModuleExports(node); } if (this.potentialThisCollisions.length) { forEach(this.potentialThisCollisions, this.checkIfThisIsCapturedInEnclosingScope); clear(this.potentialThisCollisions); } if (this.potentialNewTargetCollisions.length) { forEach(this.potentialNewTargetCollisions, this.checkIfNewTargetIsCapturedInEnclosingScope); clear(this.potentialNewTargetCollisions); } links.flags |= NodeCheckFlags.TypeChecked; } } public getDiagnostics(sourceFile: SourceFile, ct: CancellationToken): Diagnostic[] { 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. this.cancellationToken = ct; return this.getDiagnosticsWorker(sourceFile); } finally { this.cancellationToken = undefined; } } public getDiagnosticsWorker(sourceFile: SourceFile): Diagnostic[] { this.throwIfNonDiagnosticsProducing(); if (sourceFile) { // Some global diagnostics are deferred until they are needed and // may not be reported in the firt call to getGlobalDiagnostics. // We should catch these changes and report them. const previousGlobalDiagnostics = this.diagnostics.getGlobalDiagnostics(); const previousGlobalDiagnosticsSize = previousGlobalDiagnostics.length; this.checkSourceFile(sourceFile); const semanticDiagnostics = this.diagnostics.getDiagnostics(sourceFile.fileName); const currentGlobalDiagnostics = this.diagnostics.getGlobalDiagnostics(); if (currentGlobalDiagnostics !== previousGlobalDiagnostics) { // If the arrays are not the same reference, new diagnostics were added. const deferredGlobalDiagnostics = relativeComplement(previousGlobalDiagnostics, currentGlobalDiagnostics, compareDiagnostics); return concatenate(deferredGlobalDiagnostics, semanticDiagnostics); } else if (previousGlobalDiagnosticsSize === 0 && currentGlobalDiagnostics.length > 0) { // If the arrays are the same reference, but the length has changed, a single // new diagnostic was added as DiagnosticCollection attempts to reuse the // same array. return concatenate(currentGlobalDiagnostics, semanticDiagnostics); } return semanticDiagnostics; } // Global diagnostics are always added when a file is not provided to // getDiagnostics forEach(this.host.getSourceFiles(), this.checkSourceFile); return this.diagnostics.getDiagnostics(); } public getGlobalDiagnostics(): Diagnostic[] { this.throwIfNonDiagnosticsProducing(); return this.diagnostics.getGlobalDiagnostics(); } public throwIfNonDiagnosticsProducing() { if (!this.produceDiagnostics) { throw new Error("Trying to get diagnostics from a type checker that does not produce them."); } } // Language service support public isInsideWithStatementBody(node: Node): boolean { if (node) { while (node.parent) { if (node.parent.kind === SyntaxKind.WithStatement && (node.parent).statement === node) { return true; } node = node.parent; } } return false; } public getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[] { const __conv_self__ = this; if (this.isInsideWithStatementBody(location)) { // We cannot answer semantic questions within a with block, do not proceed any further return []; } const symbols = createSymbolTable(); let isStatic = false; populateSymbols(); return this.symbolsToArray(symbols); function populateSymbols() { while (location) { if (location.locals && !__conv_self__.isGlobalSourceFile(location)) { copySymbols(location.locals, meaning); } switch (location.kind) { case SyntaxKind.ModuleDeclaration: copySymbols(__conv_self__.getSymbolOfNode(location).exports, meaning & SymbolFlags.ModuleMember); break; case SyntaxKind.EnumDeclaration: copySymbols(__conv_self__.getSymbolOfNode(location).exports, meaning & SymbolFlags.EnumMember); break; case SyntaxKind.ClassExpression: const className = (location).name; if (className) { copySymbol(location.symbol, meaning); } // falls through // this fall-through is necessary because we would like to handle // type parameter inside class expression similar to how we handle it in classDeclaration and interface Declaration case SyntaxKind.ClassDeclaration: case SyntaxKind.InterfaceDeclaration: // If we didn't come from static member of class or interface, // add the type parameters into the symbol table // (type parameters of classDeclaration/classExpression and interface are in member property of the symbol. // Note: that the memberFlags come from previous iteration. if (!isStatic) { copySymbols(__conv_self__.getSymbolOfNode(location).members, meaning & SymbolFlags.Type); } break; case SyntaxKind.FunctionExpression: const funcName = (location).name; if (funcName) { copySymbol(location.symbol, meaning); } break; } if (introducesArgumentsExoticObject(location)) { copySymbol(__conv_self__.argumentsSymbol, meaning); } isStatic = hasModifier(location, ModifierFlags.Static); location = location.parent; } copySymbols(__conv_self__.globals, meaning); } /** * Copy the given symbol into symbol tables if the symbol has the given meaning * and it doesn't already existed in the symbol table * @param key a key for storing in symbol table; if undefined, use symbol.name * @param symbol the symbol to be added into symbol table * @param meaning meaning of symbol to filter by before adding to symbol table */ function copySymbol(symbol: Symbol, meaning: SymbolFlags): void { if (getCombinedLocalAndExportSymbolFlags(symbol) & meaning) { const id = symbol.escapedName; // We will copy all symbol regardless of its reserved name because // symbolsToArray will check whether the key is a reserved name and // it will not copy symbol with reserved name to the array if (!symbols.has(id)) { symbols.set(id, symbol); } } } function copySymbols(source: SymbolTable, meaning: SymbolFlags): void { if (meaning) { source.forEach(symbol => { copySymbol(symbol, meaning); }); } } } public isTypeDeclarationName(name: Node): boolean { return name.kind === SyntaxKind.Identifier && this.isTypeDeclaration(name.parent) && (name.parent).name === name; } public isTypeDeclaration(node: Node): boolean { switch (node.kind) { case SyntaxKind.TypeParameter: case SyntaxKind.ClassDeclaration: case SyntaxKind.InterfaceDeclaration: case SyntaxKind.TypeAliasDeclaration: case SyntaxKind.EnumDeclaration: return true; } } // True if the given identifier is part of a type reference public isTypeReferenceIdentifier(entityName: EntityName): boolean { let node: Node = entityName; while (node.parent && node.parent.kind === SyntaxKind.QualifiedName) { node = node.parent; } return node.parent && node.parent.kind === SyntaxKind.TypeReference; } public isHeritageClauseElementIdentifier(entityName: Node): boolean { let node = entityName; while (node.parent && node.parent.kind === SyntaxKind.PropertyAccessExpression) { node = node.parent; } return node.parent && node.parent.kind === SyntaxKind.ExpressionWithTypeArguments; } public forEachEnclosingClass(node: Node, callback: (node: Node) => T): T { let result: T; while (true) { node = getContainingClass(node); if (!node) break; if (result = callback(node)) break; } return result; } public isNodeWithinClass(node: Node, classDeclaration: ClassLikeDeclaration) { return !!this.forEachEnclosingClass(node, n => n === classDeclaration); } public getLeftSideOfImportEqualsOrExportAssignment(nodeOnRightSide: EntityName): ImportEqualsDeclaration | ExportAssignment { while (nodeOnRightSide.parent.kind === SyntaxKind.QualifiedName) { nodeOnRightSide = nodeOnRightSide.parent; } if (nodeOnRightSide.parent.kind === SyntaxKind.ImportEqualsDeclaration) { return (nodeOnRightSide.parent).moduleReference === nodeOnRightSide && nodeOnRightSide.parent; } if (nodeOnRightSide.parent.kind === SyntaxKind.ExportAssignment) { return (nodeOnRightSide.parent).expression === nodeOnRightSide && nodeOnRightSide.parent; } return undefined; } public isInRightSideOfImportOrExportAssignment(node: EntityName) { return this.getLeftSideOfImportEqualsOrExportAssignment(node) !== undefined; } public getSpecialPropertyAssignmentSymbolFromEntityName(entityName: EntityName | PropertyAccessExpression) { const specialPropertyAssignmentKind = getSpecialPropertyAssignmentKind(entityName.parent.parent as BinaryExpression); switch (specialPropertyAssignmentKind) { case SpecialPropertyAssignmentKind.ExportsProperty: case SpecialPropertyAssignmentKind.PrototypeProperty: return this.getSymbolOfNode(entityName.parent); case SpecialPropertyAssignmentKind.ThisProperty: case SpecialPropertyAssignmentKind.ModuleExports: case SpecialPropertyAssignmentKind.Property: return this.getSymbolOfNode(entityName.parent.parent); } } public getSymbolOfEntityNameOrPropertyAccessExpression(entityName: EntityName | PropertyAccessExpression): Symbol | undefined { if (isDeclarationName(entityName)) { return this.getSymbolOfNode(entityName.parent); } if (isInJavaScriptFile(entityName) && entityName.parent.kind === SyntaxKind.PropertyAccessExpression && entityName.parent === (entityName.parent.parent as BinaryExpression).left) { // Check if this is a special property assignment const specialPropertyAssignmentSymbol = this.getSpecialPropertyAssignmentSymbolFromEntityName(entityName); if (specialPropertyAssignmentSymbol) { return specialPropertyAssignmentSymbol; } } if (entityName.parent.kind === SyntaxKind.ExportAssignment && isEntityNameExpression(entityName)) { return this.resolveEntityName((entityName), /*all meanings*/ SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias); } if (entityName.kind !== SyntaxKind.PropertyAccessExpression && this.isInRightSideOfImportOrExportAssignment((entityName))) { // Since we already checked for ExportAssignment, this really could only be an Import const importEqualsDeclaration = getAncestor(entityName, SyntaxKind.ImportEqualsDeclaration); Debug.assert(importEqualsDeclaration !== undefined); return this.getSymbolOfPartOfRightHandSideOfImportEquals((entityName), /*dontResolveAlias*/ true); } if (isRightSideOfQualifiedNameOrPropertyAccess(entityName)) { entityName = entityName.parent; } if (this.isHeritageClauseElementIdentifier((entityName))) { let meaning = SymbolFlags.None; // In an interface or class, we're definitely interested in a type. if (entityName.parent.kind === SyntaxKind.ExpressionWithTypeArguments) { meaning = SymbolFlags.Type; // In a class 'extends' clause we are also looking for a value. if (isExpressionWithTypeArgumentsInClassExtendsClause(entityName.parent)) { meaning |= SymbolFlags.Value; } } else { meaning = SymbolFlags.Namespace; } meaning |= SymbolFlags.Alias; const entityNameSymbol = this.resolveEntityName((entityName), meaning); if (entityNameSymbol) { return entityNameSymbol; } } if (entityName.parent!.kind === SyntaxKind.JSDocParameterTag) { return getParameterSymbolFromJSDoc(entityName.parent as JSDocParameterTag); } if (entityName.parent.kind === SyntaxKind.TypeParameter && entityName.parent.parent.kind === SyntaxKind.JSDocTemplateTag) { Debug.assert(!isInJavaScriptFile(entityName)); // Otherwise `isDeclarationName` would have been true. const typeParameter = getTypeParameterFromJsDoc(entityName.parent as TypeParameterDeclaration & { parent: JSDocTemplateTag; }); return typeParameter && typeParameter.symbol; } if (isPartOfExpression(entityName)) { if (nodeIsMissing(entityName)) { // Missing entity name. return undefined; } if (entityName.kind === SyntaxKind.Identifier) { if (isJSXTagName(entityName) && this.isJsxIntrinsicIdentifier(entityName)) { return this.getIntrinsicTagSymbol((entityName.parent)); } return this.resolveEntityName(entityName, SymbolFlags.Value, /*ignoreErrors*/ false, /*dontResolveAlias*/ true); } else if (entityName.kind === SyntaxKind.PropertyAccessExpression || entityName.kind === SyntaxKind.QualifiedName) { const links = this.getNodeLinks(entityName); if (links.resolvedSymbol) { return links.resolvedSymbol; } if (entityName.kind === SyntaxKind.PropertyAccessExpression) { this.checkPropertyAccessExpression(entityName); } else { this.checkQualifiedName(entityName); } return links.resolvedSymbol; } } else if (this.isTypeReferenceIdentifier((entityName))) { const meaning = entityName.parent.kind === SyntaxKind.TypeReference ? SymbolFlags.Type : SymbolFlags.Namespace; return this.resolveEntityName((entityName), meaning, /*ignoreErrors*/ false, /*dontResolveAlias*/ true); } else if (entityName.parent.kind === SyntaxKind.JsxAttribute) { return this.getJsxAttributePropertySymbol((entityName.parent)); } if (entityName.parent.kind === SyntaxKind.TypePredicate) { return this.resolveEntityName((entityName), /*meaning*/ SymbolFlags.FunctionScopedVariable); } // Do we want to return undefined here? return undefined; } public getSymbolAtLocation(node: Node) { if (node.kind === SyntaxKind.SourceFile) { return isExternalModule(node) ? this.getMergedSymbol(node.symbol) : undefined; } if (this.isInsideWithStatementBody(node)) { // We cannot answer semantic questions within a with block, do not proceed any further return undefined; } if (isDeclarationNameOrImportPropertyName(node)) { // This is a declaration, call getSymbolOfNode return this.getSymbolOfNode(node.parent); } else if (isLiteralComputedPropertyDeclarationName(node)) { return this.getSymbolOfNode(node.parent.parent); } if (node.kind === SyntaxKind.Identifier) { if (this.isInRightSideOfImportOrExportAssignment((node))) { return this.getSymbolOfEntityNameOrPropertyAccessExpression((node)); } else if (node.parent.kind === SyntaxKind.BindingElement && node.parent.parent.kind === SyntaxKind.ObjectBindingPattern && node === (node.parent).propertyName) { const typeOfPattern = this.getTypeOfNode(node.parent.parent); const propertyDeclaration = typeOfPattern && this.getPropertyOfType(typeOfPattern, (node).escapedText); if (propertyDeclaration) { return propertyDeclaration; } } } switch (node.kind) { case SyntaxKind.Identifier: case SyntaxKind.PropertyAccessExpression: case SyntaxKind.QualifiedName: return this.getSymbolOfEntityNameOrPropertyAccessExpression((node)); case SyntaxKind.ThisKeyword: const container = getThisContainer(node, /*includeArrowFunctions*/ false); if (isFunctionLike(container)) { const sig = this.getSignatureFromDeclaration(container); if (sig.thisParameter) { return sig.thisParameter; } } // falls through case SyntaxKind.SuperKeyword: const type = isPartOfExpression(node) ? this.getTypeOfExpression((node)) : this.getTypeFromTypeNode((node)); return type.symbol; case SyntaxKind.ThisType: return this.getTypeFromTypeNode((node)).symbol; case SyntaxKind.ConstructorKeyword: // constructor keyword for an overload, should take us to the definition if it exist const constructorDeclaration = node.parent; if (constructorDeclaration && constructorDeclaration.kind === SyntaxKind.Constructor) { return (constructorDeclaration.parent).symbol; } return undefined; case SyntaxKind.StringLiteral: // 1). import x = require("./mo/*gotToDefinitionHere*/d") // 2). External module name in an import declaration // 3). Dynamic import call or require in javascript if ((isExternalModuleImportEqualsDeclaration(node.parent.parent) && getExternalModuleImportEqualsDeclarationExpression(node.parent.parent) === node) || ((node.parent.kind === SyntaxKind.ImportDeclaration || node.parent.kind === SyntaxKind.ExportDeclaration) && (node.parent).moduleSpecifier === node) || ((isInJavaScriptFile(node) && isRequireCall(node.parent, /*checkArgumentIsStringLiteral*/ false)) || isImportCall(node.parent))) { return this.resolveExternalModuleName(node, (node)); } // falls through case SyntaxKind.NumericLiteral: // index access if (node.parent.kind === SyntaxKind.ElementAccessExpression && (node.parent).argumentExpression === node) { const objectType = this.getTypeOfExpression((node.parent).expression); if (objectType === this.unknownType) return undefined; const apparentType = this.getApparentType(objectType); if (apparentType === this.unknownType) return undefined; return this.getPropertyOfType(apparentType, ((node).text as __String)); } break; } return undefined; } public getShorthandAssignmentValueSymbol(location: Node): Symbol { // The function returns a value symbol of an identifier in the short-hand property assignment. // This is necessary as an identifier in short-hand property assignment can contains two meaning: // property name and property value. if (location && location.kind === SyntaxKind.ShorthandPropertyAssignment) { return this.resolveEntityName((location).name, SymbolFlags.Value | SymbolFlags.Alias); } return undefined; } /** Returns the target of an export specifier without following aliases */ public getExportSpecifierLocalTargetSymbol(node: ExportSpecifier): Symbol { return (node.parent.parent).moduleSpecifier ? this.getExternalModuleMember((node.parent.parent), node) : this.resolveEntityName(node.propertyName || node.name, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias); } public getTypeOfNode(node: Node): Type { if (this.isInsideWithStatementBody(node)) { // We cannot answer semantic questions within a with block, do not proceed any further return this.unknownType; } if (isPartOfTypeNode(node)) { let typeFromTypeNode = this.getTypeFromTypeNode((node)); if (typeFromTypeNode && isExpressionWithTypeArgumentsInClassImplementsClause(node)) { const containingClass = getContainingClass(node); const classType = (this.getTypeOfNode(containingClass) as InterfaceType); typeFromTypeNode = this.getTypeWithThisArgument(typeFromTypeNode, classType.thisType); } return typeFromTypeNode; } if (isPartOfExpression(node)) { return this.getRegularTypeOfExpression((node)); } if (isExpressionWithTypeArgumentsInClassExtendsClause(node)) { // A SyntaxKind.ExpressionWithTypeArguments is considered a type node, except when it occurs in the // extends clause of a class. We handle that case here. const classNode = getContainingClass(node); const classType = (this.getDeclaredTypeOfSymbol(this.getSymbolOfNode(classNode)) as InterfaceType); const baseType = this.getBaseTypes(classType)[0]; return baseType && this.getTypeWithThisArgument(baseType, classType.thisType); } if (this.isTypeDeclaration(node)) { // In this case, we call getSymbolOfNode instead of getSymbolAtLocation because it is a declaration const symbol = this.getSymbolOfNode(node); return this.getDeclaredTypeOfSymbol(symbol); } if (this.isTypeDeclarationName(node)) { const symbol = this.getSymbolAtLocation(node); return symbol && this.getDeclaredTypeOfSymbol(symbol); } if (isDeclaration(node)) { // In this case, we call getSymbolOfNode instead of getSymbolAtLocation because it is a declaration const symbol = this.getSymbolOfNode(node); return this.getTypeOfSymbol(symbol); } if (isDeclarationNameOrImportPropertyName(node)) { const symbol = this.getSymbolAtLocation(node); return symbol && this.getTypeOfSymbol(symbol); } if (isBindingPattern(node)) { return this.getTypeForVariableLikeDeclaration((node.parent), /*includeOptionality*/ true); } if (this.isInRightSideOfImportOrExportAssignment((node))) { const symbol = this.getSymbolAtLocation(node); const declaredType = symbol && this.getDeclaredTypeOfSymbol(symbol); return declaredType !== this.unknownType ? declaredType : this.getTypeOfSymbol(symbol); } return this.unknownType; } // Gets the type of object literal or array literal of destructuring assignment. // { a } from // for ( { a } of elems) { // } // [ a ] from // [a] = [ some array ...] public getTypeOfArrayLiteralOrObjectLiteralDestructuringAssignment(expr: Expression): Type { Debug.assert(expr.kind === SyntaxKind.ObjectLiteralExpression || expr.kind === SyntaxKind.ArrayLiteralExpression); // If this is from "for of" // for ( { a } of elems) { // } if (expr.parent.kind === SyntaxKind.ForOfStatement) { const iteratedType = this.checkRightHandSideOfForOf((expr.parent).expression, (expr.parent).awaitModifier); return this.checkDestructuringAssignment(expr, iteratedType || this.unknownType); } // If this is from "for" initializer // for ({a } = elems[0];.....) { } if (expr.parent.kind === SyntaxKind.BinaryExpression) { const iteratedType = this.getTypeOfExpression((expr.parent).right); return this.checkDestructuringAssignment(expr, iteratedType || this.unknownType); } // If this is from nested object binding pattern // for ({ skills: { primary, secondary } } = multiRobot, i = 0; i < 1; i++) { if (expr.parent.kind === SyntaxKind.PropertyAssignment) { const typeOfParentObjectLiteral = this.getTypeOfArrayLiteralOrObjectLiteralDestructuringAssignment((expr.parent.parent)); return this.checkObjectLiteralDestructuringPropertyAssignment(typeOfParentObjectLiteral || this.unknownType, (expr.parent)); } // Array literal assignment - array destructuring pattern Debug.assert(expr.parent.kind === SyntaxKind.ArrayLiteralExpression); // [{ property1: p1, property2 }] = elems; const typeOfArrayLiteral = this.getTypeOfArrayLiteralOrObjectLiteralDestructuringAssignment((expr.parent)); const elementType = this.checkIteratedTypeOrElementType(typeOfArrayLiteral || this.unknownType, expr.parent, /*allowStringInput*/ false, /*allowAsyncIterables*/ false) || this.unknownType; return this.checkArrayLiteralDestructuringElementAssignment((expr.parent), typeOfArrayLiteral, indexOf((expr.parent).elements, expr), elementType || this.unknownType); } // Gets the property symbol corresponding to the property in destructuring assignment // 'property1' from // for ( { property1: a } of elems) { // } // 'property1' at location 'a' from: // [a] = [ property1, property2 ] public getPropertySymbolOfDestructuringAssignment(location: Identifier) { // Get the type of the object or array literal and then look for property of given name in the type const typeOfObjectLiteral = this.getTypeOfArrayLiteralOrObjectLiteralDestructuringAssignment((location.parent.parent)); return typeOfObjectLiteral && this.getPropertyOfType(typeOfObjectLiteral, location.escapedText); } public getRegularTypeOfExpression(expr: Expression): Type { if (isRightSideOfQualifiedNameOrPropertyAccess(expr)) { expr = expr.parent; } return this.getRegularTypeOfLiteralType(this.getTypeOfExpression(expr)); } /** * Gets either the static or instance type of a class element, based on * whether the element is declared as "static". */ public getParentTypeOfClassElement(node: ClassElement) { const classSymbol = this.getSymbolOfNode(node.parent); return hasModifier(node, ModifierFlags.Static) ? this.getTypeOfSymbol(classSymbol) : this.getDeclaredTypeOfSymbol(classSymbol); } // Return the list of properties of the given type, augmented with properties from Function // if the type has call or construct signatures public getAugmentedPropertiesOfType(type: Type): Symbol[] { type = this.getApparentType(type); const propsByName = createSymbolTable(this.getPropertiesOfType(type)); if (this.getSignaturesOfType(type, SignatureKind.Call).length || this.getSignaturesOfType(type, SignatureKind.Construct).length) { forEach(this.getPropertiesOfType(this.globalFunctionType), p => { if (!propsByName.has(p.escapedName)) { propsByName.set(p.escapedName, p); } }); } return this.getNamedMembers(propsByName); } public getRootSymbols(symbol: Symbol): Symbol[] { const __conv_self__ = this; if (getCheckFlags(symbol) & CheckFlags.Synthetic) { const symbols: Symbol[] = []; const name = symbol.escapedName; forEach(this.getSymbolLinks(symbol).containingType.types, t => { const symbol = __conv_self__.getPropertyOfType(t, name); if (symbol) { symbols.push(symbol); } }); return symbols; } else if (symbol.flags & SymbolFlags.Transient) { const transient = symbol as TransientSymbol; if (transient.leftSpread) { return [...this.getRootSymbols(transient.leftSpread), ...this.getRootSymbols(transient.rightSpread)]; } if (transient.syntheticOrigin) { return this.getRootSymbols(transient.syntheticOrigin); } let target: Symbol; let next = symbol; while (next = this.getSymbolLinks(next).target) { target = next; } if (target) { return [target]; } } return [symbol]; } // Emitter support public isArgumentsLocalBinding(node: Identifier): boolean { if (!isGeneratedIdentifier(node)) { node = getParseTreeNode(node, isIdentifier); if (node) { const isPropertyName = node.parent.kind === SyntaxKind.PropertyAccessExpression && (node.parent).name === node; return !isPropertyName && this.getReferencedValueSymbol(node) === this.argumentsSymbol; } } return false; } public moduleExportsSomeValue(moduleReferenceExpression: Expression): boolean { const __conv_self__ = this; let moduleSymbol = this.resolveExternalModuleName(moduleReferenceExpression.parent, moduleReferenceExpression); if (!moduleSymbol || isShorthandAmbientModuleSymbol(moduleSymbol)) { // If the module is not found or is shorthand, assume that it may export a value. return true; } const hasExportAssignment = this.hasExportAssignmentSymbol(moduleSymbol); // if module has export assignment then 'resolveExternalModuleSymbol' will return resolved symbol for export assignment // otherwise it will return moduleSymbol itself moduleSymbol = this.resolveExternalModuleSymbol(moduleSymbol); const symbolLinks = this.getSymbolLinks(moduleSymbol); if (symbolLinks.exportsSomeValue === undefined) { // for export assignments - check if resolved symbol for RHS is itself a value // otherwise - check if at least one export is value symbolLinks.exportsSomeValue = hasExportAssignment ? !!(moduleSymbol.flags & SymbolFlags.Value) : forEachEntry(this.getExportsOfModule(moduleSymbol), isValue); } return symbolLinks.exportsSomeValue; function isValue(s: Symbol): boolean { s = __conv_self__.resolveSymbol(s); return s && !!(s.flags & SymbolFlags.Value); } } public isNameOfModuleOrEnumDeclaration(node: Identifier) { const parent = node.parent; return parent && isModuleOrEnumDeclaration(parent) && node === parent.name; } // When resolved as an expression identifier, if the given node references an exported entity, return the declaration // node of the exported entity's container. Otherwise, return undefined. public getReferencedExportContainer(node: Identifier, prefixLocals?: boolean): SourceFile | ModuleDeclaration | EnumDeclaration | undefined { const __conv_self__ = this; node = getParseTreeNode(node, isIdentifier); if (node) { // When resolving the export container for the name of a module or enum // declaration, we need to start resolution at the declaration's container. // Otherwise, we could incorrectly resolve the export container as the // declaration if it contains an exported member with the same name. let symbol = this.getReferencedValueSymbol(node, /*startInDeclarationContainer*/ this.isNameOfModuleOrEnumDeclaration(node)); if (symbol) { if (symbol.flags & SymbolFlags.ExportValue) { // If we reference an exported entity within the same module declaration, then whether // we prefix depends on the kind of entity. SymbolFlags.ExportHasLocal encompasses all the // kinds that we do NOT prefix. const exportSymbol = this.getMergedSymbol(symbol.exportSymbol); if (!prefixLocals && exportSymbol.flags & SymbolFlags.ExportHasLocal) { return undefined; } symbol = exportSymbol; } const parentSymbol = this.getParentOfSymbol(symbol); if (parentSymbol) { if (parentSymbol.flags & SymbolFlags.ValueModule && parentSymbol.valueDeclaration.kind === SyntaxKind.SourceFile) { const symbolFile = parentSymbol.valueDeclaration; const referenceFile = getSourceFileOfNode(node); // If `node` accesses an export and that export isn't in the same file, then symbol is a namespace export, so return undefined. const symbolIsUmdExport = symbolFile !== referenceFile; return symbolIsUmdExport ? undefined : symbolFile; } return findAncestor(node.parent, (n): n is ModuleDeclaration | EnumDeclaration => isModuleOrEnumDeclaration(n) && __conv_self__.getSymbolOfNode(n) === parentSymbol); } } } } // When resolved as an expression identifier, if the given node references an import, return the declaration of // that import. Otherwise, return undefined. public getReferencedImportDeclaration(node: Identifier): Declaration { node = getParseTreeNode(node, isIdentifier); if (node) { const symbol = this.getReferencedValueSymbol(node); // We should only get the declaration of an alias if there isn't a local value // declaration for the symbol if (this.isNonLocalAlias(symbol, /*excludes*/ SymbolFlags.Value)) { return this.getDeclarationOfAliasSymbol(symbol); } } return undefined; } public isSymbolOfDeclarationWithCollidingName(symbol: Symbol): boolean { if (symbol.flags & SymbolFlags.BlockScoped) { const links = this.getSymbolLinks(symbol); if (links.isDeclarationWithCollidingName === undefined) { const container = getEnclosingBlockScopeContainer(symbol.valueDeclaration); if (isStatementWithLocals(container)) { const nodeLinks = this.getNodeLinks(symbol.valueDeclaration); if (!!this.resolveName(container.parent, symbol.escapedName, SymbolFlags.Value, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined)) { // redeclaration - always should be renamed links.isDeclarationWithCollidingName = true; } else if (nodeLinks.flags & NodeCheckFlags.CapturedBlockScopedBinding) { // binding is captured in the function // should be renamed if: // - binding is not top level - top level bindings never collide with anything // AND // - binding is not declared in loop, should be renamed to avoid name reuse across siblings // let a, b // { let x = 1; a = () => x; } // { let x = 100; b = () => x; } // console.log(a()); // should print '1' // console.log(b()); // should print '100' // OR // - binding is declared inside loop but not in inside initializer of iteration statement or directly inside loop body // * variables from initializer are passed to rewritten loop body as parameters so they are not captured directly // * variables that are declared immediately in loop body will become top level variable after loop is rewritten and thus // they will not collide with anything const isDeclaredInLoop = nodeLinks.flags & NodeCheckFlags.BlockScopedBindingInLoop; const inLoopInitializer = isIterationStatement(container, /*lookInLabeledStatements*/ false); const inLoopBodyBlock = container.kind === SyntaxKind.Block && isIterationStatement(container.parent, /*lookInLabeledStatements*/ false); links.isDeclarationWithCollidingName = !isBlockScopedContainerTopLevel(container) && (!isDeclaredInLoop || (!inLoopInitializer && !inLoopBodyBlock)); } else { links.isDeclarationWithCollidingName = false; } } } return links.isDeclarationWithCollidingName; } return false; } // When resolved as an expression identifier, if the given node references a nested block scoped entity with // a name that either hides an existing name or might hide it when compiled downlevel, // return the declaration of that entity. Otherwise, return undefined. public getReferencedDeclarationWithCollidingName(node: Identifier): Declaration { if (!isGeneratedIdentifier(node)) { node = getParseTreeNode(node, isIdentifier); if (node) { const symbol = this.getReferencedValueSymbol(node); if (symbol && this.isSymbolOfDeclarationWithCollidingName(symbol)) { return symbol.valueDeclaration; } } } return undefined; } // Return true if the given node is a declaration of a nested block scoped entity with a name that either hides an // existing name or might hide a name when compiled downlevel public isDeclarationWithCollidingName(node: Declaration): boolean { node = getParseTreeNode(node, isDeclaration); if (node) { const symbol = this.getSymbolOfNode(node); if (symbol) { return this.isSymbolOfDeclarationWithCollidingName(symbol); } } return false; } public isValueAliasDeclaration(node: Node): boolean { switch (node.kind) { case SyntaxKind.ImportEqualsDeclaration: case SyntaxKind.ImportClause: case SyntaxKind.NamespaceImport: case SyntaxKind.ImportSpecifier: case SyntaxKind.ExportSpecifier: return this.isAliasResolvedToValue(this.getSymbolOfNode(node) || this.unknownSymbol); case SyntaxKind.ExportDeclaration: const exportClause = (node).exportClause; return exportClause && forEach(exportClause.elements, this.isValueAliasDeclaration); case SyntaxKind.ExportAssignment: return (node).expression && (node).expression.kind === SyntaxKind.Identifier ? this.isAliasResolvedToValue(this.getSymbolOfNode(node) || this.unknownSymbol) : true; } return false; } public isTopLevelValueImportEqualsWithEntityName(node: ImportEqualsDeclaration): boolean { node = getParseTreeNode(node, isImportEqualsDeclaration); if (node === undefined || node.parent.kind !== SyntaxKind.SourceFile || !isInternalModuleImportEqualsDeclaration(node)) { // parent is not source file or it is not reference to internal module return false; } const isValue = this.isAliasResolvedToValue(this.getSymbolOfNode(node)); return isValue && node.moduleReference && !nodeIsMissing(node.moduleReference); } public isAliasResolvedToValue(symbol: Symbol): boolean { const target = this.resolveAlias(symbol); if (target === this.unknownSymbol) { return true; } // const enums and modules that contain only const enums are not considered values from the emit perspective // unless 'preserveConstEnums' option is set to true return target.flags & SymbolFlags.Value && (this.compilerOptions.preserveConstEnums || !this.isConstEnumOrConstEnumOnlyModule(target)); } public isConstEnumOrConstEnumOnlyModule(s: Symbol): boolean { return this.isConstEnumSymbol(s) || s.constEnumOnlyModule; } public isReferencedAliasDeclaration(node: Node, checkChildren?: boolean): boolean { const __conv_self__ = this; if (isAliasSymbolDeclaration(node)) { const symbol = this.getSymbolOfNode(node); if (symbol && this.getSymbolLinks(symbol).referenced) { return true; } } if (checkChildren) { return forEachChild(node, node => __conv_self__.isReferencedAliasDeclaration(node, checkChildren)); } return false; } public isImplementationOfOverload(node: FunctionLike) { if (nodeIsPresent((node as FunctionLikeDeclaration).body)) { const symbol = this.getSymbolOfNode(node); const signaturesOfSymbol = this.getSignaturesOfSymbol(symbol); // If this function body corresponds to function with multiple signature, it is implementation of overload // e.g.: function foo(a: string): string; // function foo(a: number): number; // function foo(a: any) { // This is implementation of the overloads // return a; // } return signaturesOfSymbol.length > 1 || // If there is single signature for the symbol, it is overload if that signature isn't coming from the node // e.g.: function foo(a: string): string; // function foo(a: any) { // This is implementation of the overloads // return a; // } (signaturesOfSymbol.length === 1 && signaturesOfSymbol[0].declaration !== node); } return false; } public isRequiredInitializedParameter(parameter: ParameterDeclaration) { return this.strictNullChecks && !this.isOptionalParameter(parameter) && parameter.initializer && !hasModifier(parameter, ModifierFlags.ParameterPropertyModifier); } public isOptionalUninitializedParameterProperty(parameter: ParameterDeclaration) { return this.strictNullChecks && this.isOptionalParameter(parameter) && !parameter.initializer && hasModifier(parameter, ModifierFlags.ParameterPropertyModifier); } public getNodeCheckFlags(node: Node): NodeCheckFlags { return this.getNodeLinks(node).flags; } public getEnumMemberValue(node: EnumMember): string | number { this.computeEnumMemberValues((node.parent)); return this.getNodeLinks(node).enumMemberValue; } public canHaveConstantValue(node: Node): node is EnumMember | PropertyAccessExpression | ElementAccessExpression { switch (node.kind) { case SyntaxKind.EnumMember: case SyntaxKind.PropertyAccessExpression: case SyntaxKind.ElementAccessExpression: return true; } return false; } public getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): string | number { if (node.kind === SyntaxKind.EnumMember) { return this.getEnumMemberValue((node)); } const symbol = this.getNodeLinks(node).resolvedSymbol; if (symbol && (symbol.flags & SymbolFlags.EnumMember)) { // inline property\index accesses only for const enums if (isConstEnumDeclaration(symbol.valueDeclaration.parent)) { return this.getEnumMemberValue((symbol.valueDeclaration)); } } return undefined; } public isFunctionType(type: Type): boolean { return type.flags & TypeFlags.Object && this.getSignaturesOfType(type, SignatureKind.Call).length > 0; } public getTypeReferenceSerializationKind(typeName: EntityName, location?: Node): TypeReferenceSerializationKind { // Resolve the symbol as a value to ensure the type can be reached at runtime during emit. const valueSymbol = this.resolveEntityName(typeName, SymbolFlags.Value, /*ignoreErrors*/ true, /*dontResolveAlias*/ false, location); // Resolve the symbol as a type so that we can provide a more useful hint for the type serializer. const typeSymbol = this.resolveEntityName(typeName, SymbolFlags.Type, /*ignoreErrors*/ true, /*dontResolveAlias*/ false, location); if (valueSymbol && valueSymbol === typeSymbol) { const globalPromiseSymbol = this.getGlobalPromiseConstructorSymbol(/*reportErrors*/ false); if (globalPromiseSymbol && valueSymbol === globalPromiseSymbol) { return TypeReferenceSerializationKind.Promise; } const constructorType = this.getTypeOfSymbol(valueSymbol); if (constructorType && this.isConstructorType(constructorType)) { return TypeReferenceSerializationKind.TypeWithConstructSignatureAndValue; } } // We might not be able to resolve type symbol so use unknown type in that case (eg error case) if (!typeSymbol) { return TypeReferenceSerializationKind.ObjectType; } const type = this.getDeclaredTypeOfSymbol(typeSymbol); if (type === this.unknownType) { return TypeReferenceSerializationKind.Unknown; } else if (type.flags & TypeFlags.Any) { return TypeReferenceSerializationKind.ObjectType; } else if (this.isTypeAssignableToKind(type, TypeFlags.Void | TypeFlags.Nullable | TypeFlags.Never)) { return TypeReferenceSerializationKind.VoidNullableOrNeverType; } else if (this.isTypeAssignableToKind(type, TypeFlags.BooleanLike)) { return TypeReferenceSerializationKind.BooleanType; } else if (this.isTypeAssignableToKind(type, TypeFlags.NumberLike)) { return TypeReferenceSerializationKind.NumberLikeType; } else if (this.isTypeAssignableToKind(type, TypeFlags.StringLike)) { return TypeReferenceSerializationKind.StringLikeType; } else if (this.isTupleType(type)) { return TypeReferenceSerializationKind.ArrayLikeType; } else if (this.isTypeAssignableToKind(type, TypeFlags.ESSymbol)) { return TypeReferenceSerializationKind.ESSymbolType; } else if (this.isFunctionType(type)) { return TypeReferenceSerializationKind.TypeWithCallSignature; } else if (this.isArrayType(type)) { return TypeReferenceSerializationKind.ArrayLikeType; } else { return TypeReferenceSerializationKind.ObjectType; } } public writeTypeOfDeclaration(declaration: AccessorDeclaration | VariableLikeDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter) { // Get type of the symbol if this is the valid symbol otherwise get type at location const symbol = this.getSymbolOfNode(declaration); let type = symbol && !(symbol.flags & (SymbolFlags.TypeLiteral | SymbolFlags.Signature)) ? this.getWidenedLiteralType(this.getTypeOfSymbol(symbol)) : this.unknownType; if (flags & TypeFormatFlags.AddUndefined) { type = this.getNullableType(type, TypeFlags.Undefined); } this.getSymbolDisplayBuilder().buildTypeDisplay(type, writer, enclosingDeclaration, flags); } public writeReturnTypeOfSignatureDeclaration(signatureDeclaration: SignatureDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter) { const signature = this.getSignatureFromDeclaration(signatureDeclaration); this.getSymbolDisplayBuilder().buildTypeDisplay(this.getReturnTypeOfSignature(signature), writer, enclosingDeclaration, flags); } public writeTypeOfExpression(expr: Expression, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter) { const type = this.getWidenedType(this.getRegularTypeOfExpression(expr)); this.getSymbolDisplayBuilder().buildTypeDisplay(type, writer, enclosingDeclaration, flags); } public hasGlobalName(name: string): boolean { return this.globals.has(escapeLeadingUnderscores(name)); } public getReferencedValueSymbol(reference: Identifier, startInDeclarationContainer?: boolean): Symbol { const resolvedSymbol = this.getNodeLinks(reference).resolvedSymbol; if (resolvedSymbol) { return resolvedSymbol; } let location: Node = reference; if (startInDeclarationContainer) { // When resolving the name of a declaration as a value, we need to start resolution // at a point outside of the declaration. const parent = reference.parent; if (isDeclaration(parent) && reference === parent.name) { location = this.getDeclarationContainer(parent); } } return this.resolveName(location, reference.escapedText, SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias, /*nodeNotFoundMessage*/ undefined, /*nameArg*/ undefined); } public getReferencedValueDeclaration(reference: Identifier): Declaration { if (!isGeneratedIdentifier(reference)) { reference = getParseTreeNode(reference, isIdentifier); if (reference) { const symbol = this.getReferencedValueSymbol(reference); if (symbol) { return this.getExportSymbolOfValueSymbolIfExported(symbol).valueDeclaration; } } } return undefined; } public isLiteralConstDeclaration(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration): boolean { if (isConst(node)) { const type = this.getTypeOfSymbol(this.getSymbolOfNode(node)); return !!(type.flags & TypeFlags.StringOrNumberLiteral && type.flags & TypeFlags.FreshLiteral); } return false; } public writeLiteralConstValue(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration, writer: SymbolWriter) { const type = this.getTypeOfSymbol(this.getSymbolOfNode(node)); writer.writeStringLiteral(this.literalTypeToString((type))); } public createResolver(): EmitResolver { const __conv_self__ = this; // this variable and functions that use it are deliberately moved here from the outer scope // to avoid scope pollution const resolvedTypeReferenceDirectives = this.host.getResolvedTypeReferenceDirectives(); let fileToDirective: Map; if (resolvedTypeReferenceDirectives) { // populate reverse mapping: file path -> type reference directive that was resolved to this file fileToDirective = createMap(); resolvedTypeReferenceDirectives.forEach((resolvedDirective, key) => { if (!resolvedDirective) { return; } const file = __conv_self__.host.getSourceFile(resolvedDirective.resolvedFileName); fileToDirective.set(file.path, key); }); } return { getReferencedExportContainer: this.getReferencedExportContainer, getReferencedImportDeclaration: this.getReferencedImportDeclaration, getReferencedDeclarationWithCollidingName: this.getReferencedDeclarationWithCollidingName, isDeclarationWithCollidingName: this.isDeclarationWithCollidingName, isValueAliasDeclaration: node => { node = getParseTreeNode(node); // Synthesized nodes are always treated like values. return node ? __conv_self__.isValueAliasDeclaration(node) : true; }, hasGlobalName: this.hasGlobalName, isReferencedAliasDeclaration: (node, checkChildren?) => { node = getParseTreeNode(node); // Synthesized nodes are always treated as referenced. return node ? __conv_self__.isReferencedAliasDeclaration(node, checkChildren) : true; }, getNodeCheckFlags: node => { node = getParseTreeNode(node); return node ? __conv_self__.getNodeCheckFlags(node) : undefined; }, isTopLevelValueImportEqualsWithEntityName: this.isTopLevelValueImportEqualsWithEntityName, isDeclarationVisible: this.isDeclarationVisible, isImplementationOfOverload: this.isImplementationOfOverload, isRequiredInitializedParameter: this.isRequiredInitializedParameter, isOptionalUninitializedParameterProperty: this.isOptionalUninitializedParameterProperty, writeTypeOfDeclaration: this.writeTypeOfDeclaration, writeReturnTypeOfSignatureDeclaration: this.writeReturnTypeOfSignatureDeclaration, writeTypeOfExpression: this.writeTypeOfExpression, isSymbolAccessible: this.isSymbolAccessible, isEntityNameVisible: this.isEntityNameVisible, getConstantValue: node => { node = getParseTreeNode(node, __conv_self__.canHaveConstantValue); return node ? __conv_self__.getConstantValue(node) : undefined; }, collectLinkedAliases: this.collectLinkedAliases, getReferencedValueDeclaration: this.getReferencedValueDeclaration, getTypeReferenceSerializationKind: this.getTypeReferenceSerializationKind, isOptionalParameter: this.isOptionalParameter, moduleExportsSomeValue: this.moduleExportsSomeValue, isArgumentsLocalBinding: this.isArgumentsLocalBinding, getExternalModuleFileFromDeclaration: this.getExternalModuleFileFromDeclaration, getTypeReferenceDirectivesForEntityName: getTypeReferenceDirectivesForEntityName, getTypeReferenceDirectivesForSymbol: getTypeReferenceDirectivesForSymbol, isLiteralConstDeclaration: this.isLiteralConstDeclaration, writeLiteralConstValue: this.writeLiteralConstValue, getJsxFactoryEntity: () => __conv_self__._jsxFactoryEntity }; // defined here to avoid outer scope pollution function getTypeReferenceDirectivesForEntityName(node: EntityNameOrEntityNameExpression): string[] { // program does not have any files with type reference directives - bail out if (!fileToDirective) { return undefined; } // property access can only be used as values // qualified names can only be used as types\namespaces // identifiers are treated as values only if they appear in type queries const meaning = (node.kind === SyntaxKind.PropertyAccessExpression) || (node.kind === SyntaxKind.Identifier && __conv_self__.isInTypeQuery(node)) ? SymbolFlags.Value | SymbolFlags.ExportValue : SymbolFlags.Type | SymbolFlags.Namespace; const symbol = __conv_self__.resolveEntityName(node, meaning, /*ignoreErrors*/ true); return symbol && symbol !== __conv_self__.unknownSymbol ? getTypeReferenceDirectivesForSymbol(symbol, meaning) : undefined; } // defined here to avoid outer scope pollution function getTypeReferenceDirectivesForSymbol(symbol: Symbol, meaning?: SymbolFlags): string[] { // program does not have any files with type reference directives - bail out if (!fileToDirective) { return undefined; } if (!isSymbolFromTypeDeclarationFile(symbol)) { return undefined; } // check what declarations in the symbol can contribute to the target meaning let typeReferenceDirectives: string[]; for (const decl of symbol.declarations) { // check meaning of the local symbol to see if declaration needs to be analyzed further if (decl.symbol && decl.symbol.flags & meaning) { const file = getSourceFileOfNode(decl); const typeReferenceDirective = fileToDirective.get(file.path); if (typeReferenceDirective) { (typeReferenceDirectives || (typeReferenceDirectives = [])).push(typeReferenceDirective); } else { // found at least one entry that does not originate from type reference directive return undefined; } } } return typeReferenceDirectives; } function isSymbolFromTypeDeclarationFile(symbol: Symbol): boolean { // bail out if symbol does not have associated declarations (i.e. this is transient symbol created for property in binding pattern) if (!symbol.declarations) { return false; } // walk the parent chain for symbols to make sure that top level parent symbol is in the global scope // external modules cannot define or contribute to type declaration files let current = symbol; while (true) { const parent = __conv_self__.getParentOfSymbol(current); if (parent) { current = parent; } else { break; } } if (current.valueDeclaration && current.valueDeclaration.kind === SyntaxKind.SourceFile && current.flags & SymbolFlags.ValueModule) { return false; } // check that at least one declaration of top level symbol originates from type declaration file for (const decl of symbol.declarations) { const file = getSourceFileOfNode(decl); if (fileToDirective.has(file.path)) { return true; } } return false; } } public getExternalModuleFileFromDeclaration(declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration): SourceFile { const specifier = getExternalModuleName(declaration); const moduleSymbol = this.resolveExternalModuleNameWorker(specifier, specifier, /*moduleNotFoundError*/ undefined); if (!moduleSymbol) { return undefined; } return getDeclarationOfKind(moduleSymbol, SyntaxKind.SourceFile) as SourceFile; } public initializeTypeChecker() { const __conv_self__ = this; // Bind all source files and propagate errors for (const file of this.host.getSourceFiles()) { bindSourceFile(file, this.compilerOptions); } // Initialize global symbol table let augmentations: ReadonlyArray[]; for (const file of this.host.getSourceFiles()) { if (!isExternalOrCommonJsModule(file)) { this.mergeSymbolTable(this.globals, file.locals); } if (file.patternAmbientModules && file.patternAmbientModules.length) { this.patternAmbientModules = concatenate(this.patternAmbientModules, file.patternAmbientModules); } if (file.moduleAugmentations.length) { (augmentations || (augmentations = [])).push(file.moduleAugmentations); } if (file.symbol && file.symbol.globalExports) { // Merge in UMD exports with first-in-wins semantics (see #9771) const source = file.symbol.globalExports; source.forEach((sourceSymbol, id) => { if (!__conv_self__.globals.has(id)) { __conv_self__.globals.set(id, sourceSymbol); } }); } } if (augmentations) { // merge module augmentations. // this needs to be done after global symbol table is initialized to make sure that all ambient modules are indexed for (const list of augmentations) { for (const augmentation of list) { this.mergeModuleAugmentation(augmentation); } } } // Setup global builtins this.addToSymbolTable(this.globals, this.builtinGlobals, Diagnostics.Declaration_name_conflicts_with_built_in_global_identifier_0); this.getSymbolLinks(this.undefinedSymbol).type = this.undefinedWideningType; this.getSymbolLinks(this.argumentsSymbol).type = this.getGlobalType(("IArguments" as __String), /*arity*/ 0, /*reportErrors*/ true); this.getSymbolLinks(this.unknownSymbol).type = this.unknownType; // Initialize special types this.globalArrayType = this.getGlobalType(("Array" as __String), /*arity*/ 1, /*reportErrors*/ true); this.globalObjectType = this.getGlobalType(("Object" as __String), /*arity*/ 0, /*reportErrors*/ true); this.globalFunctionType = this.getGlobalType(("Function" as __String), /*arity*/ 0, /*reportErrors*/ true); this.globalStringType = this.getGlobalType(("String" as __String), /*arity*/ 0, /*reportErrors*/ true); this.globalNumberType = this.getGlobalType(("Number" as __String), /*arity*/ 0, /*reportErrors*/ true); this.globalBooleanType = this.getGlobalType(("Boolean" as __String), /*arity*/ 0, /*reportErrors*/ true); this.globalRegExpType = this.getGlobalType(("RegExp" as __String), /*arity*/ 0, /*reportErrors*/ true); this.anyArrayType = this.createArrayType(this.anyType); this.autoArrayType = this.createArrayType(this.autoType); this.globalReadonlyArrayType = (this.getGlobalTypeOrUndefined(("ReadonlyArray" as __String), /*arity*/ 1)); this.anyReadonlyArrayType = this.globalReadonlyArrayType ? this.createTypeFromGenericGlobalType(this.globalReadonlyArrayType, [this.anyType]) : this.anyArrayType; this.globalThisType = (this.getGlobalTypeOrUndefined(("ThisType" as __String), /*arity*/ 1)); } public checkExternalEmitHelpers(location: Node, helpers: ExternalEmitHelpers) { if ((this.requestedExternalEmitHelpers & helpers) !== helpers && this.compilerOptions.importHelpers) { const sourceFile = getSourceFileOfNode(location); if (isEffectiveExternalModule(sourceFile, this.compilerOptions) && !isInAmbientContext(location)) { const helpersModule = this.resolveHelpersModule(sourceFile, location); if (helpersModule !== this.unknownSymbol) { const uncheckedHelpers = helpers & ~this.requestedExternalEmitHelpers; for (let helper = ExternalEmitHelpers.FirstEmitHelper; helper <= ExternalEmitHelpers.LastEmitHelper; helper <<= 1) { if (uncheckedHelpers & helper) { const name = this.getHelperName(helper); const symbol = this.getSymbol(helpersModule.exports, escapeLeadingUnderscores(name), SymbolFlags.Value); if (!symbol) { this.error(location, Diagnostics.This_syntax_requires_an_imported_helper_named_1_but_module_0_has_no_exported_member_1, externalHelpersModuleNameText, name); } } } } this.requestedExternalEmitHelpers |= helpers; } } } public getHelperName(helper: ExternalEmitHelpers) { switch (helper) { case ExternalEmitHelpers.Extends: return "__extends"; case ExternalEmitHelpers.Assign: return "__assign"; case ExternalEmitHelpers.Rest: return "__rest"; case ExternalEmitHelpers.Decorate: return "__decorate"; case ExternalEmitHelpers.Metadata: return "__metadata"; case ExternalEmitHelpers.Param: return "__param"; case ExternalEmitHelpers.Awaiter: return "__awaiter"; case ExternalEmitHelpers.Generator: return "__generator"; case ExternalEmitHelpers.Values: return "__values"; case ExternalEmitHelpers.Read: return "__read"; case ExternalEmitHelpers.Spread: return "__spread"; case ExternalEmitHelpers.Await: return "__await"; case ExternalEmitHelpers.AsyncGenerator: return "__asyncGenerator"; case ExternalEmitHelpers.AsyncDelegator: return "__asyncDelegator"; case ExternalEmitHelpers.AsyncValues: return "__asyncValues"; case ExternalEmitHelpers.ExportStar: return "__exportStar"; default: Debug.fail("Unrecognized helper"); } } public resolveHelpersModule(node: SourceFile, errorNode: Node) { if (!this.externalHelpersModule) { this.externalHelpersModule = this.resolveExternalModule(node, externalHelpersModuleNameText, Diagnostics.This_syntax_requires_an_imported_helper_but_module_0_cannot_be_found, errorNode) || this.unknownSymbol; } return this.externalHelpersModule; } // GRAMMAR CHECKING public checkGrammarDecorators(node: Node): boolean { if (!node.decorators) { return false; } if (!nodeCanBeDecorated(node)) { if (node.kind === SyntaxKind.MethodDeclaration && !ts.nodeIsPresent((node).body)) { return this.grammarErrorOnFirstToken(node, Diagnostics.A_decorator_can_only_decorate_a_method_implementation_not_an_overload); } else { return this.grammarErrorOnFirstToken(node, Diagnostics.Decorators_are_not_valid_here); } } else if (node.kind === SyntaxKind.GetAccessor || node.kind === SyntaxKind.SetAccessor) { const accessors = getAllAccessorDeclarations((node.parent).members, node); if (accessors.firstAccessor.decorators && node === accessors.secondAccessor) { return this.grammarErrorOnFirstToken(node, Diagnostics.Decorators_cannot_be_applied_to_multiple_get_Slashset_accessors_of_the_same_name); } } return false; } public checkGrammarModifiers(node: Node): boolean { const quickResult = this.reportObviousModifierErrors(node); if (quickResult !== undefined) { return quickResult; } let lastStatic: Node, lastPrivate: Node, lastProtected: Node, lastDeclare: Node, lastAsync: Node, lastReadonly: Node; let flags = ModifierFlags.None; for (const modifier of node.modifiers) { if (modifier.kind !== SyntaxKind.ReadonlyKeyword) { if (node.kind === SyntaxKind.PropertySignature || node.kind === SyntaxKind.MethodSignature) { return this.grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_type_member, tokenToString(modifier.kind)); } if (node.kind === SyntaxKind.IndexSignature) { return this.grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_an_index_signature, tokenToString(modifier.kind)); } } switch (modifier.kind) { case SyntaxKind.ConstKeyword: if (node.kind !== SyntaxKind.EnumDeclaration && node.parent.kind === SyntaxKind.ClassDeclaration) { return this.grammarErrorOnNode(node, Diagnostics.A_class_member_cannot_have_the_0_keyword, tokenToString(SyntaxKind.ConstKeyword)); } break; case SyntaxKind.PublicKeyword: case SyntaxKind.ProtectedKeyword: case SyntaxKind.PrivateKeyword: const text = this.visibilityToString(modifierToFlag(modifier.kind)); if (modifier.kind === SyntaxKind.ProtectedKeyword) { lastProtected = modifier; } else if (modifier.kind === SyntaxKind.PrivateKeyword) { lastPrivate = modifier; } if (flags & ModifierFlags.AccessibilityModifier) { return this.grammarErrorOnNode(modifier, Diagnostics.Accessibility_modifier_already_seen); } else if (flags & ModifierFlags.Static) { return this.grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, text, "static"); } else if (flags & ModifierFlags.Readonly) { return this.grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, text, "readonly"); } else if (flags & ModifierFlags.Async) { return this.grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, text, "async"); } else if (node.parent.kind === SyntaxKind.ModuleBlock || node.parent.kind === SyntaxKind.SourceFile) { return this.grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_module_or_namespace_element, text); } else if (flags & ModifierFlags.Abstract) { if (modifier.kind === SyntaxKind.PrivateKeyword) { return this.grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_be_used_with_1_modifier, text, "abstract"); } else { return this.grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, text, "abstract"); } } flags |= modifierToFlag(modifier.kind); break; case SyntaxKind.StaticKeyword: if (flags & ModifierFlags.Static) { return this.grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "static"); } else if (flags & ModifierFlags.Readonly) { return this.grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "static", "readonly"); } else if (flags & ModifierFlags.Async) { return this.grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "static", "async"); } else if (node.parent.kind === SyntaxKind.ModuleBlock || node.parent.kind === SyntaxKind.SourceFile) { return this.grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_module_or_namespace_element, "static"); } else if (node.kind === SyntaxKind.Parameter) { return this.grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_parameter, "static"); } else if (flags & ModifierFlags.Abstract) { return this.grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_be_used_with_1_modifier, "static", "abstract"); } flags |= ModifierFlags.Static; lastStatic = modifier; break; case SyntaxKind.ReadonlyKeyword: if (flags & ModifierFlags.Readonly) { return this.grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "readonly"); } else if (node.kind !== SyntaxKind.PropertyDeclaration && node.kind !== SyntaxKind.PropertySignature && node.kind !== SyntaxKind.IndexSignature && node.kind !== SyntaxKind.Parameter) { // If node.kind === SyntaxKind.Parameter, checkParameter report an error if it's not a parameter property. return this.grammarErrorOnNode(modifier, Diagnostics.readonly_modifier_can_only_appear_on_a_property_declaration_or_index_signature); } flags |= ModifierFlags.Readonly; lastReadonly = modifier; break; case SyntaxKind.ExportKeyword: if (flags & ModifierFlags.Export) { return this.grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "export"); } else if (flags & ModifierFlags.Ambient) { return this.grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "export", "declare"); } else if (flags & ModifierFlags.Abstract) { return this.grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "export", "abstract"); } else if (flags & ModifierFlags.Async) { return this.grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "export", "async"); } else if (node.parent.kind === SyntaxKind.ClassDeclaration) { return this.grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_class_element, "export"); } else if (node.kind === SyntaxKind.Parameter) { return this.grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_parameter, "export"); } flags |= ModifierFlags.Export; break; case SyntaxKind.DefaultKeyword: const container = node.parent.kind === SyntaxKind.SourceFile ? node.parent : node.parent.parent; if (container.kind === SyntaxKind.ModuleDeclaration && !isAmbientModule(container)) { return this.grammarErrorOnNode(modifier, Diagnostics.A_default_export_can_only_be_used_in_an_ECMAScript_style_module); } flags |= ModifierFlags.Default; break; case SyntaxKind.DeclareKeyword: if (flags & ModifierFlags.Ambient) { return this.grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "declare"); } else if (flags & ModifierFlags.Async) { return this.grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_be_used_in_an_ambient_context, "async"); } else if (node.parent.kind === SyntaxKind.ClassDeclaration) { return this.grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_class_element, "declare"); } else if (node.kind === SyntaxKind.Parameter) { return this.grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_parameter, "declare"); } else if (isInAmbientContext(node.parent) && node.parent.kind === SyntaxKind.ModuleBlock) { return this.grammarErrorOnNode(modifier, Diagnostics.A_declare_modifier_cannot_be_used_in_an_already_ambient_context); } flags |= ModifierFlags.Ambient; lastDeclare = modifier; break; case SyntaxKind.AbstractKeyword: if (flags & ModifierFlags.Abstract) { return this.grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "abstract"); } if (node.kind !== SyntaxKind.ClassDeclaration) { if (node.kind !== SyntaxKind.MethodDeclaration && node.kind !== SyntaxKind.PropertyDeclaration && node.kind !== SyntaxKind.GetAccessor && node.kind !== SyntaxKind.SetAccessor) { return this.grammarErrorOnNode(modifier, Diagnostics.abstract_modifier_can_only_appear_on_a_class_method_or_property_declaration); } if (!(node.parent.kind === SyntaxKind.ClassDeclaration && hasModifier(node.parent, ModifierFlags.Abstract))) { return this.grammarErrorOnNode(modifier, Diagnostics.Abstract_methods_can_only_appear_within_an_abstract_class); } if (flags & ModifierFlags.Static) { return this.grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_be_used_with_1_modifier, "static", "abstract"); } if (flags & ModifierFlags.Private) { return this.grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_be_used_with_1_modifier, "private", "abstract"); } } flags |= ModifierFlags.Abstract; break; case SyntaxKind.AsyncKeyword: if (flags & ModifierFlags.Async) { return this.grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "async"); } else if (flags & ModifierFlags.Ambient || isInAmbientContext(node.parent)) { return this.grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_be_used_in_an_ambient_context, "async"); } else if (node.kind === SyntaxKind.Parameter) { return this.grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_parameter, "async"); } flags |= ModifierFlags.Async; lastAsync = modifier; break; } } if (node.kind === SyntaxKind.Constructor) { if (flags & ModifierFlags.Static) { return this.grammarErrorOnNode(lastStatic, Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration, "static"); } if (flags & ModifierFlags.Abstract) { return this.grammarErrorOnNode(lastStatic, Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration, "abstract"); } else if (flags & ModifierFlags.Async) { return this.grammarErrorOnNode(lastAsync, Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration, "async"); } else if (flags & ModifierFlags.Readonly) { return this.grammarErrorOnNode(lastReadonly, Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration, "readonly"); } return; } else if ((node.kind === SyntaxKind.ImportDeclaration || node.kind === SyntaxKind.ImportEqualsDeclaration) && flags & ModifierFlags.Ambient) { return this.grammarErrorOnNode(lastDeclare, Diagnostics.A_0_modifier_cannot_be_used_with_an_import_declaration, "declare"); } else if (node.kind === SyntaxKind.Parameter && (flags & ModifierFlags.ParameterPropertyModifier) && isBindingPattern((node).name)) { return this.grammarErrorOnNode(node, Diagnostics.A_parameter_property_may_not_be_declared_using_a_binding_pattern); } else if (node.kind === SyntaxKind.Parameter && (flags & ModifierFlags.ParameterPropertyModifier) && (node).dotDotDotToken) { return this.grammarErrorOnNode(node, Diagnostics.A_parameter_property_cannot_be_declared_using_a_rest_parameter); } if (flags & ModifierFlags.Async) { return this.checkGrammarAsyncModifier(node, lastAsync); } } /** * true | false: Early return this value from checkGrammarModifiers. * undefined: Need to do full checking on the modifiers. */ public reportObviousModifierErrors(node: Node): boolean | undefined { return !node.modifiers ? false : this.shouldReportBadModifier(node) ? this.grammarErrorOnFirstToken(node, Diagnostics.Modifiers_cannot_appear_here) : undefined; } public shouldReportBadModifier(node: Node): boolean { switch (node.kind) { case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: case SyntaxKind.Constructor: case SyntaxKind.PropertyDeclaration: case SyntaxKind.PropertySignature: case SyntaxKind.MethodDeclaration: case SyntaxKind.MethodSignature: case SyntaxKind.IndexSignature: case SyntaxKind.ModuleDeclaration: case SyntaxKind.ImportDeclaration: case SyntaxKind.ImportEqualsDeclaration: case SyntaxKind.ExportDeclaration: case SyntaxKind.ExportAssignment: case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: case SyntaxKind.Parameter: return false; default: if (node.parent.kind === SyntaxKind.ModuleBlock || node.parent.kind === SyntaxKind.SourceFile) { return false; } switch (node.kind) { case SyntaxKind.FunctionDeclaration: return this.nodeHasAnyModifiersExcept(node, SyntaxKind.AsyncKeyword); case SyntaxKind.ClassDeclaration: return this.nodeHasAnyModifiersExcept(node, SyntaxKind.AbstractKeyword); case SyntaxKind.InterfaceDeclaration: case SyntaxKind.VariableStatement: case SyntaxKind.TypeAliasDeclaration: return true; case SyntaxKind.EnumDeclaration: return this.nodeHasAnyModifiersExcept(node, SyntaxKind.ConstKeyword); default: Debug.fail(); return false; } } } public nodeHasAnyModifiersExcept(node: Node, allowedModifier: SyntaxKind): boolean { return node.modifiers.length > 1 || node.modifiers[0].kind !== allowedModifier; } public checkGrammarAsyncModifier(node: Node, asyncModifier: Node): boolean { switch (node.kind) { case SyntaxKind.MethodDeclaration: case SyntaxKind.FunctionDeclaration: case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: return false; } return this.grammarErrorOnNode(asyncModifier, Diagnostics._0_modifier_cannot_be_used_here, "async"); } public checkGrammarForDisallowedTrailingComma(list: NodeArray): boolean { if (list && list.hasTrailingComma) { const start = list.end - ",".length; const end = list.end; const sourceFile = getSourceFileOfNode(list[0]); return this.grammarErrorAtPos(sourceFile, start, end - start, Diagnostics.Trailing_comma_not_allowed); } } public checkGrammarTypeParameterList(typeParameters: NodeArray, file: SourceFile): boolean { if (this.checkGrammarForDisallowedTrailingComma(typeParameters)) { return true; } if (typeParameters && typeParameters.length === 0) { const start = typeParameters.pos - "<".length; const end = skipTrivia(file.text, typeParameters.end) + ">".length; return this.grammarErrorAtPos(file, start, end - start, Diagnostics.Type_parameter_list_cannot_be_empty); } } public checkGrammarParameterList(parameters: NodeArray) { let seenOptionalParameter = false; const parameterCount = parameters.length; for (let i = 0; i < parameterCount; i++) { const parameter = parameters[i]; if (parameter.dotDotDotToken) { if (i !== (parameterCount - 1)) { return this.grammarErrorOnNode(parameter.dotDotDotToken, Diagnostics.A_rest_parameter_must_be_last_in_a_parameter_list); } if (isBindingPattern(parameter.name)) { return this.grammarErrorOnNode(parameter.name, Diagnostics.A_rest_element_cannot_contain_a_binding_pattern); } if (parameter.questionToken) { return this.grammarErrorOnNode(parameter.questionToken, Diagnostics.A_rest_parameter_cannot_be_optional); } if (parameter.initializer) { return this.grammarErrorOnNode(parameter.name, Diagnostics.A_rest_parameter_cannot_have_an_initializer); } } else if (parameter.questionToken) { seenOptionalParameter = true; if (parameter.initializer) { return this.grammarErrorOnNode(parameter.name, Diagnostics.Parameter_cannot_have_question_mark_and_initializer); } } else if (seenOptionalParameter && !parameter.initializer) { return this.grammarErrorOnNode(parameter.name, Diagnostics.A_required_parameter_cannot_follow_an_optional_parameter); } } } public checkGrammarFunctionLikeDeclaration(node: FunctionLikeDeclaration): boolean { // Prevent cascading error by short-circuit const file = getSourceFileOfNode(node); return this.checkGrammarDecorators(node) || this.checkGrammarModifiers(node) || this.checkGrammarTypeParameterList(node.typeParameters, file) || this.checkGrammarParameterList(node.parameters) || this.checkGrammarArrowFunction(node, file); } public checkGrammarClassLikeDeclaration(node: ClassLikeDeclaration): boolean { const file = getSourceFileOfNode(node); return this.checkGrammarClassDeclarationHeritageClauses(node) || this.checkGrammarTypeParameterList(node.typeParameters, file); } public checkGrammarArrowFunction(node: FunctionLikeDeclaration, file: SourceFile): boolean { if (node.kind === SyntaxKind.ArrowFunction) { const arrowFunction = node; const startLine = getLineAndCharacterOfPosition(file, arrowFunction.equalsGreaterThanToken.pos).line; const endLine = getLineAndCharacterOfPosition(file, arrowFunction.equalsGreaterThanToken.end).line; if (startLine !== endLine) { return this.grammarErrorOnNode(arrowFunction.equalsGreaterThanToken, Diagnostics.Line_terminator_not_permitted_before_arrow); } } return false; } public checkGrammarIndexSignatureParameters(node: SignatureDeclaration): boolean { const parameter = node.parameters[0]; if (node.parameters.length !== 1) { if (parameter) { return this.grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_must_have_exactly_one_parameter); } else { return this.grammarErrorOnNode(node, Diagnostics.An_index_signature_must_have_exactly_one_parameter); } } if (parameter.dotDotDotToken) { return this.grammarErrorOnNode(parameter.dotDotDotToken, Diagnostics.An_index_signature_cannot_have_a_rest_parameter); } if (hasModifiers(parameter)) { return this.grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_parameter_cannot_have_an_accessibility_modifier); } if (parameter.questionToken) { return this.grammarErrorOnNode(parameter.questionToken, Diagnostics.An_index_signature_parameter_cannot_have_a_question_mark); } if (parameter.initializer) { return this.grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_parameter_cannot_have_an_initializer); } if (!parameter.type) { return this.grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_parameter_must_have_a_type_annotation); } if (parameter.type.kind !== SyntaxKind.StringKeyword && parameter.type.kind !== SyntaxKind.NumberKeyword) { return this.grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_parameter_type_must_be_string_or_number); } if (!node.type) { return this.grammarErrorOnNode(node, Diagnostics.An_index_signature_must_have_a_type_annotation); } } public checkGrammarIndexSignature(node: SignatureDeclaration) { // Prevent cascading error by short-circuit return this.checkGrammarDecorators(node) || this.checkGrammarModifiers(node) || this.checkGrammarIndexSignatureParameters(node); } public checkGrammarForAtLeastOneTypeArgument(node: Node, typeArguments: NodeArray): boolean { if (typeArguments && typeArguments.length === 0) { const sourceFile = getSourceFileOfNode(node); const start = typeArguments.pos - "<".length; const end = skipTrivia(sourceFile.text, typeArguments.end) + ">".length; return this.grammarErrorAtPos(sourceFile, start, end - start, Diagnostics.Type_argument_list_cannot_be_empty); } } public checkGrammarTypeArguments(node: Node, typeArguments: NodeArray): boolean { return this.checkGrammarForDisallowedTrailingComma(typeArguments) || this.checkGrammarForAtLeastOneTypeArgument(node, typeArguments); } public checkGrammarForOmittedArgument(node: CallExpression | NewExpression, args: NodeArray): boolean { if (args) { const sourceFile = getSourceFileOfNode(node); for (const arg of args) { if (arg.kind === SyntaxKind.OmittedExpression) { return this.grammarErrorAtPos(sourceFile, arg.pos, 0, Diagnostics.Argument_expression_expected); } } } } public checkGrammarArguments(node: CallExpression | NewExpression, args: NodeArray): boolean { return this.checkGrammarForOmittedArgument(node, args); } public checkGrammarHeritageClause(node: HeritageClause): boolean { const types = node.types; if (this.checkGrammarForDisallowedTrailingComma(types)) { return true; } if (types && types.length === 0) { const listType = tokenToString(node.token); const sourceFile = getSourceFileOfNode(node); return this.grammarErrorAtPos(sourceFile, types.pos, 0, Diagnostics._0_list_cannot_be_empty, listType); } return forEach(types, this.checkGrammarExpressionWithTypeArguments); } public checkGrammarExpressionWithTypeArguments(node: ExpressionWithTypeArguments) { return this.checkGrammarTypeArguments(node, node.typeArguments); } public checkGrammarClassDeclarationHeritageClauses(node: ClassLikeDeclaration) { let seenExtendsClause = false; let seenImplementsClause = false; if (!this.checkGrammarDecorators(node) && !this.checkGrammarModifiers(node) && node.heritageClauses) { for (const heritageClause of node.heritageClauses) { if (heritageClause.token === SyntaxKind.ExtendsKeyword) { if (seenExtendsClause) { return this.grammarErrorOnFirstToken(heritageClause, Diagnostics.extends_clause_already_seen); } if (seenImplementsClause) { return this.grammarErrorOnFirstToken(heritageClause, Diagnostics.extends_clause_must_precede_implements_clause); } if (heritageClause.types.length > 1) { return this.grammarErrorOnFirstToken(heritageClause.types[1], Diagnostics.Classes_can_only_extend_a_single_class); } seenExtendsClause = true; } else { Debug.assert(heritageClause.token === SyntaxKind.ImplementsKeyword); if (seenImplementsClause) { return this.grammarErrorOnFirstToken(heritageClause, Diagnostics.implements_clause_already_seen); } seenImplementsClause = true; } // Grammar checking heritageClause inside class declaration this.checkGrammarHeritageClause(heritageClause); } } } public checkGrammarInterfaceDeclaration(node: InterfaceDeclaration) { let seenExtendsClause = false; if (node.heritageClauses) { for (const heritageClause of node.heritageClauses) { if (heritageClause.token === SyntaxKind.ExtendsKeyword) { if (seenExtendsClause) { return this.grammarErrorOnFirstToken(heritageClause, Diagnostics.extends_clause_already_seen); } seenExtendsClause = true; } else { Debug.assert(heritageClause.token === SyntaxKind.ImplementsKeyword); return this.grammarErrorOnFirstToken(heritageClause, Diagnostics.Interface_declaration_cannot_have_implements_clause); } // Grammar checking heritageClause inside class declaration this.checkGrammarHeritageClause(heritageClause); } } return false; } public checkGrammarComputedPropertyName(node: Node): boolean { // If node is not a computedPropertyName, just skip the grammar checking if (node.kind !== SyntaxKind.ComputedPropertyName) { return false; } const computedPropertyName = node; if (computedPropertyName.expression.kind === SyntaxKind.BinaryExpression && (computedPropertyName.expression).operatorToken.kind === SyntaxKind.CommaToken) { return this.grammarErrorOnNode(computedPropertyName.expression, Diagnostics.A_comma_expression_is_not_allowed_in_a_computed_property_name); } } public checkGrammarForGenerator(node: FunctionLikeDeclaration) { if (node.asteriskToken) { Debug.assert(node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.FunctionExpression || node.kind === SyntaxKind.MethodDeclaration); if (isInAmbientContext(node)) { return this.grammarErrorOnNode(node.asteriskToken, Diagnostics.Generators_are_not_allowed_in_an_ambient_context); } if (!node.body) { return this.grammarErrorOnNode(node.asteriskToken, Diagnostics.An_overload_signature_cannot_be_declared_as_a_generator); } } } public checkGrammarForInvalidQuestionMark(questionToken: Node, message: DiagnosticMessage): boolean { if (questionToken) { return this.grammarErrorOnNode(questionToken, message); } } public checkGrammarObjectLiteralExpression(node: ObjectLiteralExpression, inDestructuring: boolean) { const seen = createUnderscoreEscapedMap(); const Property = 1; const GetAccessor = 2; const SetAccessor = 4; const GetOrSetAccessor = GetAccessor | SetAccessor; for (const prop of node.properties) { if (prop.kind === SyntaxKind.SpreadAssignment) { continue; } const name = prop.name; if (name.kind === SyntaxKind.ComputedPropertyName) { // If the name is not a ComputedPropertyName, the grammar checking will skip it this.checkGrammarComputedPropertyName((name)); } if (prop.kind === SyntaxKind.ShorthandPropertyAssignment && !inDestructuring && (prop).objectAssignmentInitializer) { // having objectAssignmentInitializer is only valid in ObjectAssignmentPattern // outside of destructuring it is a syntax error return this.grammarErrorOnNode((prop).equalsToken, Diagnostics.can_only_be_used_in_an_object_literal_property_inside_a_destructuring_assignment); } // Modifiers are never allowed on properties except for 'async' on a method declaration if (prop.modifiers) { for (const mod of prop.modifiers) { if (mod.kind !== SyntaxKind.AsyncKeyword || prop.kind !== SyntaxKind.MethodDeclaration) { this.grammarErrorOnNode(mod, Diagnostics._0_modifier_cannot_be_used_here, getTextOfNode(mod)); } } } // ECMA-262 11.1.5 Object Initializer // If previous is not undefined then throw a SyntaxError exception if any of the following conditions are true // a.This production is contained in strict code and IsDataDescriptor(previous) is true and // IsDataDescriptor(propId.descriptor) is true. // b.IsDataDescriptor(previous) is true and IsAccessorDescriptor(propId.descriptor) is true. // c.IsAccessorDescriptor(previous) is true and IsDataDescriptor(propId.descriptor) is true. // d.IsAccessorDescriptor(previous) is true and IsAccessorDescriptor(propId.descriptor) is true // and either both previous and propId.descriptor have[[Get]] fields or both previous and propId.descriptor have[[Set]] fields let currentKind: number; if (prop.kind === SyntaxKind.PropertyAssignment || prop.kind === SyntaxKind.ShorthandPropertyAssignment) { // Grammar checking for computedPropertyName and shorthandPropertyAssignment this.checkGrammarForInvalidQuestionMark((prop).questionToken, Diagnostics.An_object_member_cannot_be_declared_optional); if (name.kind === SyntaxKind.NumericLiteral) { this.checkGrammarNumericLiteral((name)); } currentKind = Property; } else if (prop.kind === SyntaxKind.MethodDeclaration) { currentKind = Property; } else if (prop.kind === SyntaxKind.GetAccessor) { currentKind = GetAccessor; } else if (prop.kind === SyntaxKind.SetAccessor) { currentKind = SetAccessor; } else { Debug.fail("Unexpected syntax kind:" + (prop).kind); } const effectiveName = getPropertyNameForPropertyNameNode(name); if (effectiveName === undefined) { continue; } const existingKind = seen.get(effectiveName); if (!existingKind) { seen.set(effectiveName, currentKind); } else { if (currentKind === Property && existingKind === Property) { this.grammarErrorOnNode(name, Diagnostics.Duplicate_identifier_0, getTextOfNode(name)); } else if ((currentKind & GetOrSetAccessor) && (existingKind & GetOrSetAccessor)) { if (existingKind !== GetOrSetAccessor && currentKind !== existingKind) { seen.set(effectiveName, currentKind | existingKind); } else { return this.grammarErrorOnNode(name, Diagnostics.An_object_literal_cannot_have_multiple_get_Slashset_accessors_with_the_same_name); } } else { return this.grammarErrorOnNode(name, Diagnostics.An_object_literal_cannot_have_property_and_accessor_with_the_same_name); } } } } public checkGrammarJsxElement(node: JsxOpeningLikeElement) { const seen = createUnderscoreEscapedMap(); for (const attr of node.attributes.properties) { if (attr.kind === SyntaxKind.JsxSpreadAttribute) { continue; } const jsxAttr = (attr); const name = jsxAttr.name; if (!seen.get(name.escapedText)) { seen.set(name.escapedText, true); } else { return this.grammarErrorOnNode(name, Diagnostics.JSX_elements_cannot_have_multiple_attributes_with_the_same_name); } const initializer = jsxAttr.initializer; if (initializer && initializer.kind === SyntaxKind.JsxExpression && !(initializer).expression) { return this.grammarErrorOnNode(jsxAttr.initializer, Diagnostics.JSX_attributes_must_only_be_assigned_a_non_empty_expression); } } } public checkGrammarForInOrForOfStatement(forInOrOfStatement: ForInOrOfStatement): boolean { if (this.checkGrammarStatementInAmbientContext(forInOrOfStatement)) { return true; } if (forInOrOfStatement.kind === SyntaxKind.ForOfStatement && forInOrOfStatement.awaitModifier) { if ((forInOrOfStatement.flags & NodeFlags.AwaitContext) === NodeFlags.None) { return this.grammarErrorOnNode(forInOrOfStatement.awaitModifier, Diagnostics.A_for_await_of_statement_is_only_allowed_within_an_async_function_or_async_generator); } } if (forInOrOfStatement.initializer.kind === SyntaxKind.VariableDeclarationList) { const variableList = forInOrOfStatement.initializer; if (!this.checkGrammarVariableDeclarationList(variableList)) { const declarations = variableList.declarations; // declarations.length can be zero if there is an error in variable declaration in for-of or for-in // See http://www.ecma-international.org/ecma-262/6.0/#sec-for-in-and-for-of-statements for details // For example: // var let = 10; // for (let of [1,2,3]) {} // this is invalid ES6 syntax // for (let in [1,2,3]) {} // this is invalid ES6 syntax // We will then want to skip on grammar checking on variableList declaration if (!declarations.length) { return false; } if (declarations.length > 1) { const diagnostic = forInOrOfStatement.kind === SyntaxKind.ForInStatement ? Diagnostics.Only_a_single_variable_declaration_is_allowed_in_a_for_in_statement : Diagnostics.Only_a_single_variable_declaration_is_allowed_in_a_for_of_statement; return this.grammarErrorOnFirstToken(variableList.declarations[1], diagnostic); } const firstDeclaration = declarations[0]; if (firstDeclaration.initializer) { const diagnostic = forInOrOfStatement.kind === SyntaxKind.ForInStatement ? Diagnostics.The_variable_declaration_of_a_for_in_statement_cannot_have_an_initializer : Diagnostics.The_variable_declaration_of_a_for_of_statement_cannot_have_an_initializer; return this.grammarErrorOnNode(firstDeclaration.name, diagnostic); } if (firstDeclaration.type) { const diagnostic = forInOrOfStatement.kind === SyntaxKind.ForInStatement ? Diagnostics.The_left_hand_side_of_a_for_in_statement_cannot_use_a_type_annotation : Diagnostics.The_left_hand_side_of_a_for_of_statement_cannot_use_a_type_annotation; return this.grammarErrorOnNode(firstDeclaration, diagnostic); } } } return false; } public checkGrammarAccessor(accessor: AccessorDeclaration): boolean { const kind = accessor.kind; if (this.languageVersion < ScriptTarget.ES5) { return this.grammarErrorOnNode(accessor.name, Diagnostics.Accessors_are_only_available_when_targeting_ECMAScript_5_and_higher); } else if (isInAmbientContext(accessor)) { return this.grammarErrorOnNode(accessor.name, Diagnostics.An_accessor_cannot_be_declared_in_an_ambient_context); } else if (accessor.body === undefined && !hasModifier(accessor, ModifierFlags.Abstract)) { return this.grammarErrorAtPos(getSourceFileOfNode(accessor), accessor.end - 1, ";".length, Diagnostics._0_expected, "{"); } else if (accessor.body && hasModifier(accessor, ModifierFlags.Abstract)) { return this.grammarErrorOnNode(accessor, Diagnostics.An_abstract_accessor_cannot_have_an_implementation); } else if (accessor.typeParameters) { return this.grammarErrorOnNode(accessor.name, Diagnostics.An_accessor_cannot_have_type_parameters); } else if (!this.doesAccessorHaveCorrectParameterCount(accessor)) { return this.grammarErrorOnNode(accessor.name, kind === SyntaxKind.GetAccessor ? Diagnostics.A_get_accessor_cannot_have_parameters : Diagnostics.A_set_accessor_must_have_exactly_one_parameter); } else if (kind === SyntaxKind.SetAccessor) { if (accessor.type) { return this.grammarErrorOnNode(accessor.name, Diagnostics.A_set_accessor_cannot_have_a_return_type_annotation); } else { const parameter = accessor.parameters[0]; if (parameter.dotDotDotToken) { return this.grammarErrorOnNode(parameter.dotDotDotToken, Diagnostics.A_set_accessor_cannot_have_rest_parameter); } else if (parameter.questionToken) { return this.grammarErrorOnNode(parameter.questionToken, Diagnostics.A_set_accessor_cannot_have_an_optional_parameter); } else if (parameter.initializer) { return this.grammarErrorOnNode(accessor.name, Diagnostics.A_set_accessor_parameter_cannot_have_an_initializer); } } } } /** Does the accessor have the right number of parameters? * A get accessor has no parameters or a single `this` parameter. * A set accessor has one parameter or a `this` parameter and one more parameter. */ public doesAccessorHaveCorrectParameterCount(accessor: AccessorDeclaration) { return this.getAccessorThisParameter(accessor) || accessor.parameters.length === (accessor.kind === SyntaxKind.GetAccessor ? 0 : 1); } public getAccessorThisParameter(accessor: AccessorDeclaration): ParameterDeclaration { if (accessor.parameters.length === (accessor.kind === SyntaxKind.GetAccessor ? 1 : 2)) { return getThisParameter(accessor); } } public checkGrammarForNonSymbolComputedProperty(node: DeclarationName, message: DiagnosticMessage) { if (isDynamicName(node)) { return this.grammarErrorOnNode(node, message); } } public checkGrammarMethod(node: MethodDeclaration) { if (this.checkGrammarDisallowedModifiersOnObjectLiteralExpressionMethod(node) || this.checkGrammarFunctionLikeDeclaration(node) || this.checkGrammarForGenerator(node)) { return true; } if (node.parent.kind === SyntaxKind.ObjectLiteralExpression) { if (this.checkGrammarForInvalidQuestionMark(node.questionToken, Diagnostics.An_object_member_cannot_be_declared_optional)) { return true; } else if (node.body === undefined) { return this.grammarErrorAtPos(getSourceFileOfNode(node), node.end - 1, ";".length, Diagnostics._0_expected, "{"); } } if (isClassLike(node.parent)) { // Technically, computed properties in ambient contexts is disallowed // for property declarations and accessors too, not just methods. // However, property declarations disallow computed names in general, // and accessors are not allowed in ambient contexts in general, // so this error only really matters for methods. if (isInAmbientContext(node)) { return this.checkGrammarForNonSymbolComputedProperty(node.name, Diagnostics.A_computed_property_name_in_an_ambient_context_must_directly_refer_to_a_built_in_symbol); } else if (!node.body) { return this.checkGrammarForNonSymbolComputedProperty(node.name, Diagnostics.A_computed_property_name_in_a_method_overload_must_directly_refer_to_a_built_in_symbol); } } else if (node.parent.kind === SyntaxKind.InterfaceDeclaration) { return this.checkGrammarForNonSymbolComputedProperty(node.name, Diagnostics.A_computed_property_name_in_an_interface_must_directly_refer_to_a_built_in_symbol); } else if (node.parent.kind === SyntaxKind.TypeLiteral) { return this.checkGrammarForNonSymbolComputedProperty(node.name, Diagnostics.A_computed_property_name_in_a_type_literal_must_directly_refer_to_a_built_in_symbol); } } public checkGrammarBreakOrContinueStatement(node: BreakOrContinueStatement): boolean { let current: Node = node; while (current) { if (isFunctionLike(current)) { return this.grammarErrorOnNode(node, Diagnostics.Jump_target_cannot_cross_function_boundary); } switch (current.kind) { case SyntaxKind.LabeledStatement: if (node.label && (current).label.escapedText === node.label.escapedText) { // found matching label - verify that label usage is correct // continue can only target labels that are on iteration statements const isMisplacedContinueLabel = node.kind === SyntaxKind.ContinueStatement && !isIterationStatement((current).statement, /*lookInLabeledStatement*/ true); if (isMisplacedContinueLabel) { return this.grammarErrorOnNode(node, Diagnostics.A_continue_statement_can_only_jump_to_a_label_of_an_enclosing_iteration_statement); } return false; } break; case SyntaxKind.SwitchStatement: if (node.kind === SyntaxKind.BreakStatement && !node.label) { // unlabeled break within switch statement - ok return false; } break; default: if (isIterationStatement(current, /*lookInLabeledStatement*/ false) && !node.label) { // unlabeled break or continue within iteration statement - ok return false; } break; } current = current.parent; } if (node.label) { const message = node.kind === SyntaxKind.BreakStatement ? Diagnostics.A_break_statement_can_only_jump_to_a_label_of_an_enclosing_statement : Diagnostics.A_continue_statement_can_only_jump_to_a_label_of_an_enclosing_iteration_statement; return this.grammarErrorOnNode(node, message); } else { const message = node.kind === SyntaxKind.BreakStatement ? Diagnostics.A_break_statement_can_only_be_used_within_an_enclosing_iteration_or_switch_statement : Diagnostics.A_continue_statement_can_only_be_used_within_an_enclosing_iteration_statement; return this.grammarErrorOnNode(node, message); } } public checkGrammarBindingElement(node: BindingElement) { if (node.dotDotDotToken) { const elements = (node.parent).elements; if (node !== lastOrUndefined(elements)) { return this.grammarErrorOnNode(node, Diagnostics.A_rest_element_must_be_last_in_a_destructuring_pattern); } if (node.name.kind === SyntaxKind.ArrayBindingPattern || node.name.kind === SyntaxKind.ObjectBindingPattern) { return this.grammarErrorOnNode(node.name, Diagnostics.A_rest_element_cannot_contain_a_binding_pattern); } if (node.initializer) { // Error on equals token which immediately precedes the initializer return this.grammarErrorAtPos(getSourceFileOfNode(node), node.initializer.pos - 1, 1, Diagnostics.A_rest_element_cannot_have_an_initializer); } } } public isStringOrNumberLiteralExpression(expr: Expression) { return expr.kind === SyntaxKind.StringLiteral || expr.kind === SyntaxKind.NumericLiteral || expr.kind === SyntaxKind.PrefixUnaryExpression && (expr).operator === SyntaxKind.MinusToken && (expr).operand.kind === SyntaxKind.NumericLiteral; } public checkGrammarVariableDeclaration(node: VariableDeclaration) { if (node.parent.parent.kind !== SyntaxKind.ForInStatement && node.parent.parent.kind !== SyntaxKind.ForOfStatement) { if (isInAmbientContext(node)) { if (node.initializer) { if (isConst(node) && !node.type) { if (!this.isStringOrNumberLiteralExpression(node.initializer)) { return this.grammarErrorOnNode(node.initializer, Diagnostics.A_const_initializer_in_an_ambient_context_must_be_a_string_or_numeric_literal); } } else { // Error on equals token which immediate precedes the initializer const equalsTokenLength = "=".length; return this.grammarErrorAtPos(getSourceFileOfNode(node), node.initializer.pos - equalsTokenLength, equalsTokenLength, Diagnostics.Initializers_are_not_allowed_in_ambient_contexts); } } if (node.initializer && !(isConst(node) && this.isStringOrNumberLiteralExpression(node.initializer))) { // Error on equals token which immediate precedes the initializer const equalsTokenLength = "=".length; return this.grammarErrorAtPos(getSourceFileOfNode(node), node.initializer.pos - equalsTokenLength, equalsTokenLength, Diagnostics.Initializers_are_not_allowed_in_ambient_contexts); } } else if (!node.initializer) { if (isBindingPattern(node.name) && !isBindingPattern(node.parent)) { return this.grammarErrorOnNode(node, Diagnostics.A_destructuring_declaration_must_have_an_initializer); } if (isConst(node)) { return this.grammarErrorOnNode(node, Diagnostics.const_declarations_must_be_initialized); } } } if (this.compilerOptions.module !== ModuleKind.ES2015 && this.compilerOptions.module !== ModuleKind.System && !this.compilerOptions.noEmit && !isInAmbientContext(node.parent.parent) && hasModifier(node.parent.parent, ModifierFlags.Export)) { this.checkESModuleMarker(node.name); } const checkLetConstNames = (isLet(node) || isConst(node)); // 1. LexicalDeclaration : LetOrConst BindingList ; // It is a Syntax Error if the BoundNames of BindingList contains "let". // 2. ForDeclaration: ForDeclaration : LetOrConst ForBinding // It is a Syntax Error if the BoundNames of ForDeclaration contains "let". // It is a SyntaxError if a VariableDeclaration or VariableDeclarationNoIn occurs within strict code // and its Identifier is eval or arguments return checkLetConstNames && this.checkGrammarNameInLetOrConstDeclarations(node.name); } public checkESModuleMarker(name: Identifier | BindingPattern): boolean { if (name.kind === SyntaxKind.Identifier) { if (unescapeLeadingUnderscores(name.escapedText) === "__esModule") { return this.grammarErrorOnNode(name, Diagnostics.Identifier_expected_esModule_is_reserved_as_an_exported_marker_when_transforming_ECMAScript_modules); } } else { const elements = (name).elements; for (const element of elements) { if (!isOmittedExpression(element)) { return this.checkESModuleMarker(element.name); } } } } public checkGrammarNameInLetOrConstDeclarations(name: Identifier | BindingPattern): boolean { if (name.kind === SyntaxKind.Identifier) { if ((name).originalKeywordKind === SyntaxKind.LetKeyword) { return this.grammarErrorOnNode(name, Diagnostics.let_is_not_allowed_to_be_used_as_a_name_in_let_or_const_declarations); } } else { const elements = (name).elements; for (const element of elements) { if (!isOmittedExpression(element)) { this.checkGrammarNameInLetOrConstDeclarations(element.name); } } } } public checkGrammarVariableDeclarationList(declarationList: VariableDeclarationList): boolean { const declarations = declarationList.declarations; if (this.checkGrammarForDisallowedTrailingComma(declarationList.declarations)) { return true; } if (!declarationList.declarations.length) { return this.grammarErrorAtPos(getSourceFileOfNode(declarationList), declarations.pos, declarations.end - declarations.pos, Diagnostics.Variable_declaration_list_cannot_be_empty); } } public allowLetAndConstDeclarations(parent: Node): boolean { switch (parent.kind) { case SyntaxKind.IfStatement: case SyntaxKind.DoStatement: case SyntaxKind.WhileStatement: case SyntaxKind.WithStatement: case SyntaxKind.ForStatement: case SyntaxKind.ForInStatement: case SyntaxKind.ForOfStatement: return false; case SyntaxKind.LabeledStatement: return this.allowLetAndConstDeclarations(parent.parent); } return true; } public checkGrammarForDisallowedLetOrConstStatement(node: VariableStatement) { if (!this.allowLetAndConstDeclarations(node.parent)) { if (isLet(node.declarationList)) { return this.grammarErrorOnNode(node, Diagnostics.let_declarations_can_only_be_declared_inside_a_block); } else if (isConst(node.declarationList)) { return this.grammarErrorOnNode(node, Diagnostics.const_declarations_can_only_be_declared_inside_a_block); } } } public checkGrammarMetaProperty(node: MetaProperty) { if (node.keywordToken === SyntaxKind.NewKeyword) { if (node.name.escapedText !== "target") { return this.grammarErrorOnNode(node.name, Diagnostics._0_is_not_a_valid_meta_property_for_keyword_1_Did_you_mean_2, node.name.escapedText, tokenToString(node.keywordToken), "target"); } } } public hasParseDiagnostics(sourceFile: SourceFile): boolean { return sourceFile.parseDiagnostics.length > 0; } public grammarErrorOnFirstToken(node: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): boolean { const sourceFile = getSourceFileOfNode(node); if (!this.hasParseDiagnostics(sourceFile)) { const span = getSpanOfTokenAtPosition(sourceFile, node.pos); this.diagnostics.add(createFileDiagnostic(sourceFile, span.start, span.length, message, arg0, arg1, arg2)); return true; } } public grammarErrorAtPos(sourceFile: SourceFile, start: number, length: number, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): boolean { if (!this.hasParseDiagnostics(sourceFile)) { this.diagnostics.add(createFileDiagnostic(sourceFile, start, length, message, arg0, arg1, arg2)); return true; } } public grammarErrorOnNode(node: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): boolean { const sourceFile = getSourceFileOfNode(node); if (!this.hasParseDiagnostics(sourceFile)) { this.diagnostics.add(createDiagnosticForNode(node, message, arg0, arg1, arg2)); return true; } } public checkGrammarConstructorTypeParameters(node: ConstructorDeclaration) { if (node.typeParameters) { return this.grammarErrorAtPos(getSourceFileOfNode(node), node.typeParameters.pos, node.typeParameters.end - node.typeParameters.pos, Diagnostics.Type_parameters_cannot_appear_on_a_constructor_declaration); } } public checkGrammarConstructorTypeAnnotation(node: ConstructorDeclaration) { if (node.type) { return this.grammarErrorOnNode(node.type, Diagnostics.Type_annotation_cannot_appear_on_a_constructor_declaration); } } public checkGrammarProperty(node: PropertyDeclaration) { if (isClassLike(node.parent)) { if (this.checkGrammarForNonSymbolComputedProperty(node.name, Diagnostics.A_computed_property_name_in_a_class_property_declaration_must_directly_refer_to_a_built_in_symbol)) { return true; } } else if (node.parent.kind === SyntaxKind.InterfaceDeclaration) { if (this.checkGrammarForNonSymbolComputedProperty(node.name, Diagnostics.A_computed_property_name_in_an_interface_must_directly_refer_to_a_built_in_symbol)) { return true; } if (node.initializer) { return this.grammarErrorOnNode(node.initializer, Diagnostics.An_interface_property_cannot_have_an_initializer); } } else if (node.parent.kind === SyntaxKind.TypeLiteral) { if (this.checkGrammarForNonSymbolComputedProperty(node.name, Diagnostics.A_computed_property_name_in_a_type_literal_must_directly_refer_to_a_built_in_symbol)) { return true; } if (node.initializer) { return this.grammarErrorOnNode(node.initializer, Diagnostics.A_type_literal_property_cannot_have_an_initializer); } } if (isInAmbientContext(node) && node.initializer) { return this.grammarErrorOnFirstToken(node.initializer, Diagnostics.Initializers_are_not_allowed_in_ambient_contexts); } } public checkGrammarTopLevelElementForRequiredDeclareModifier(node: Node): boolean { // A declare modifier is required for any top level .d.ts declaration except export=, export default, export as namespace // interfaces and imports categories: // // DeclarationElement: // ExportAssignment // export_opt InterfaceDeclaration // export_opt TypeAliasDeclaration // export_opt ImportDeclaration // export_opt ExternalImportDeclaration // export_opt AmbientDeclaration // // TODO: The spec needs to be amended to reflect this grammar. if (node.kind === SyntaxKind.InterfaceDeclaration || node.kind === SyntaxKind.TypeAliasDeclaration || node.kind === SyntaxKind.ImportDeclaration || node.kind === SyntaxKind.ImportEqualsDeclaration || node.kind === SyntaxKind.ExportDeclaration || node.kind === SyntaxKind.ExportAssignment || node.kind === SyntaxKind.NamespaceExportDeclaration || hasModifier(node, ModifierFlags.Ambient | ModifierFlags.Export | ModifierFlags.Default)) { return false; } return this.grammarErrorOnFirstToken(node, Diagnostics.A_declare_modifier_is_required_for_a_top_level_declaration_in_a_d_ts_file); } public checkGrammarTopLevelElementsForRequiredDeclareModifier(file: SourceFile): boolean { for (const decl of file.statements) { if (isDeclaration(decl) || decl.kind === SyntaxKind.VariableStatement) { if (this.checkGrammarTopLevelElementForRequiredDeclareModifier(decl)) { return true; } } } } public checkGrammarSourceFile(node: SourceFile): boolean { return isInAmbientContext(node) && this.checkGrammarTopLevelElementsForRequiredDeclareModifier(node); } public checkGrammarStatementInAmbientContext(node: Node): boolean { if (isInAmbientContext(node)) { // An accessors is already reported about the ambient context if (isAccessor(node.parent)) { return this.getNodeLinks(node).hasReportedStatementInAmbientContext = true; } // Find containing block which is either Block, ModuleBlock, SourceFile const links = this.getNodeLinks(node); if (!links.hasReportedStatementInAmbientContext && isFunctionLike(node.parent)) { return this.getNodeLinks(node).hasReportedStatementInAmbientContext = this.grammarErrorOnFirstToken(node, Diagnostics.An_implementation_cannot_be_declared_in_ambient_contexts); } // We are either parented by another statement, or some sort of block. // If we're in a block, we only want to really report an error once // to prevent noisiness. So use a bit on the block to indicate if // this has already been reported, and don't report if it has. // if (node.parent.kind === SyntaxKind.Block || node.parent.kind === SyntaxKind.ModuleBlock || node.parent.kind === SyntaxKind.SourceFile) { const links = this.getNodeLinks(node.parent); // Check if the containing block ever report this error if (!links.hasReportedStatementInAmbientContext) { return links.hasReportedStatementInAmbientContext = this.grammarErrorOnFirstToken(node, Diagnostics.Statements_are_not_allowed_in_ambient_contexts); } } else { // We must be parented by a statement. If so, there's no need // to report the error as our parent will have already done it. // Debug.assert(isStatement(node.parent)); } } } public checkGrammarNumericLiteral(node: NumericLiteral): boolean { // Grammar checking if (node.numericLiteralFlags & NumericLiteralFlags.Octal) { let diagnosticMessage: DiagnosticMessage | undefined; if (this.languageVersion >= ScriptTarget.ES5) { diagnosticMessage = Diagnostics.Octal_literals_are_not_available_when_targeting_ECMAScript_5_and_higher_Use_the_syntax_0; } else if (isChildOfNodeWithKind(node, SyntaxKind.LiteralType)) { diagnosticMessage = Diagnostics.Octal_literal_types_must_use_ES2015_syntax_Use_the_syntax_0; } else if (isChildOfNodeWithKind(node, SyntaxKind.EnumMember)) { diagnosticMessage = Diagnostics.Octal_literals_are_not_allowed_in_enums_members_initializer_Use_the_syntax_0; } if (diagnosticMessage) { const withMinus = isPrefixUnaryExpression(node.parent) && node.parent.operator === SyntaxKind.MinusToken; const literal = `${withMinus ? "-" : ""}0o${node.text}`; return this.grammarErrorOnNode(withMinus ? node.parent : node, diagnosticMessage, literal); } } } public grammarErrorAfterFirstToken(node: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): boolean { const sourceFile = getSourceFileOfNode(node); if (!this.hasParseDiagnostics(sourceFile)) { const span = getSpanOfTokenAtPosition(sourceFile, node.pos); this.diagnostics.add(createFileDiagnostic(sourceFile, textSpanEnd(span), /*length*/ 0, message, arg0, arg1, arg2)); return true; } } public getAmbientModules(): Symbol[] { const result: Symbol[] = []; this.globals.forEach((global, sym) => { if (ambientModuleSymbolRegex.test(unescapeLeadingUnderscores(sym))) { result.push(global); } }); return result; } public checkGrammarImportCallExpression(node: ImportCall): boolean { if (this.modulekind === ModuleKind.ES2015) { return this.grammarErrorOnNode(node, Diagnostics.Dynamic_import_cannot_be_used_when_targeting_ECMAScript_2015_modules); } if (node.typeArguments) { return this.grammarErrorOnNode(node, Diagnostics.Dynamic_import_cannot_have_type_arguments); } const nodeArguments = node.arguments; if (nodeArguments.length !== 1) { return this.grammarErrorOnNode(node, Diagnostics.Dynamic_import_must_have_one_specifier_as_an_argument); } // see: parseArgumentOrArrayLiteralElement...we use this function which parse arguments of callExpression to parse specifier for dynamic import. // parseArgumentOrArrayLiteralElement allows spread element to be in an argument list which is not allowed as specifier in dynamic import. if (isSpreadElement(nodeArguments[0])) { return this.grammarErrorOnNode(nodeArguments[0], Diagnostics.Specifier_of_dynamic_import_cannot_be_spread_element); } } } /** Like 'isDeclarationName', but returns true for LHS of `import { x as y }` or `export { x as y }`. */ function isDeclarationNameOrImportPropertyName(name: Node): boolean { switch (name.parent.kind) { case SyntaxKind.ImportSpecifier: case SyntaxKind.ExportSpecifier: return true; default: return isDeclarationName(name); } } function isSomeImportDeclaration(decl: Node): boolean { switch (decl.kind) { case SyntaxKind.ImportClause: // For default import case SyntaxKind.ImportEqualsDeclaration: case SyntaxKind.NamespaceImport: case SyntaxKind.ImportSpecifier:// For rename import `x as y` return true; case SyntaxKind.Identifier: // For regular import, `decl` is an Identifier under the ImportSpecifier. return decl.parent.kind === SyntaxKind.ImportSpecifier; default: return false; } } }