///
///
/* @internal */
namespace ts {
const ambientModuleSymbolRegex = /^".+"$/;
let nextSymbolId = 1;
let nextNodeId = 1;
let nextMergeId = 1;
let nextFlowId = 1;
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 createTypeChecker(host: TypeCheckerHost, produceDiagnostics: boolean): TypeChecker {
// 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).
let cancellationToken: CancellationToken;
let requestedExternalEmitHelpers: ExternalEmitHelpers;
let externalHelpersModule: Symbol;
const Symbol = objectAllocator.getSymbolConstructor();
const Type = objectAllocator.getTypeConstructor();
const Signature = objectAllocator.getSignatureConstructor();
let typeCount = 0;
let symbolCount = 0;
let symbolInstantiationDepth = 0;
const emptyArray: any[] = [];
const emptySymbols = createMap();
const compilerOptions = host.getCompilerOptions();
const languageVersion = getEmitScriptTarget(compilerOptions);
const modulekind = getEmitModuleKind(compilerOptions);
const noUnusedIdentifiers = !!compilerOptions.noUnusedLocals || !!compilerOptions.noUnusedParameters;
const allowSyntheticDefaultImports = typeof compilerOptions.allowSyntheticDefaultImports !== "undefined" ? compilerOptions.allowSyntheticDefaultImports : modulekind === ModuleKind.System;
const strictNullChecks = compilerOptions.strictNullChecks === undefined ? compilerOptions.strict : compilerOptions.strictNullChecks;
const noImplicitAny = compilerOptions.noImplicitAny === undefined ? compilerOptions.strict : compilerOptions.noImplicitAny;
const noImplicitThis = compilerOptions.noImplicitThis === undefined ? compilerOptions.strict : compilerOptions.noImplicitThis;
const emitResolver = createResolver();
const nodeBuilder = createNodeBuilder();
const undefinedSymbol = createSymbol(SymbolFlags.Property, "undefined");
undefinedSymbol.declarations = [];
const argumentsSymbol = createSymbol(SymbolFlags.Property, "arguments");
// for public members that accept a Node or one of its subtypes, we must guard against
// synthetic nodes created during transformations by calling `getParseTreeNode`.
// for most of these, we perform the guard only on `checker` to avoid any possible
// extra cost of calling `getParseTreeNode` when calling these functions from inside the
// checker.
const checker: TypeChecker = {
getNodeCount: () => sum(host.getSourceFiles(), "nodeCount"),
getIdentifierCount: () => sum(host.getSourceFiles(), "identifierCount"),
getSymbolCount: () => sum(host.getSourceFiles(), "symbolCount") + symbolCount,
getTypeCount: () => typeCount,
isUndefinedSymbol: symbol => symbol === undefinedSymbol,
isArgumentsSymbol: symbol => symbol === argumentsSymbol,
isUnknownSymbol: symbol => symbol === unknownSymbol,
getMergedSymbol,
getDiagnostics,
getGlobalDiagnostics,
getTypeOfSymbolAtLocation: (symbol, location) => {
location = getParseTreeNode(location);
return location ? getTypeOfSymbolAtLocation(symbol, location) : 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 getSymbolsOfParameterPropertyDeclaration(parameter, parameterName);
},
getDeclaredTypeOfSymbol,
getPropertiesOfType,
getPropertyOfType,
getIndexInfoOfType,
getSignaturesOfType,
getIndexTypeOfType,
getBaseTypes,
getBaseTypeOfLiteralType,
getWidenedType,
getTypeFromTypeNode: node => {
node = getParseTreeNode(node, isTypeNode);
return node ? getTypeFromTypeNode(node) : unknownType;
},
getParameterType: getTypeAtPosition,
getReturnTypeOfSignature,
getNonNullableType,
typeToTypeNode: nodeBuilder.typeToTypeNode,
indexInfoToIndexSignatureDeclaration: nodeBuilder.indexInfoToIndexSignatureDeclaration,
signatureToSignatureDeclaration: nodeBuilder.signatureToSignatureDeclaration,
getSymbolsInScope: (location, meaning) => {
location = getParseTreeNode(location);
return location ? getSymbolsInScope(location, meaning) : [];
},
getSymbolAtLocation: node => {
node = getParseTreeNode(node);
return node ? getSymbolAtLocation(node) : undefined;
},
getShorthandAssignmentValueSymbol: node => {
node = getParseTreeNode(node);
return node ? getShorthandAssignmentValueSymbol(node) : undefined;
},
getExportSpecifierLocalTargetSymbol: node => {
node = getParseTreeNode(node, isExportSpecifier);
return node ? getExportSpecifierLocalTargetSymbol(node) : undefined;
},
getTypeAtLocation: node => {
node = getParseTreeNode(node);
return node ? getTypeOfNode(node) : unknownType;
},
getPropertySymbolOfDestructuringAssignment: location => {
location = getParseTreeNode(location, isIdentifier);
return location ? getPropertySymbolOfDestructuringAssignment(location) : undefined;
},
signatureToString: (signature, enclosingDeclaration?, flags?, kind?) => {
return signatureToString(signature, getParseTreeNode(enclosingDeclaration), flags, kind);
},
typeToString: (type, enclosingDeclaration?, flags?) => {
return typeToString(type, getParseTreeNode(enclosingDeclaration), flags);
},
getSymbolDisplayBuilder,
symbolToString: (symbol, enclosingDeclaration?, meaning?) => {
return symbolToString(symbol, getParseTreeNode(enclosingDeclaration), meaning);
},
getAugmentedPropertiesOfType,
getRootSymbols,
getContextualType: node => {
node = getParseTreeNode(node, isExpression);
return node ? getContextualType(node) : undefined;
},
getFullyQualifiedName,
getResolvedSignature: (node, candidatesOutArray?) => {
node = getParseTreeNode(node, isCallLikeExpression);
return node ? getResolvedSignature(node, candidatesOutArray) : undefined;
},
getConstantValue: node => {
node = getParseTreeNode(node, canHaveConstantValue);
return node ? getConstantValue(node) : undefined;
},
isValidPropertyAccess: (node, propertyName) => {
node = getParseTreeNode(node, isPropertyAccessOrQualifiedName);
return node ? isValidPropertyAccess(node, propertyName) : false;
},
getSignatureFromDeclaration: declaration => {
declaration = getParseTreeNode(declaration, isFunctionLike);
return declaration ? getSignatureFromDeclaration(declaration) : undefined;
},
isImplementationOfOverload: node => {
node = getParseTreeNode(node, isFunctionLike);
return node ? isImplementationOfOverload(node) : undefined;
},
getImmediateAliasedSymbol: symbol => {
Debug.assert((symbol.flags & SymbolFlags.Alias) !== 0, "Should only get Alias here.");
const links = getSymbolLinks(symbol);
if (!links.immediateTarget) {
const node = getDeclarationOfAliasSymbol(symbol);
Debug.assert(!!node);
links.immediateTarget = getTargetOfAliasDeclaration(node, /*dontRecursivelyResolve*/ true);
}
return links.immediateTarget;
},
getAliasedSymbol: resolveAlias,
getEmitResolver,
getExportsOfModule: getExportsOfModuleAsArray,
getExportsAndPropertiesOfModule,
getAmbientModules,
getAllAttributesTypeFromJsxOpeningLikeElement: node => {
node = getParseTreeNode(node, isJsxOpeningLikeElement);
return node ? getAllAttributesTypeFromJsxOpeningLikeElement(node) : undefined;
},
getJsxIntrinsicTagNames,
isOptionalParameter: node => {
node = getParseTreeNode(node, isParameter);
return node ? isOptionalParameter(node) : false;
},
tryGetMemberInModuleExports,
tryFindAmbientModuleWithoutAugmentations: moduleName => {
// we deliberately exclude augmentations
// since we are only interested in declarations of the module itself
return tryFindAmbientModule(moduleName, /*withAugmentations*/ false);
},
getApparentType,
getAllPossiblePropertiesOfType,
getSuggestionForNonexistentProperty,
getSuggestionForNonexistentSymbol,
getBaseConstraintOfType,
};
const tupleTypes: GenericType[] = [];
const unionTypes = createMap();
const intersectionTypes = createMap();
const stringLiteralTypes = createMap();
const numericLiteralTypes = createMap();
const indexedAccessTypes = createMap();
const evolvingArrayTypes: EvolvingArrayType[] = [];
const unknownSymbol = createSymbol(SymbolFlags.Property, "unknown");
const resolvingSymbol = createSymbol(0, "__resolving__");
const anyType = createIntrinsicType(TypeFlags.Any, "any");
const autoType = createIntrinsicType(TypeFlags.Any, "any");
const unknownType = createIntrinsicType(TypeFlags.Any, "unknown");
const undefinedType = createIntrinsicType(TypeFlags.Undefined, "undefined");
const undefinedWideningType = strictNullChecks ? undefinedType : createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsWideningType, "undefined");
const nullType = createIntrinsicType(TypeFlags.Null, "null");
const nullWideningType = strictNullChecks ? nullType : createIntrinsicType(TypeFlags.Null | TypeFlags.ContainsWideningType, "null");
const stringType = createIntrinsicType(TypeFlags.String, "string");
const numberType = createIntrinsicType(TypeFlags.Number, "number");
const trueType = createIntrinsicType(TypeFlags.BooleanLiteral, "true");
const falseType = createIntrinsicType(TypeFlags.BooleanLiteral, "false");
const booleanType = createBooleanType([trueType, falseType]);
const esSymbolType = createIntrinsicType(TypeFlags.ESSymbol, "symbol");
const voidType = createIntrinsicType(TypeFlags.Void, "void");
const neverType = createIntrinsicType(TypeFlags.Never, "never");
const silentNeverType = createIntrinsicType(TypeFlags.Never, "never");
const nonPrimitiveType = createIntrinsicType(TypeFlags.NonPrimitive, "object");
const emptyObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
const emptyTypeLiteralSymbol = createSymbol(SymbolFlags.TypeLiteral, "__type");
emptyTypeLiteralSymbol.members = createMap();
const emptyTypeLiteralType = createAnonymousType(emptyTypeLiteralSymbol, emptySymbols, emptyArray, emptyArray, undefined, undefined);
const emptyGenericType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
emptyGenericType.instantiations = createMap();
const anyFunctionType = createAnonymousType(undefined, 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.
anyFunctionType.flags |= TypeFlags.ContainsAnyFunctionType;
const noConstraintType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
const circularConstraintType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
const anySignature = createSignature(undefined, undefined, undefined, emptyArray, anyType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false);
const unknownSignature = createSignature(undefined, undefined, undefined, emptyArray, unknownType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false);
const resolvingSignature = createSignature(undefined, undefined, undefined, emptyArray, anyType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false);
const silentNeverSignature = createSignature(undefined, undefined, undefined, emptyArray, silentNeverType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false);
const enumNumberIndexInfo = createIndexInfo(stringType, /*isReadonly*/ true);
const jsObjectLiteralIndexInfo = createIndexInfo(anyType, /*isReadonly*/ false);
const globals = createMap();
/**
* 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.
*/
let patternAmbientModules: PatternAmbientModule[];
let globalObjectType: ObjectType;
let globalFunctionType: ObjectType;
let globalArrayType: GenericType;
let globalReadonlyArrayType: GenericType;
let globalStringType: ObjectType;
let globalNumberType: ObjectType;
let globalBooleanType: ObjectType;
let globalRegExpType: ObjectType;
let globalThisType: GenericType;
let anyArrayType: Type;
let autoArrayType: Type;
let 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
let deferredGlobalESSymbolConstructorSymbol: Symbol;
let deferredGlobalESSymbolType: ObjectType;
let deferredGlobalTypedPropertyDescriptorType: GenericType;
let deferredGlobalPromiseType: GenericType;
let deferredGlobalPromiseConstructorSymbol: Symbol;
let deferredGlobalPromiseConstructorLikeType: ObjectType;
let deferredGlobalIterableType: GenericType;
let deferredGlobalIteratorType: GenericType;
let deferredGlobalIterableIteratorType: GenericType;
let deferredGlobalAsyncIterableType: GenericType;
let deferredGlobalAsyncIteratorType: GenericType;
let deferredGlobalAsyncIterableIteratorType: GenericType;
let deferredGlobalTemplateStringsArrayType: ObjectType;
let deferredJsxElementClassType: Type;
let deferredJsxElementType: Type;
let deferredJsxStatelessElementType: Type;
let deferredNodes: Node[];
let deferredUnusedIdentifierNodes: Node[];
let flowLoopStart = 0;
let flowLoopCount = 0;
let visitedFlowCount = 0;
const emptyStringType = getLiteralTypeForText(TypeFlags.StringLiteral, "");
const zeroType = getLiteralTypeForText(TypeFlags.NumberLiteral, "0");
const resolutionTargets: TypeSystemEntity[] = [];
const resolutionResults: boolean[] = [];
const resolutionPropertyNames: TypeSystemPropertyName[] = [];
let suggestionCount = 0;
const maximumSuggestionCount = 10;
const mergedSymbols: Symbol[] = [];
const symbolLinks: SymbolLinks[] = [];
const nodeLinks: NodeLinks[] = [];
const flowLoopCaches: Map[] = [];
const flowLoopNodes: FlowNode[] = [];
const flowLoopKeys: string[] = [];
const flowLoopTypes: Type[][] = [];
const visitedFlowNodes: FlowNode[] = [];
const visitedFlowTypes: FlowType[] = [];
const potentialThisCollisions: Node[] = [];
const potentialNewTargetCollisions: Node[] = [];
const awaitedTypeStack: number[] = [];
const diagnostics = createDiagnosticCollection();
const enum TypeFacts {
None = 0,
TypeofEQString = 1 << 0, // typeof x === "string"
TypeofEQNumber = 1 << 1, // typeof x === "number"
TypeofEQBoolean = 1 << 2, // typeof x === "boolean"
TypeofEQSymbol = 1 << 3, // typeof x === "symbol"
TypeofEQObject = 1 << 4, // typeof x === "object"
TypeofEQFunction = 1 << 5, // typeof x === "function"
TypeofEQHostObject = 1 << 6, // typeof x === "xxx"
TypeofNEString = 1 << 7, // typeof x !== "string"
TypeofNENumber = 1 << 8, // typeof x !== "number"
TypeofNEBoolean = 1 << 9, // typeof x !== "boolean"
TypeofNESymbol = 1 << 10, // typeof x !== "symbol"
TypeofNEObject = 1 << 11, // typeof x !== "object"
TypeofNEFunction = 1 << 12, // typeof x !== "function"
TypeofNEHostObject = 1 << 13, // typeof x !== "xxx"
EQUndefined = 1 << 14, // x === undefined
EQNull = 1 << 15, // x === null
EQUndefinedOrNull = 1 << 16, // x === undefined / x === null
NEUndefined = 1 << 17, // x !== undefined
NENull = 1 << 18, // x !== null
NEUndefinedOrNull = 1 << 19, // x != undefined / x != null
Truthy = 1 << 20, // x
Falsy = 1 << 21, // !x
Discriminatable = 1 << 22, // May have discriminant property
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,
}
const typeofEQFacts = createMapFromTemplate({
"string": TypeFacts.TypeofEQString,
"number": TypeFacts.TypeofEQNumber,
"boolean": TypeFacts.TypeofEQBoolean,
"symbol": TypeFacts.TypeofEQSymbol,
"undefined": TypeFacts.EQUndefined,
"object": TypeFacts.TypeofEQObject,
"function": TypeFacts.TypeofEQFunction
});
const typeofNEFacts = createMapFromTemplate({
"string": TypeFacts.TypeofNEString,
"number": TypeFacts.TypeofNENumber,
"boolean": TypeFacts.TypeofNEBoolean,
"symbol": TypeFacts.TypeofNESymbol,
"undefined": TypeFacts.NEUndefined,
"object": TypeFacts.TypeofNEObject,
"function": TypeFacts.TypeofNEFunction
});
const typeofTypesByName = createMapFromTemplate({
"string": stringType,
"number": numberType,
"boolean": booleanType,
"symbol": esSymbolType,
"undefined": undefinedType
});
const typeofType = createTypeofType();
let _jsxNamespace: string;
let _jsxFactoryEntity: EntityName;
let _jsxElementPropertiesName: string;
let _hasComputedJsxElementPropertiesName = false;
let _jsxElementChildrenPropertyName: string;
let _hasComputedJsxElementChildrenPropertyName = false;
/** Things we lazy load from the JSX namespace */
const jsxTypes = createMap();
const JsxNames = {
JSX: "JSX",
IntrinsicElements: "IntrinsicElements",
ElementClass: "ElementClass",
ElementAttributesPropertyNameContainer: "ElementAttributesProperty",
ElementChildrenAttributeNameContainer: "ElementChildrenAttribute",
Element: "Element",
IntrinsicAttributes: "IntrinsicAttributes",
IntrinsicClassAttributes: "IntrinsicClassAttributes"
};
const subtypeRelation = createMap();
const assignableRelation = createMap();
const comparableRelation = createMap();
const identityRelation = createMap();
const enumRelation = createMap();
// This is for caching the result of getSymbolDisplayBuilder. Do not access directly.
let _displayBuilder: SymbolDisplayBuilder;
type TypeSystemEntity = Symbol | Type | Signature;
const enum TypeSystemPropertyName {
Type,
ResolvedBaseConstructorType,
DeclaredType,
ResolvedReturnType
}
const enum CheckMode {
Normal = 0, // Normal type checking
SkipContextSensitive = 1, // Skip context sensitive function expressions
Inferential = 2, // Inferential typing
}
const builtinGlobals = createMap();
builtinGlobals.set(undefinedSymbol.name, undefinedSymbol);
initializeTypeChecker();
return checker;
function getJsxNamespace(): string {
if (!_jsxNamespace) {
_jsxNamespace = "React";
if (compilerOptions.jsxFactory) {
_jsxFactoryEntity = parseIsolatedEntityName(compilerOptions.jsxFactory, languageVersion);
if (_jsxFactoryEntity) {
_jsxNamespace = getFirstIdentifier(_jsxFactoryEntity).text;
}
}
else if (compilerOptions.reactNamespace) {
_jsxNamespace = compilerOptions.reactNamespace;
}
}
return _jsxNamespace;
}
function 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.
getDiagnostics(sourceFile, cancellationToken);
return emitResolver;
}
function 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);
diagnostics.add(diagnostic);
}
function createSymbol(flags: SymbolFlags, name: string) {
symbolCount++;
const symbol = (new Symbol(flags | SymbolFlags.Transient, name));
symbol.checkFlags = 0;
return symbol;
}
function isTransientSymbol(symbol: Symbol): symbol is TransientSymbol {
return (symbol.flags & SymbolFlags.Transient) !== 0;
}
function getExcludedSymbolFlags(flags: SymbolFlags): SymbolFlags {
let result: SymbolFlags = 0;
if (flags & SymbolFlags.BlockScopedVariable) result |= SymbolFlags.BlockScopedVariableExcludes;
if (flags & SymbolFlags.FunctionScopedVariable) result |= SymbolFlags.FunctionScopedVariableExcludes;
if (flags & SymbolFlags.Property) result |= SymbolFlags.PropertyExcludes;
if (flags & SymbolFlags.EnumMember) result |= SymbolFlags.EnumMemberExcludes;
if (flags & SymbolFlags.Function) result |= SymbolFlags.FunctionExcludes;
if (flags & SymbolFlags.Class) result |= SymbolFlags.ClassExcludes;
if (flags & SymbolFlags.Interface) result |= SymbolFlags.InterfaceExcludes;
if (flags & SymbolFlags.RegularEnum) result |= SymbolFlags.RegularEnumExcludes;
if (flags & SymbolFlags.ConstEnum) result |= SymbolFlags.ConstEnumExcludes;
if (flags & SymbolFlags.ValueModule) result |= SymbolFlags.ValueModuleExcludes;
if (flags & SymbolFlags.Method) result |= SymbolFlags.MethodExcludes;
if (flags & SymbolFlags.GetAccessor) result |= SymbolFlags.GetAccessorExcludes;
if (flags & SymbolFlags.SetAccessor) result |= SymbolFlags.SetAccessorExcludes;
if (flags & SymbolFlags.TypeParameter) result |= SymbolFlags.TypeParameterExcludes;
if (flags & SymbolFlags.TypeAlias) result |= SymbolFlags.TypeAliasExcludes;
if (flags & SymbolFlags.Alias) result |= SymbolFlags.AliasExcludes;
return result;
}
function recordMergedSymbol(target: Symbol, source: Symbol) {
if (!source.mergeId) {
source.mergeId = nextMergeId;
nextMergeId++;
}
mergedSymbols[source.mergeId] = target;
}
function cloneSymbol(symbol: Symbol): Symbol {
const result = createSymbol(symbol.flags, symbol.name);
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);
recordMergedSymbol(result, symbol);
return result;
}
function mergeSymbol(target: Symbol, source: Symbol) {
if (!(target.flags & 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 = createMap();
mergeSymbolTable(target.members, source.members);
}
if (source.exports) {
if (!target.exports) target.exports = createMap();
mergeSymbolTable(target.exports, source.exports);
}
recordMergedSymbol(target, source);
}
else if (target.flags & SymbolFlags.NamespaceModule) {
error(getNameOfDeclaration(source.declarations[0]), Diagnostics.Cannot_augment_module_0_with_value_exports_because_it_resolves_to_a_non_module_entity, 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 => {
error(getNameOfDeclaration(node) || node, message, symbolToString(source));
});
forEach(target.declarations, node => {
error(getNameOfDeclaration(node) || node, message, symbolToString(source));
});
}
}
function mergeSymbolTable(target: SymbolTable, source: SymbolTable) {
source.forEach((sourceSymbol, id) => {
let targetSymbol = target.get(id);
if (!targetSymbol) {
target.set(id, sourceSymbol);
}
else {
if (!(targetSymbol.flags & SymbolFlags.Transient)) {
targetSymbol = cloneSymbol(targetSymbol);
target.set(id, targetSymbol);
}
mergeSymbol(targetSymbol, sourceSymbol);
}
});
}
function 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)) {
mergeSymbolTable(globals, moduleAugmentation.symbol.exports);
}
else {
// find a module that about to be augmented
// do not validate names of augmentations that are defined in ambient context
const moduleNotFoundError = !isInAmbientContext(moduleName.parent.parent)
? Diagnostics.Invalid_module_name_in_augmentation_module_0_cannot_be_found
: undefined;
let mainModule = resolveExternalModuleNameWorker(moduleName, moduleName, moduleNotFoundError, /*isForAugmentation*/ true);
if (!mainModule) {
return;
}
// obtain item referenced by 'export='
mainModule = 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 : cloneSymbol(mainModule);
mergeSymbol(mainModule, moduleAugmentation.symbol);
}
else {
error(moduleName, Diagnostics.Cannot_augment_module_0_because_it_resolves_to_a_non_module_entity, moduleName.text);
}
}
}
function addToSymbolTable(target: SymbolTable, source: SymbolTable, message: DiagnosticMessage) {
source.forEach((sourceSymbol, id) => {
const targetSymbol = target.get(id);
if (targetSymbol) {
// Error on redeclarations
forEach(targetSymbol.declarations, addDeclarationDiagnostic(id, message));
}
else {
target.set(id, sourceSymbol);
}
});
function addDeclarationDiagnostic(id: string, message: DiagnosticMessage) {
return (declaration: Declaration) => diagnostics.add(createDiagnosticForNode(declaration, message, id));
}
}
function getSymbolLinks(symbol: Symbol): SymbolLinks {
if (symbol.flags & SymbolFlags.Transient) return symbol;
const id = getSymbolId(symbol);
return symbolLinks[id] || (symbolLinks[id] = {});
}
function getNodeLinks(node: Node): NodeLinks {
const nodeId = getNodeId(node);
return nodeLinks[nodeId] || (nodeLinks[nodeId] = { flags: 0 });
}
function getObjectFlags(type: Type): ObjectFlags {
return type.flags & TypeFlags.Object ? (type).objectFlags : 0;
}
function isGlobalSourceFile(node: Node) {
return node.kind === SyntaxKind.SourceFile && !isExternalOrCommonJsModule(node);
}
function 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 = resolveAlias(symbol);
// Unknown symbol means an error occurred in alias resolution, treat it as positive answer to avoid cascading errors
if (target === 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
*/
function getSymbolsOfParameterPropertyDeclaration(parameter: ParameterDeclaration, parameterName: string): [Symbol, Symbol] {
const constructorDeclaration = parameter.parent;
const classDeclaration = parameter.parent.parent;
const parameterSymbol = getSymbol(constructorDeclaration.locals, parameterName, SymbolFlags.Value);
const propertySymbol = 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");
}
function isBlockScopedNameDeclaredBeforeUse(declaration: Declaration, usage: Node): boolean {
const declarationFile = getSourceFileOfNode(declaration);
const useFile = getSourceFileOfNode(usage);
if (declarationFile !== useFile) {
if ((modulekind && (declarationFile.externalModuleIndicator || useFile.externalModuleIndicator)) ||
(!compilerOptions.outFile && !compilerOptions.out) ||
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 = 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 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 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 (isSameScopeDescendentOf(usage, declaration, container)) {
return true;
}
break;
}
switch (declaration.parent.parent.kind) {
case SyntaxKind.ForInStatement:
case SyntaxKind.ForOfStatement:
// ForIn/ForOf case - use site should not be used in expression part
if (isSameScopeDescendentOf(usage, (declaration.parent.parent).expression, container)) {
return true;
}
}
return false;
}
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 (getModifierFlags(current.parent) & ModifierFlags.Static) {
if (declaration.kind === SyntaxKind.MethodDeclaration) {
return true;
}
}
else {
const isDeclarationInstanceProperty = declaration.kind === SyntaxKind.PropertyDeclaration && !(getModifierFlags(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.
function resolveName(
location: Node | undefined,
name: string,
meaning: SymbolFlags,
nameNotFoundMessage: DiagnosticMessage,
nameArg: string | Identifier,
suggestedNameNotFoundMessage?: DiagnosticMessage): Symbol {
return resolveNameHelper(location, name, meaning, nameNotFoundMessage, nameArg, getSymbol, suggestedNameNotFoundMessage);
}
function 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 && !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
// type parameters are visible in parameter list, return type and type parameter list
? lastLocation === (location).type ||
lastLocation.kind === SyntaxKind.Parameter ||
lastLocation.kind === SyntaxKind.TypeParameter
// local types not visible outside the function body
: 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 = 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")) {
const localSymbol = getLocalSymbolForExportDefault(result);
if (localSymbol && (result.flags & meaning) && localSymbol.name === 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(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) && !(getModifierFlags(location) & ModifierFlags.Static)) {
const ctor = 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(getSymbolOfNode(location).members, name, meaning & SymbolFlags.Type)) {
if (!isTypeParameterSymbolDeclaredInContainer(result, location)) {
// ignore type parameters not declared in this container
result = undefined;
break;
}
if (lastLocation && getModifierFlags(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.
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.text) {
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(getSymbolOfNode(grandparent).members, name, meaning & SymbolFlags.Type)) {
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 = argumentsSymbol;
break loop;
}
break;
case SyntaxKind.FunctionExpression:
if (meaning & SymbolFlags.Variable && name === "arguments") {
result = argumentsSymbol;
break loop;
}
if (meaning & SymbolFlags.Function) {
const functionName = (location).name;
if (functionName && name === functionName.text) {
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;
}
if (result && nameNotFoundMessage && noUnusedIdentifiers) {
result.isReferenced = true;
}
if (!result) {
result = lookup(globals, name, meaning);
}
if (!result) {
if (nameNotFoundMessage) {
if (!errorLocation ||
!checkAndReportErrorForMissingPrefix(errorLocation, name, nameArg) &&
!checkAndReportErrorForExtendingInterface(errorLocation) &&
!checkAndReportErrorForUsingTypeAsNamespace(errorLocation, name, meaning) &&
!checkAndReportErrorForUsingTypeAsValue(errorLocation, name, meaning) &&
!checkAndReportErrorForUsingNamespaceModuleAsValue(errorLocation, name, meaning)) {
let suggestion: string | undefined;
if (suggestedNameNotFoundMessage && suggestionCount < maximumSuggestionCount) {
suggestion = getSuggestionForNonexistentSymbol(originalLocation, name, meaning);
if (suggestion) {
suggestionCount++;
error(errorLocation, suggestedNameNotFoundMessage, typeof nameArg === "string" ? nameArg : declarationNameToString(nameArg), suggestion);
}
}
if (!suggestion) {
error(errorLocation, nameNotFoundMessage, typeof nameArg === "string" ? nameArg : declarationNameToString(nameArg));
}
}
}
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;
error(errorLocation, Diagnostics.Initializer_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor,
declarationNameToString(propertyName), typeof nameArg === "string" ? nameArg : declarationNameToString(nameArg));
return undefined;
}
// Only check for block-scoped variable if we 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 (meaning & SymbolFlags.BlockScopedVariable ||
((meaning & SymbolFlags.Class || meaning & SymbolFlags.Enum) && (meaning & SymbolFlags.Value) === SymbolFlags.Value)) {
const exportOrLocalSymbol = getExportSymbolOfValueSymbolIfExported(result);
if (exportOrLocalSymbol.flags & SymbolFlags.BlockScopedVariable || exportOrLocalSymbol.flags & SymbolFlags.Class || exportOrLocalSymbol.flags & SymbolFlags.Enum) {
checkResolvedBlockScopedVariable(exportOrLocalSymbol, errorLocation);
}
}
// If we're in an external module, we can't reference value symbols created from UMD export declarations
if (result && isInExternalModule && (meaning & SymbolFlags.Value) === SymbolFlags.Value) {
const decls = result.declarations;
if (decls && decls.length === 1 && decls[0].kind === SyntaxKind.NamespaceExportDeclaration) {
error(errorLocation, Diagnostics._0_refers_to_a_UMD_global_but_the_current_file_is_a_module_Consider_adding_an_import_instead, name);
}
}
}
return result;
}
function isTypeParameterSymbolDeclaredInContainer(symbol: Symbol, container: Node) {
for (const decl of symbol.declarations) {
if (decl.kind === SyntaxKind.TypeParameter && decl.parent === container) {
return true;
}
}
return false;
}
function checkAndReportErrorForMissingPrefix(errorLocation: Node, name: string, nameArg: string | Identifier): boolean {
if ((errorLocation.kind === SyntaxKind.Identifier && (isTypeReferenceIdentifier(errorLocation)) || isInTypeQuery(errorLocation))) {
return false;
}
const container = getThisContainer(errorLocation, /*includeArrowFunctions*/ true);
let location = container;
while (location) {
if (isClassLike(location.parent)) {
const classSymbol = getSymbolOfNode(location.parent);
if (!classSymbol) {
break;
}
// Check to see if a static member exists.
const constructorType = getTypeOfSymbol(classSymbol);
if (getPropertyOfType(constructorType, name)) {
error(errorLocation, Diagnostics.Cannot_find_name_0_Did_you_mean_the_static_member_1_0, typeof nameArg === "string" ? nameArg : declarationNameToString(nameArg), symbolToString(classSymbol));
return true;
}
// No static member is present.
// Check if we're in an instance method and look for a relevant instance member.
if (location === container && !(getModifierFlags(location) & ModifierFlags.Static)) {
const instanceType = (getDeclaredTypeOfSymbol(classSymbol)).thisType;
if (getPropertyOfType(instanceType, name)) {
error(errorLocation, Diagnostics.Cannot_find_name_0_Did_you_mean_the_instance_member_this_0, typeof nameArg === "string" ? nameArg : declarationNameToString(nameArg));
return true;
}
}
}
location = location.parent;
}
return false;
}
function checkAndReportErrorForExtendingInterface(errorLocation: Node): boolean {
const expression = getEntityNameForExtendingInterface(errorLocation);
const isError = !!(expression && resolveEntityName(expression, SymbolFlags.Interface, /*ignoreErrors*/ true));
if (isError) {
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.
*/
function getEntityNameForExtendingInterface(node: Node): EntityNameExpression | undefined {
switch (node.kind) {
case SyntaxKind.Identifier:
case SyntaxKind.PropertyAccessExpression:
return node.parent ? getEntityNameForExtendingInterface(node.parent) : undefined;
case SyntaxKind.ExpressionWithTypeArguments:
Debug.assert(isEntityNameExpression((node).expression));
return (node).expression;
default:
return undefined;
}
}
function checkAndReportErrorForUsingTypeAsNamespace(errorLocation: Node, name: string, meaning: SymbolFlags): boolean {
if (meaning === SymbolFlags.Namespace) {
const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.Type & ~SymbolFlags.Value, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined));
if (symbol) {
error(errorLocation, Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_namespace_here, name);
return true;
}
}
return false;
}
function 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") {
error(errorLocation, Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_value_here, name);
return true;
}
const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.Type & ~SymbolFlags.Value, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined));
if (symbol && !(symbol.flags & SymbolFlags.NamespaceModule)) {
error(errorLocation, Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_value_here, name);
return true;
}
}
return false;
}
function checkAndReportErrorForUsingNamespaceModuleAsValue(errorLocation: Node, name: string, meaning: SymbolFlags): boolean {
if (meaning & (SymbolFlags.Value & ~SymbolFlags.NamespaceModule & ~SymbolFlags.Type)) {
const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.NamespaceModule & ~SymbolFlags.Value, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined));
if (symbol) {
error(errorLocation, Diagnostics.Cannot_use_namespace_0_as_a_value, name);
return true;
}
}
else if (meaning & (SymbolFlags.Type & ~SymbolFlags.NamespaceModule & ~SymbolFlags.Value)) {
const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.NamespaceModule & ~SymbolFlags.Type, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined));
if (symbol) {
error(errorLocation, Diagnostics.Cannot_use_namespace_0_as_a_type, name);
return true;
}
}
return false;
}
function checkResolvedBlockScopedVariable(result: Symbol, errorLocation: Node): void {
Debug.assert(!!(result.flags & SymbolFlags.BlockScopedVariable || result.flags & SymbolFlags.Class || result.flags & SymbolFlags.Enum));
// 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) && !isBlockScopedNameDeclaredBeforeUse(declaration, errorLocation)) {
if (result.flags & SymbolFlags.BlockScopedVariable) {
error(errorLocation, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, declarationNameToString(getNameOfDeclaration(declaration)));
}
else if (result.flags & SymbolFlags.Class) {
error(errorLocation, Diagnostics.Class_0_used_before_its_declaration, declarationNameToString(getNameOfDeclaration(declaration)));
}
else if (result.flags & SymbolFlags.RegularEnum) {
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.
*/
function isSameScopeDescendentOf(initial: Node, parent: Node, stopAt: Node): boolean {
return parent && !!findAncestor(initial, n => n === stopAt || isFunctionLike(n) ? "quit" : n === parent);
}
function getAnyImportSyntax(node: Node): AnyImportSyntax {
if (isAliasSymbolDeclaration(node)) {
if (node.kind === SyntaxKind.ImportEqualsDeclaration) {
return node;
}
return findAncestor(node, n => n.kind === SyntaxKind.ImportDeclaration) as ImportDeclaration;
}
}
function getDeclarationOfAliasSymbol(symbol: Symbol): Declaration | undefined {
return find(symbol.declarations, isAliasSymbolDeclaration);
}
function getTargetOfImportEqualsDeclaration(node: ImportEqualsDeclaration, dontResolveAlias: boolean): Symbol {
if (node.moduleReference.kind === SyntaxKind.ExternalModuleReference) {
return resolveExternalModuleSymbol(resolveExternalModuleName(node, getExternalModuleImportEqualsDeclarationExpression(node)));
}
return getSymbolOfPartOfRightHandSideOfImportEquals(node.moduleReference, dontResolveAlias);
}
function getTargetOfImportClause(node: ImportClause, dontResolveAlias: boolean): Symbol {
const moduleSymbol = resolveExternalModuleName(node, (node.parent).moduleSpecifier);
if (moduleSymbol) {
let exportDefaultSymbol: Symbol;
if (isShorthandAmbientModuleSymbol(moduleSymbol)) {
exportDefaultSymbol = moduleSymbol;
}
else {
const exportValue = moduleSymbol.exports.get("export=");
exportDefaultSymbol = exportValue
? getPropertyOfType(getTypeOfSymbol(exportValue), "default")
: resolveSymbol(moduleSymbol.exports.get("default"), dontResolveAlias);
}
if (!exportDefaultSymbol && !allowSyntheticDefaultImports) {
error(node.name, Diagnostics.Module_0_has_no_default_export, symbolToString(moduleSymbol));
}
else if (!exportDefaultSymbol && allowSyntheticDefaultImports) {
return resolveExternalModuleSymbol(moduleSymbol, dontResolveAlias) || resolveSymbol(moduleSymbol, dontResolveAlias);
}
return exportDefaultSymbol;
}
}
function getTargetOfNamespaceImport(node: NamespaceImport, dontResolveAlias: boolean): Symbol {
const moduleSpecifier = (node.parent.parent).moduleSpecifier;
return resolveESModuleSymbol(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'.
function combineValueAndTypeSymbols(valueSymbol: Symbol, typeSymbol: Symbol): Symbol {
if (valueSymbol.flags & (SymbolFlags.Type | SymbolFlags.Namespace)) {
return valueSymbol;
}
const result = createSymbol(valueSymbol.flags | typeSymbol.flags, valueSymbol.name);
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;
}
function getExportOfModule(symbol: Symbol, name: string, dontResolveAlias: boolean): Symbol {
if (symbol.flags & SymbolFlags.Module) {
return resolveSymbol(getExportsOfSymbol(symbol).get(name), dontResolveAlias);
}
}
function getPropertyOfVariable(symbol: Symbol, name: string): Symbol {
if (symbol.flags & SymbolFlags.Variable) {
const typeAnnotation = (symbol.valueDeclaration).type;
if (typeAnnotation) {
return resolveSymbol(getPropertyOfType(getTypeFromTypeNode(typeAnnotation), name));
}
}
}
function getExternalModuleMember(node: ImportDeclaration | ExportDeclaration, specifier: ImportOrExportSpecifier, dontResolveAlias?: boolean): Symbol {
const moduleSymbol = resolveExternalModuleName(node, node.moduleSpecifier);
const targetSymbol = resolveESModuleSymbol(moduleSymbol, node.moduleSpecifier, dontResolveAlias);
if (targetSymbol) {
const name = specifier.propertyName || specifier.name;
if (name.text) {
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=")) {
symbolFromVariable = getPropertyOfType(getTypeOfSymbol(targetSymbol), name.text);
}
else {
symbolFromVariable = getPropertyOfVariable(targetSymbol, name.text);
}
// if symbolFromVariable is export - get its final target
symbolFromVariable = resolveSymbol(symbolFromVariable, dontResolveAlias);
let symbolFromModule = getExportOfModule(targetSymbol, name.text, 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 && allowSyntheticDefaultImports && name.text === "default") {
symbolFromModule = resolveExternalModuleSymbol(moduleSymbol, dontResolveAlias) || resolveSymbol(moduleSymbol, dontResolveAlias);
}
const symbol = symbolFromModule && symbolFromVariable ?
combineValueAndTypeSymbols(symbolFromVariable, symbolFromModule) :
symbolFromModule || symbolFromVariable;
if (!symbol) {
error(name, Diagnostics.Module_0_has_no_exported_member_1, getFullyQualifiedName(moduleSymbol), declarationNameToString(name));
}
return symbol;
}
}
}
function getTargetOfImportSpecifier(node: ImportSpecifier, dontResolveAlias: boolean): Symbol {
return getExternalModuleMember(node.parent.parent.parent, node, dontResolveAlias);
}
function getTargetOfNamespaceExportDeclaration(node: NamespaceExportDeclaration, dontResolveAlias: boolean): Symbol {
return resolveExternalModuleSymbol(node.parent.symbol, dontResolveAlias);
}
function getTargetOfExportSpecifier(node: ExportSpecifier, meaning: SymbolFlags, dontResolveAlias?: boolean) {
return node.parent.parent.moduleSpecifier ?
getExternalModuleMember(node.parent.parent, node, dontResolveAlias) :
resolveEntityName(node.propertyName || node.name, meaning, /*ignoreErrors*/ false, dontResolveAlias);
}
function getTargetOfExportAssignment(node: ExportAssignment, dontResolveAlias: boolean): Symbol {
return resolveEntityName(node.expression, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace, /*ignoreErrors*/ false, dontResolveAlias);
}
function getTargetOfAliasDeclaration(node: Declaration, dontRecursivelyResolve?: boolean): Symbol {
switch (node.kind) {
case SyntaxKind.ImportEqualsDeclaration:
return getTargetOfImportEqualsDeclaration(node, dontRecursivelyResolve);
case SyntaxKind.ImportClause:
return getTargetOfImportClause(node, dontRecursivelyResolve);
case SyntaxKind.NamespaceImport:
return getTargetOfNamespaceImport(node, dontRecursivelyResolve);
case SyntaxKind.ImportSpecifier:
return getTargetOfImportSpecifier(node, dontRecursivelyResolve);
case SyntaxKind.ExportSpecifier:
return getTargetOfExportSpecifier(node, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace, dontRecursivelyResolve);
case SyntaxKind.ExportAssignment:
return getTargetOfExportAssignment(node, dontRecursivelyResolve);
case SyntaxKind.NamespaceExportDeclaration:
return getTargetOfNamespaceExportDeclaration(node, dontRecursivelyResolve);
}
}
function resolveSymbol(symbol: Symbol, dontResolveAlias?: boolean): Symbol {
const shouldResolve = !dontResolveAlias && symbol && symbol.flags & SymbolFlags.Alias && !(symbol.flags & (SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace));
return shouldResolve ? resolveAlias(symbol) : symbol;
}
function resolveAlias(symbol: Symbol): Symbol {
Debug.assert((symbol.flags & SymbolFlags.Alias) !== 0, "Should only get Alias here.");
const links = getSymbolLinks(symbol);
if (!links.target) {
links.target = resolvingSymbol;
const node = getDeclarationOfAliasSymbol(symbol);
Debug.assert(!!node);
const target = getTargetOfAliasDeclaration(node);
if (links.target === resolvingSymbol) {
links.target = target || unknownSymbol;
}
else {
error(node, Diagnostics.Circular_definition_of_import_alias_0, symbolToString(symbol));
}
}
else if (links.target === resolvingSymbol) {
links.target = unknownSymbol;
}
return links.target;
}
function markExportAsReferenced(node: ImportEqualsDeclaration | ExportAssignment | ExportSpecifier) {
const symbol = getSymbolOfNode(node);
const target = resolveAlias(symbol);
if (target) {
const markAlias = target === unknownSymbol ||
((target.flags & SymbolFlags.Value) && !isConstEnumOrConstEnumOnlyModule(target));
if (markAlias) {
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).
function markAliasSymbolAsReferenced(symbol: Symbol) {
const links = getSymbolLinks(symbol);
if (!links.referenced) {
links.referenced = true;
const node = getDeclarationOfAliasSymbol(symbol);
Debug.assert(!!node);
if (node.kind === SyntaxKind.ExportAssignment) {
// export default
checkExpressionCached((node).expression);
}
else if (node.kind === SyntaxKind.ExportSpecifier) {
// export { } or export { as foo }
checkExpressionCached((node).propertyName || (node).name);
}
else if (isInternalModuleImportEqualsDeclaration(node)) {
// import foo =
checkExpressionCached((node).moduleReference);
}
}
}
// This function is only for imports with entity names
function 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 resolveEntityName(entityName, SymbolFlags.Namespace, /*ignoreErrors*/ false, dontResolveAlias);
}
else {
// Case 2 in above example
// entityName.kind could be a QualifiedName or a Missing identifier
Debug.assert(entityName.parent.kind === SyntaxKind.ImportEqualsDeclaration);
return resolveEntityName(entityName, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace, /*ignoreErrors*/ false, dontResolveAlias);
}
}
function getFullyQualifiedName(symbol: Symbol): string {
return symbol.parent ? getFullyQualifiedName(symbol.parent) + "." + symbolToString(symbol) : symbolToString(symbol);
}
/**
* Resolves a qualified name and any involved aliases.
*/
function resolveEntityName(name: EntityNameOrEntityNameExpression, meaning: SymbolFlags, ignoreErrors?: boolean, dontResolveAlias?: boolean, location?: Node): Symbol | undefined {
if (nodeIsMissing(name)) {
return undefined;
}
let symbol: Symbol;
if (name.kind === SyntaxKind.Identifier) {
const message = meaning === SymbolFlags.Namespace ? Diagnostics.Cannot_find_namespace_0 : Diagnostics.Cannot_find_name_0;
symbol = resolveName(location || name, (name).text, 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 = resolveEntityName(left, SymbolFlags.Namespace, ignoreErrors, /*dontResolveAlias*/ false, location);
if (!namespace || nodeIsMissing(right)) {
return undefined;
}
else if (namespace === unknownSymbol) {
return namespace;
}
symbol = getSymbol(getExportsOfSymbol(namespace), right.text, meaning);
if (!symbol) {
if (!ignoreErrors) {
error(right, Diagnostics.Namespace_0_has_no_exported_member_1, 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) ?
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 : resolveAlias(symbol);
}
function resolveExternalModuleName(location: Node, moduleReferenceExpression: Expression): Symbol {
return resolveExternalModuleNameWorker(location, moduleReferenceExpression, Diagnostics.Cannot_find_module_0);
}
function resolveExternalModuleNameWorker(location: Node, moduleReferenceExpression: Expression, moduleNotFoundError: DiagnosticMessage, isForAugmentation = false): Symbol {
if (moduleReferenceExpression.kind !== SyntaxKind.StringLiteral && moduleReferenceExpression.kind !== SyntaxKind.NoSubstitutionTemplateLiteral) {
return;
}
const moduleReferenceLiteral = moduleReferenceExpression;
return resolveExternalModule(location, moduleReferenceLiteral.text, moduleNotFoundError, moduleReferenceLiteral, isForAugmentation);
}
function resolveExternalModule(location: Node, moduleReference: string, moduleNotFoundError: DiagnosticMessage, errorNode: Node, isForAugmentation = false): Symbol {
// Module names are escaped in our symbol table. However, string literal values aren't.
// Escape the name in the "require(...)" clause to ensure we find the right symbol.
const moduleName = escapeIdentifier(moduleReference);
if (moduleName === undefined) {
return;
}
const ambientModule = tryFindAmbientModule(moduleName, /*withAugmentations*/ true);
if (ambientModule) {
return ambientModule;
}
const isRelative = isExternalModuleNameRelative(moduleName);
const resolvedModule = getResolvedModule(getSourceFileOfNode(location), moduleReference);
const resolutionDiagnostic = resolvedModule && getResolutionDiagnostic(compilerOptions, resolvedModule);
const sourceFile = resolvedModule && !resolutionDiagnostic && host.getSourceFile(resolvedModule.resolvedFileName);
if (sourceFile) {
if (sourceFile.symbol) {
// merged symbol is module declaration symbol combined with all augmentations
return getMergedSymbol(sourceFile.symbol);
}
if (moduleNotFoundError) {
// report errors only if it was requested
error(errorNode, Diagnostics.File_0_is_not_a_module, sourceFile.fileName);
}
return undefined;
}
if (patternAmbientModules) {
const pattern = findBestPatternMatch(patternAmbientModules, _ => _.pattern, moduleName);
if (pattern) {
return 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;
error(errorNode, diag, moduleReference, resolvedModule.resolvedFileName);
}
else if (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);
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) {
error(errorNode, resolutionDiagnostic, moduleName, resolvedModule.resolvedFileName);
}
else {
const tsExtension = tryExtractTypeScriptExtension(moduleName);
if (tsExtension) {
const diag = Diagnostics.An_import_path_cannot_end_with_a_0_extension_Consider_importing_1_instead;
error(errorNode, diag, tsExtension, removeExtension(moduleName, tsExtension));
}
else {
error(errorNode, moduleNotFoundError, moduleName);
}
}
}
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.
function resolveExternalModuleSymbol(moduleSymbol: Symbol, dontResolveAlias?: boolean): Symbol {
return moduleSymbol && getMergedSymbol(resolveSymbol(moduleSymbol.exports.get("export="), 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).
function resolveESModuleSymbol(moduleSymbol: Symbol, moduleReferenceExpression: Expression, dontResolveAlias: boolean): Symbol {
const symbol = resolveExternalModuleSymbol(moduleSymbol, dontResolveAlias);
if (!dontResolveAlias && symbol && !(symbol.flags & (SymbolFlags.Module | SymbolFlags.Variable))) {
error(moduleReferenceExpression, Diagnostics.Module_0_resolves_to_a_non_module_entity_and_cannot_be_imported_using_this_construct, symbolToString(moduleSymbol));
}
return symbol;
}
function hasExportAssignmentSymbol(moduleSymbol: Symbol): boolean {
return moduleSymbol.exports.get("export=") !== undefined;
}
function getExportsOfModuleAsArray(moduleSymbol: Symbol): Symbol[] {
return symbolsToArray(getExportsOfModule(moduleSymbol));
}
function getExportsAndPropertiesOfModule(moduleSymbol: Symbol): Symbol[] {
const exports = getExportsOfModuleAsArray(moduleSymbol);
const exportEquals = resolveExternalModuleSymbol(moduleSymbol);
if (exportEquals !== moduleSymbol) {
addRange(exports, getPropertiesOfType(getTypeOfSymbol(exportEquals)));
}
return exports;
}
function tryGetMemberInModuleExports(memberName: string, moduleSymbol: Symbol): Symbol | undefined {
const symbolTable = getExportsOfModule(moduleSymbol);
if (symbolTable) {
return symbolTable.get(memberName);
}
}
function getExportsOfSymbol(symbol: Symbol): SymbolTable {
return symbol.flags & SymbolFlags.Module ? getExportsOfModule(symbol) : symbol.exports || emptySymbols;
}
function getExportsOfModule(moduleSymbol: Symbol): SymbolTable {
const links = getSymbolLinks(moduleSymbol);
return links.resolvedExports || (links.resolvedExports = getExportsForModule(moduleSymbol));
}
interface ExportCollisionTracker {
specifierText: string;
exportsWithDuplicate: ExportDeclaration[];
}
/**
* Extends one symbol table with another while collecting information on name collisions for error message generation into the `lookupTable` argument
* Not passing `lookupTable` and `exportNode` disables this collection, and just extends the tables
*/
function extendExportSymbols(target: SymbolTable, source: SymbolTable, lookupTable?: Map, exportNode?: ExportDeclaration) {
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 && resolveSymbol(targetSymbol) !== resolveSymbol(sourceSymbol)) {
const collisionTracker = lookupTable.get(id);
if (!collisionTracker.exportsWithDuplicate) {
collisionTracker.exportsWithDuplicate = [exportNode];
}
else {
collisionTracker.exportsWithDuplicate.push(exportNode);
}
}
});
}
function getExportsForModule(moduleSymbol: Symbol): SymbolTable {
const visitedSymbols: Symbol[] = [];
// A module defined by an 'export=' consists on one export that needs to be resolved
moduleSymbol = resolveExternalModuleSymbol(moduleSymbol);
return visit(moduleSymbol) || moduleSymbol.exports;
// 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("__export");
if (exportStars) {
const nestedSymbols = createMap();
const lookupTable = createMap();
for (const node of exportStars.declarations) {
const resolvedModule = resolveExternalModuleName(node, (node as ExportDeclaration).moduleSpecifier);
const exportedSymbols = visit(resolvedModule);
extendExportSymbols(
nestedSymbols,
exportedSymbols,
lookupTable,
node as ExportDeclaration
);
}
lookupTable.forEach(({ exportsWithDuplicate }, id) => {
// It's not an error if the file with multiple `export *`s with duplicate names exports a member with that name itself
if (id === "export=" || !(exportsWithDuplicate && exportsWithDuplicate.length) || symbols.has(id)) {
return;
}
for (const node of exportsWithDuplicate) {
diagnostics.add(createDiagnosticForNode(
node,
Diagnostics.Module_0_has_already_exported_a_member_named_1_Consider_explicitly_re_exporting_to_resolve_the_ambiguity,
lookupTable.get(id).specifierText,
id
));
}
});
extendExportSymbols(symbols, nestedSymbols);
}
return symbols;
}
}
function getMergedSymbol(symbol: Symbol): Symbol {
let merged: Symbol;
return symbol && symbol.mergeId && (merged = mergedSymbols[symbol.mergeId]) ? merged : symbol;
}
function getSymbolOfNode(node: Node): Symbol {
return getMergedSymbol(node.symbol);
}
function getParentOfSymbol(symbol: Symbol): Symbol {
return getMergedSymbol(symbol.parent);
}
function getExportSymbolOfValueSymbolIfExported(symbol: Symbol): Symbol {
return symbol && (symbol.flags & SymbolFlags.ExportValue) !== 0
? getMergedSymbol(symbol.exportSymbol)
: symbol;
}
function symbolIsValue(symbol: Symbol): boolean {
return !!(symbol.flags & SymbolFlags.Value || symbol.flags & SymbolFlags.Alias && resolveAlias(symbol).flags & SymbolFlags.Value);
}
function findConstructorDeclaration(node: ClassLikeDeclaration): ConstructorDeclaration {
const members = node.members;
for (const member of members) {
if (member.kind === SyntaxKind.Constructor && nodeIsPresent((member).body)) {
return member;
}
}
}
function createType(flags: TypeFlags): Type {
const result = new Type(checker, flags);
typeCount++;
result.id = typeCount;
return result;
}
function createIntrinsicType(kind: TypeFlags, intrinsicName: string): IntrinsicType {
const type = createType(kind);
type.intrinsicName = intrinsicName;
return type;
}
function createBooleanType(trueFalseTypes: Type[]): IntrinsicType & UnionType {
const type = getUnionType(trueFalseTypes);
type.flags |= TypeFlags.Boolean;
type.intrinsicName = "boolean";
return type;
}
function createObjectType(objectFlags: ObjectFlags, symbol?: Symbol): ObjectType {
const type = createType(TypeFlags.Object);
type.objectFlags = objectFlags;
type.symbol = symbol;
return type;
}
function createTypeofType() {
return getUnionType(convertToArray(typeofEQFacts.keys(), s => getLiteralTypeForText(TypeFlags.StringLiteral, s)));
}
// 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.
function isReservedMemberName(name: string) {
return name.charCodeAt(0) === CharacterCodes._ &&
name.charCodeAt(1) === CharacterCodes._ &&
name.charCodeAt(2) !== CharacterCodes._ &&
name.charCodeAt(2) !== CharacterCodes.at;
}
function getNamedMembers(members: SymbolTable): Symbol[] {
let result: Symbol[];
members.forEach((symbol, id) => {
if (!isReservedMemberName(id)) {
if (!result) result = [];
if (symbolIsValue(symbol)) {
result.push(symbol);
}
}
});
return result || emptyArray;
}
function setStructuredTypeMembers(type: StructuredType, members: SymbolTable, callSignatures: Signature[], constructSignatures: Signature[], stringIndexInfo: IndexInfo, numberIndexInfo: IndexInfo): ResolvedType {
(type).members = members;
(type).properties = getNamedMembers(members);
(type).callSignatures = callSignatures;
(type).constructSignatures = constructSignatures;
if (stringIndexInfo) (type).stringIndexInfo = stringIndexInfo;
if (numberIndexInfo) (type).numberIndexInfo = numberIndexInfo;
return type;
}
function createAnonymousType(symbol: Symbol, members: SymbolTable, callSignatures: Signature[], constructSignatures: Signature[], stringIndexInfo: IndexInfo, numberIndexInfo: IndexInfo): ResolvedType {
return setStructuredTypeMembers(createObjectType(ObjectFlags.Anonymous, symbol),
members, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo);
}
function 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 && !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(getSymbolOfNode(location).exports)) {
return result;
}
break;
}
}
return callback(globals);
}
function getQualifiedLeftMeaning(rightMeaning: SymbolFlags) {
// If we are looking in value space, the parent meaning is value, other wise it is namespace
return rightMeaning === SymbolFlags.Value ? SymbolFlags.Value : SymbolFlags.Namespace;
}
function getAccessibleSymbolChain(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags, useOnlyExternalAliasing: boolean): Symbol[] {
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 (!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 = getAccessibleSymbolChain(symbolFromSymbolTable.parent, enclosingDeclaration, 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, 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.name))) {
return [symbol];
}
// Check if symbol is any of the alias
return forEachEntry(symbols, symbolFromSymbolTable => {
if (symbolFromSymbolTable.flags & SymbolFlags.Alias
&& symbolFromSymbolTable.name !== "export="
&& !getDeclarationOfKind(symbolFromSymbolTable, SyntaxKind.ExportSpecifier)) {
if (!useOnlyExternalAliasing || // We can use any type of alias to get the name
// Is this external alias, then use it to name
ts.forEach(symbolFromSymbolTable.declarations, isExternalModuleImportEqualsDeclaration)) {
const resolvedImportedSymbol = resolveAlias(symbolFromSymbolTable);
if (isAccessible(symbolFromSymbolTable, resolveAlias(symbolFromSymbolTable))) {
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, getQualifiedLeftMeaning(meaning))) {
return [symbolFromSymbolTable].concat(accessibleSymbolsFromExports);
}
}
}
});
}
}
if (symbol) {
if (!(isPropertyOrMethodDeclarationSymbol(symbol))) {
return forEachSymbolTableInScope(enclosingDeclaration, getAccessibleSymbolChainFromSymbolTable);
}
}
}
function needsQualification(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags) {
let qualify = false;
forEachSymbolTableInScope(enclosingDeclaration, symbolTable => {
// If symbol of this name is not available in the symbol table we are ok
let symbolFromSymbolTable = symbolTable.get(symbol.name);
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)) ? resolveAlias(symbolFromSymbolTable) : symbolFromSymbolTable;
if (symbolFromSymbolTable.flags & meaning) {
qualify = true;
return true;
}
// Continue to the next symbol table
return false;
});
return qualify;
}
function isPropertyOrMethodDeclarationSymbol(symbol: Symbol) {
if (symbol.declarations && symbol.declarations.length) {
for (const declaration of symbol.declarations) {
switch (declaration.kind) {
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
continue;
default:
return false;
}
}
return true;
}
return false;
}
/**
* Check if the given symbol in given enclosing declaration is accessible and mark all associated alias to be visible if requested
*
* @param symbol a Symbol to check if accessible
* @param enclosingDeclaration a Node containing reference to the symbol
* @param meaning a SymbolFlags to check if such meaning of the symbol is accessible
* @param shouldComputeAliasToMakeVisible a boolean value to indicate whether to return aliases to be mark visible in case the symbol is accessible
*/
function isSymbolAccessible(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags, shouldComputeAliasesToMakeVisible: boolean): SymbolAccessibilityResult {
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 = getAccessibleSymbolChain(symbol, enclosingDeclaration, meaningToLook, /*useOnlyExternalAliasing*/ false);
if (accessibleSymbolChain) {
const hasAccessibleDeclarations = hasVisibleDeclarations(accessibleSymbolChain[0], shouldComputeAliasesToMakeVisible);
if (!hasAccessibleDeclarations) {
return {
accessibility: SymbolAccessibility.NotAccessible,
errorSymbolName: symbolToString(initialSymbol, enclosingDeclaration, meaning),
errorModuleName: symbol !== initialSymbol ? 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 = getQualifiedLeftMeaning(meaning);
symbol = 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: symbolToString(initialSymbol, enclosingDeclaration, meaning),
errorModuleName: symbolToString(symbolExternalModule)
};
}
}
// Just a local name that is not accessible
return {
accessibility: SymbolAccessibility.NotAccessible,
errorSymbolName: symbolToString(initialSymbol, enclosingDeclaration, meaning),
};
}
return { accessibility: SymbolAccessibility.Accessible };
function getExternalModuleContainer(declaration: Node) {
const node = findAncestor(declaration, hasExternalModuleSymbol);
return node && getSymbolOfNode(node);
}
}
function hasExternalModuleSymbol(declaration: Node) {
return isAmbientModule(declaration) || (declaration.kind === SyntaxKind.SourceFile && isExternalOrCommonJsModule(declaration));
}
function hasVisibleDeclarations(symbol: Symbol, shouldComputeAliasToMakeVisible: boolean): SymbolVisibilityResult {
let aliasesToMakeVisible: AnyImportSyntax[];
if (forEach(symbol.declarations, declaration => !getIsDeclarationVisible(declaration))) {
return undefined;
}
return { accessibility: SymbolAccessibility.Accessible, aliasesToMakeVisible };
function getIsDeclarationVisible(declaration: Declaration) {
if (!isDeclarationVisible(declaration)) {
// Mark the unexported alias as visible if its parent is visible
// because these kind of aliases can be used to name types in declaration file
const anyImportSyntax = getAnyImportSyntax(declaration);
if (anyImportSyntax &&
!(getModifierFlags(anyImportSyntax) & ModifierFlags.Export) && // import clause without export
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) {
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;
}
}
function 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 = getFirstIdentifier(entityName);
const symbol = resolveName(enclosingDeclaration, (firstIdentifier).text, meaning, /*nodeNotFoundErrorMessage*/ undefined, /*nameArg*/ undefined);
// Verify if the symbol is accessible
return (symbol && hasVisibleDeclarations(symbol, /*shouldComputeAliasToMakeVisible*/ true)) || {
accessibility: SymbolAccessibility.NotAccessible,
errorSymbolName: getTextOfNode(firstIdentifier),
errorNode: firstIdentifier
};
}
function writeKeyword(writer: SymbolWriter, kind: SyntaxKind) {
writer.writeKeyword(tokenToString(kind));
}
function writePunctuation(writer: SymbolWriter, kind: SyntaxKind) {
writer.writePunctuation(tokenToString(kind));
}
function writeSpace(writer: SymbolWriter) {
writer.writeSpace(" ");
}
function symbolToString(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): string {
const writer = getSingleLineStringWriter();
getSymbolDisplayBuilder().buildSymbolDisplay(symbol, writer, enclosingDeclaration, meaning);
const result = writer.string();
releaseStringWriter(writer);
return result;
}
function signatureToString(signature: Signature, enclosingDeclaration?: Node, flags?: TypeFormatFlags, kind?: SignatureKind): string {
const writer = getSingleLineStringWriter();
getSymbolDisplayBuilder().buildSignatureDisplay(signature, writer, enclosingDeclaration, flags, kind);
const result = writer.string();
releaseStringWriter(writer);
return result;
}
function typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string {
const writer = getSingleLineStringWriter();
getSymbolDisplayBuilder().buildTypeDisplay(type, writer, enclosingDeclaration, flags);
let result = writer.string();
releaseStringWriter(writer);
const maxLength = compilerOptions.noErrorTruncation || flags & TypeFormatFlags.NoTruncation ? undefined : 100;
if (maxLength && result.length >= maxLength) {
result = result.substr(0, maxLength - "...".length) + "...";
}
return result;
}
function createNodeBuilder() {
let context: NodeBuilderContext;
return {
typeToTypeNode: (type: Type, enclosingDeclaration?: Node, flags?: NodeBuilderFlags) => {
context = createNodeBuilderContext(enclosingDeclaration, flags);
const resultingNode = typeToTypeNodeHelper(type);
const result = context.encounteredError ? undefined : resultingNode;
return result;
},
indexInfoToIndexSignatureDeclaration: (indexInfo: IndexInfo, kind: IndexKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags) => {
context = createNodeBuilderContext(enclosingDeclaration, flags);
const resultingNode = indexInfoToIndexSignatureDeclarationHelper(indexInfo, kind);
const result = context.encounteredError ? undefined : resultingNode;
return result;
},
signatureToSignatureDeclaration: (signature: Signature, kind: SyntaxKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags) => {
context = createNodeBuilderContext(enclosingDeclaration, flags);
const resultingNode = signatureToSignatureDeclarationHelper(signature, kind);
const result = context.encounteredError ? undefined : resultingNode;
return result;
}
};
interface NodeBuilderContext {
readonly enclosingDeclaration: Node | undefined;
readonly flags: NodeBuilderFlags | undefined;
encounteredError: boolean;
inObjectTypeLiteral: boolean;
checkAlias: boolean;
symbolStack: Symbol[] | undefined;
}
function createNodeBuilderContext(enclosingDeclaration: Node | undefined, flags: NodeBuilderFlags | undefined): NodeBuilderContext {
return {
enclosingDeclaration,
flags,
encounteredError: false,
inObjectTypeLiteral: false,
checkAlias: true,
symbolStack: undefined
};
}
function typeToTypeNodeHelper(type: Type): TypeNode {
if (!type) {
context.encounteredError = true;
// TODO(aozgaa): should we return implict any (undefined) or explicit any (keywordtypenode)?
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.Enum) {
const name = symbolToName(type.symbol, /*expectsIdentifier*/ false);
return createTypeReferenceNode(name, /*typeArguments*/ undefined);
}
if (type.flags & (TypeFlags.StringLiteral)) {
return createLiteralTypeNode((createLiteral((type).text)));
}
if (type.flags & (TypeFlags.NumberLiteral)) {
return createLiteralTypeNode((createNumericLiteral((type).text)));
}
if (type.flags & TypeFlags.BooleanLiteral) {
return (type).intrinsicName === "true" ? createTrue() : createFalse();
}
if (type.flags & TypeFlags.EnumLiteral) {
const name = symbolToName(type.symbol, /*expectsIdentifier*/ false);
return createTypeReferenceNode(name, /*typeArguments*/ undefined);
}
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.inObjectTypeLiteral) {
if (!context.encounteredError && !(context.flags & NodeBuilderFlags.allowThisInObjectLiteral)) {
context.encounteredError = true;
}
}
return createThis();
}
const objectFlags = getObjectFlags(type);
if (objectFlags & ObjectFlags.Reference) {
Debug.assert(!!(type.flags & TypeFlags.Object));
return typeReferenceToTypeNode(type);
}
if (objectFlags & ObjectFlags.ClassOrInterface) {
Debug.assert(!!(type.flags & TypeFlags.Object));
const name = symbolToName(type.symbol, /*expectsIdentifier*/ false);
// TODO(aozgaa): handle type arguments.
return createTypeReferenceNode(name, /*typeArguments*/ undefined);
}
if (type.flags & TypeFlags.TypeParameter) {
const name = symbolToName(type.symbol, /*expectsIdentifier*/ false);
// Ignore constraint/default when creating a usage (as opposed to declaration) of a type parameter.
return createTypeReferenceNode(name, /*typeArguments*/ undefined);
}
if (context.checkAlias && type.aliasSymbol) {
const name = symbolToName(type.aliasSymbol, /*expectsIdentifier*/ false);
const typeArgumentNodes = type.aliasTypeArguments && mapToTypeNodeArray(type.aliasTypeArguments);
return createTypeReferenceNode(name, typeArgumentNodes);
}
context.checkAlias = false;
if (type.flags & TypeFlags.Union) {
const formattedUnionTypes = formatUnionTypes((type).types);
const unionTypeNodes = formattedUnionTypes && mapToTypeNodeArray(formattedUnionTypes);
if (unionTypeNodes && unionTypeNodes.length > 0) {
return createUnionTypeNode(unionTypeNodes);
}
else {
if (!context.encounteredError && !(context.flags & NodeBuilderFlags.allowEmptyUnionOrIntersection)) {
context.encounteredError = true;
}
return undefined;
}
}
if (type.flags & TypeFlags.Intersection) {
return createIntersectionTypeNode(mapToTypeNodeArray((type as IntersectionType).types));
}
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);
return createTypeOperatorNode(indexTypeNode);
}
if (type.flags & TypeFlags.IndexedAccess) {
const objectTypeNode = typeToTypeNodeHelper((type).objectType);
const indexTypeNode = typeToTypeNodeHelper((type).indexType);
return createIndexedAccessTypeNode(objectTypeNode, indexTypeNode);
}
Debug.fail("Should be unreachable.");
function mapToTypeNodeArray(types: Type[]): TypeNode[] {
const result = [];
for (const type of types) {
const typeNode = typeToTypeNodeHelper(type);
if (typeNode) {
result.push(typeNode);
}
}
return result;
}
function createMappedTypeNodeFromType(type: MappedType) {
Debug.assert(!!(type.flags & TypeFlags.Object));
const typeParameter = getTypeParameterFromMappedType(type);
const typeParameterNode = typeParameterToDeclaration(typeParameter);
const templateType = getTemplateTypeFromMappedType(type);
const templateTypeNode = typeToTypeNodeHelper(templateType);
const readonlyToken = type.declaration && type.declaration.readonlyToken ? createToken(SyntaxKind.ReadonlyKeyword) : undefined;
const questionToken = type.declaration && type.declaration.questionToken ? createToken(SyntaxKind.QuestionToken) : undefined;
return createMappedTypeNode(readonlyToken, typeParameterNode, questionToken, templateTypeNode);
}
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 && !getBaseTypeVariableOfClass(symbol) ||
symbol.flags & (SymbolFlags.Enum | SymbolFlags.ValueModule) ||
shouldWriteTypeOfFunctionSymbol()) {
return createTypeQueryNodeFromSymbol(symbol);
}
else if (contains(context.symbolStack, symbol)) {
// If type is an anonymous type literal in a type alias declaration, use type alias name
const typeAlias = getTypeAliasForTypeLiteral(type);
if (typeAlias) {
// The specified symbol flags need to be reinterpreted as type flags
const entityName = symbolToName(typeAlias, /*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 && // typeof static method
forEach(symbol.declarations, declaration => getModifierFlags(declaration) & ModifierFlags.Static));
const isNonLocalFunctionSymbol = !!(symbol.flags & SymbolFlags.Function) &&
(symbol.parent || // is exported function symbol
forEach(symbol.declarations, declaration =>
declaration.parent.kind === SyntaxKind.SourceFile || declaration.parent.kind === SyntaxKind.ModuleBlock));
if (isStaticMethodSymbol || isNonLocalFunctionSymbol) {
// typeof is allowed only for static/non local functions
return contains(context.symbolStack, symbol); // it is type of the symbol uses itself recursively
}
}
}
function createTypeNodeFromObjectType(type: ObjectType): TypeNode {
if (type.objectFlags & ObjectFlags.Mapped) {
if (getConstraintTypeFromMappedType(type).flags & (TypeFlags.TypeParameter | TypeFlags.Index)) {
return createMappedTypeNodeFromType(type);
}
}
const resolved = resolveStructuredTypeMembers(type);
if (!resolved.properties.length && !resolved.stringIndexInfo && !resolved.numberIndexInfo) {
if (!resolved.callSignatures.length && !resolved.constructSignatures.length) {
return createTypeLiteralNode(/*members*/ undefined);
}
if (resolved.callSignatures.length === 1 && !resolved.constructSignatures.length) {
const signature = resolved.callSignatures[0];
return signatureToSignatureDeclarationHelper(signature, SyntaxKind.FunctionType);
}
if (resolved.constructSignatures.length === 1 && !resolved.callSignatures.length) {
const signature = resolved.constructSignatures[0];
return signatureToSignatureDeclarationHelper(signature, SyntaxKind.ConstructorType);
}
}
const saveInObjectTypeLiteral = context.inObjectTypeLiteral;
context.inObjectTypeLiteral = true;
const members = createTypeNodesFromResolvedType(resolved);
context.inObjectTypeLiteral = saveInObjectTypeLiteral;
return createTypeLiteralNode(members);
}
function createTypeQueryNodeFromSymbol(symbol: Symbol) {
const entityName = symbolToName(symbol, /*expectsIdentifier*/ false);
return createTypeQueryNode(entityName);
}
function typeReferenceToTypeNode(type: TypeReference) {
const typeArguments: Type[] = type.typeArguments || emptyArray;
if (type.target === globalArrayType) {
const elementType = typeToTypeNodeHelper(typeArguments[0]);
return createArrayTypeNode(elementType);
}
else if (type.target.objectFlags & ObjectFlags.Tuple) {
if (typeArguments.length > 0) {
const tupleConstituentNodes = mapToTypeNodeArray(typeArguments.slice(0, getTypeReferenceArity(type)));
if (tupleConstituentNodes && tupleConstituentNodes.length > 0) {
return createTupleTypeNode(tupleConstituentNodes);
}
}
if (!context.encounteredError && !(context.flags & NodeBuilderFlags.allowEmptyTuple)) {
context.encounteredError = true;
}
return undefined;
}
else {
const outerTypeParameters = type.target.outerTypeParameters;
let i = 0;
let qualifiedName: QualifiedName | undefined = undefined;
if (outerTypeParameters) {
const length = outerTypeParameters.length;
while (i < length) {
// Find group of type arguments for type parameters with the same declaring container.
const start = i;
const parent = getParentSymbolOfTypeParameter(outerTypeParameters[i]);
do {
i++;
} while (i < length && getParentSymbolOfTypeParameter(outerTypeParameters[i]) === parent);
// When type parameters are their own type arguments for the whole group (i.e. we have
// the default outer type arguments), we don't show the group.
if (!rangeEquals(outerTypeParameters, typeArguments, start, i)) {
const qualifiedNamePart = symbolToName(parent, /*expectsIdentifier*/ true);
if (!qualifiedName) {
qualifiedName = createQualifiedName(qualifiedNamePart, /*right*/ undefined);
}
else {
Debug.assert(!qualifiedName.right);
qualifiedName.right = qualifiedNamePart;
qualifiedName = createQualifiedName(qualifiedName, /*right*/ undefined);
}
}
}
}
let entityName: EntityName = undefined;
const nameIdentifier = symbolToName(type.symbol, /*expectsIdentifier*/ true);
if (qualifiedName) {
Debug.assert(!qualifiedName.right);
qualifiedName.right = nameIdentifier;
entityName = qualifiedName;
}
else {
entityName = nameIdentifier;
}
const typeParameterCount = (type.target.typeParameters || emptyArray).length;
const typeArgumentNodes = some(typeArguments) ? mapToTypeNodeArray(typeArguments.slice(i, typeParameterCount - i)) : undefined;
return createTypeReferenceNode(entityName, typeArgumentNodes);
}
}
function createTypeNodesFromResolvedType(resolvedType: ResolvedType): TypeElement[] {
const typeElements: TypeElement[] = [];
for (const signature of resolvedType.callSignatures) {
typeElements.push(signatureToSignatureDeclarationHelper(signature, SyntaxKind.CallSignature));
}
for (const signature of resolvedType.constructSignatures) {
typeElements.push(signatureToSignatureDeclarationHelper(signature, SyntaxKind.ConstructSignature));
}
if (resolvedType.stringIndexInfo) {
typeElements.push(indexInfoToIndexSignatureDeclarationHelper(resolvedType.stringIndexInfo, IndexKind.String));
}
if (resolvedType.numberIndexInfo) {
typeElements.push(indexInfoToIndexSignatureDeclarationHelper(resolvedType.numberIndexInfo, IndexKind.Number));
}
const properties = resolvedType.properties;
if (!properties) {
return typeElements;
}
for (const propertySymbol of properties) {
const propertyType = getTypeOfSymbol(propertySymbol);
const oldDeclaration = propertySymbol.declarations && propertySymbol.declarations[0] as TypeElement;
if (!oldDeclaration) {
return;
}
const propertyName = oldDeclaration.name;
const optionalToken = propertySymbol.flags & SymbolFlags.Optional ? createToken(SyntaxKind.QuestionToken) : undefined;
if (propertySymbol.flags & (SymbolFlags.Function | SymbolFlags.Method) && !getPropertiesOfObjectType(propertyType).length) {
const signatures = getSignaturesOfType(propertyType, SignatureKind.Call);
for (const signature of signatures) {
const methodDeclaration = signatureToSignatureDeclarationHelper(signature, SyntaxKind.MethodSignature);
methodDeclaration.name = propertyName;
methodDeclaration.questionToken = optionalToken;
typeElements.push(methodDeclaration);
}
}
else {
// TODO(aozgaa): should we create a node with explicit or implict any?
const propertyTypeNode = propertyType ? typeToTypeNodeHelper(propertyType) : createKeywordTypeNode(SyntaxKind.AnyKeyword);
typeElements.push(createPropertySignature(
propertyName,
optionalToken,
propertyTypeNode,
/*initializer*/ undefined));
}
}
return typeElements.length ? typeElements : undefined;
}
}
function indexInfoToIndexSignatureDeclarationHelper(indexInfo: IndexInfo, kind: IndexKind): IndexSignatureDeclaration {
const indexerTypeNode = createKeywordTypeNode(kind === IndexKind.String ? SyntaxKind.StringKeyword : SyntaxKind.NumberKeyword);
const name = getNameFromIndexInfo(indexInfo);
const indexingParameter = createParameter(
/*decorators*/ undefined,
/*modifiers*/ undefined,
/*dotDotDotToken*/ undefined,
name,
/*questionToken*/ undefined,
indexerTypeNode,
/*initializer*/ undefined);
const typeNode = typeToTypeNodeHelper(indexInfo.type);
return createIndexSignature(
/*decorators*/ undefined,
indexInfo.isReadonly ? [createToken(SyntaxKind.ReadonlyKeyword)] : undefined,
[indexingParameter],
typeNode);
}
function signatureToSignatureDeclarationHelper(signature: Signature, kind: SyntaxKind): SignatureDeclaration {
const typeParameters = signature.typeParameters && signature.typeParameters.map(parameter => typeParameterToDeclaration(parameter));
const parameters = signature.parameters.map(parameter => symbolToParameterDeclaration(parameter));
let returnTypeNode: TypeNode | TypePredicate;
if (signature.typePredicate) {
const typePredicate = signature.typePredicate;
const parameterName = typePredicate.kind === TypePredicateKind.Identifier ? createIdentifier((typePredicate).parameterName) : createThisTypeNode();
const typeNode = typeToTypeNodeHelper(typePredicate.type);
returnTypeNode = createTypePredicateNode(parameterName, typeNode);
}
else {
const returnType = getReturnTypeOfSignature(signature);
returnTypeNode = returnType && typeToTypeNodeHelper(returnType);
}
const returnTypeNodeExceptAny = returnTypeNode && returnTypeNode.kind !== SyntaxKind.AnyKeyword ? returnTypeNode : undefined;
return createSignatureDeclaration(kind, typeParameters, parameters, returnTypeNodeExceptAny);
}
function typeParameterToDeclaration(type: TypeParameter): TypeParameterDeclaration {
const constraint = getConstraintFromTypeParameter(type);
const constraintNode = constraint && typeToTypeNodeHelper(constraint);
const defaultParameter = getDefaultFromTypeParameter(type);
const defaultParameterNode = defaultParameter && typeToTypeNodeHelper(defaultParameter);
const name = symbolToName(type.symbol, /*expectsIdentifier*/ true);
return createTypeParameterDeclaration(name, constraintNode, defaultParameterNode);
}
function symbolToParameterDeclaration(parameterSymbol: Symbol): ParameterDeclaration {
const parameterDeclaration = getDeclarationOfKind(parameterSymbol, SyntaxKind.Parameter);
const parameterType = getTypeOfSymbol(parameterSymbol);
const parameterTypeNode = typeToTypeNodeHelper(parameterType);
// TODO(aozgaa): In the future, check initializer accessibility.
const parameterNode = createParameter(
parameterDeclaration.decorators,
parameterDeclaration.modifiers,
parameterDeclaration.dotDotDotToken && createToken(SyntaxKind.DotDotDotToken),
// Clone name to remove trivia.
getSynthesizedClone(parameterDeclaration.name),
parameterDeclaration.questionToken && createToken(SyntaxKind.QuestionToken),
parameterTypeNode,
parameterDeclaration.initializer);
return parameterNode;
}
function symbolToName(symbol: Symbol, expectsIdentifier: true): Identifier;
function symbolToName(symbol: Symbol, expectsIdentifier: false): EntityName;
function symbolToName(symbol: Symbol, 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) {
chain = getSymbolChain(symbol, SymbolFlags.None, /*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 parentIndex = index - 1;
const symbol = chain[index];
let typeParameterString = "";
if (index > 0) {
const parentSymbol = chain[index - 1];
let typeParameters: TypeParameter[];
if (getCheckFlags(symbol) & CheckFlags.Instantiated) {
typeParameters = getTypeParametersOfClassOrInterface(parentSymbol);
}
else {
const targetSymbol = getTargetSymbol(parentSymbol);
if (targetSymbol.flags & (SymbolFlags.Class | SymbolFlags.Interface | SymbolFlags.TypeAlias)) {
typeParameters = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol);
}
}
if (typeParameters && typeParameters.length > 0) {
if (!context.encounteredError && !(context.flags & NodeBuilderFlags.allowTypeParameterInQualifiedName)) {
context.encounteredError = true;
}
const writer = getSingleLineStringWriter();
const displayBuilder = getSymbolDisplayBuilder();
displayBuilder.buildDisplayForTypeParametersAndDelimiters(typeParameters, writer, context.enclosingDeclaration, 0);
typeParameterString = writer.string();
releaseStringWriter(writer);
}
}
const symbolName = getNameOfSymbol(symbol);
const symbolNameWithTypeParameters = typeParameterString.length > 0 ? `${symbolName}<${typeParameterString}>` : symbolName;
const identifier = createIdentifier(symbolNameWithTypeParameters);
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 = getAccessibleSymbolChain(symbol, context.enclosingDeclaration, meaning, /*useOnlyExternalAliasing*/ false);
let parentSymbol: Symbol;
if (!accessibleSymbolChain ||
needsQualification(accessibleSymbolChain[0], context.enclosingDeclaration, accessibleSymbolChain.length === 1 ? meaning : getQualifiedLeftMeaning(meaning))) {
// Go up and add our parent.
const parent = getParentOfSymbol(accessibleSymbolChain ? accessibleSymbolChain[0] : symbol);
if (parent) {
const parentChain = getSymbolChain(parent, 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, 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): 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 symbol.name;
}
}
}
function typePredicateToString(typePredicate: TypePredicate, enclosingDeclaration?: Declaration, flags?: TypeFormatFlags): string {
const writer = getSingleLineStringWriter();
getSymbolDisplayBuilder().buildTypePredicateDisplay(typePredicate, writer, enclosingDeclaration, flags);
const result = writer.string();
releaseStringWriter(writer);
return result;
}
function 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 ? booleanType : (t).baseType;
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(nullType);
if (flags & TypeFlags.Undefined) result.push(undefinedType);
return result || types;
}
function visibilityToString(flags: ModifierFlags): string | undefined {
if (flags === ModifierFlags.Private) {
return "private";
}
if (flags === ModifierFlags.Protected) {
return "protected";
}
return "public";
}
function 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 getSymbolOfNode(node);
}
}
return undefined;
}
function isTopLevelInExternalModuleAugmentation(node: Node): boolean {
return node && node.parent &&
node.parent.kind === SyntaxKind.ModuleBlock &&
isExternalModuleAugmentation(node.parent.parent);
}
function literalTypeToString(type: LiteralType) {
return type.flags & TypeFlags.StringLiteral ? `"${escapeString((type).text)}"` : (type).text;
}
function 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 symbol.name;
}
function getSymbolDisplayBuilder(): SymbolDisplayBuilder {
/**
* 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(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 = getNameOfSymbol(symbol);
const firstChar = symbolName.charCodeAt(0);
const needsElementAccess = !isIdentifierStart(firstChar, languageVersion);
if (needsElementAccess) {
writePunctuation(writer, SyntaxKind.OpenBracketToken);
if (isSingleOrDoubleQuote(firstChar)) {
writer.writeStringLiteral(symbolName);
}
else {
writer.writeSymbol(symbolName, symbol);
}
writePunctuation(writer, SyntaxKind.CloseBracketToken);
}
else {
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) {
buildDisplayForTypeArgumentsAndDelimiters(getTypeParametersOfClassOrInterface(parentSymbol),
(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 = getAccessibleSymbolChain(symbol, enclosingDeclaration, meaning, !!(flags & SymbolFormatFlags.UseOnlyExternalAliasing));
if (!accessibleSymbolChain ||
needsQualification(accessibleSymbolChain[0], enclosingDeclaration, accessibleSymbolChain.length === 1 ? meaning : getQualifiedLeftMeaning(meaning))) {
// Go up and add our parent.
const parent = getParentOfSymbol(accessibleSymbolChain ? accessibleSymbolChain[0] : symbol);
if (parent) {
walkSymbol(parent, 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, 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;
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) && isTypeAny(type)
? "any"
: (type).intrinsicName);
}
else if (type.flags & TypeFlags.TypeParameter && (type as TypeParameter).isThisType) {
if (inObjectTypeLiteral) {
writer.reportInaccessibleThisError();
}
writer.writeKeyword("this");
}
else if (getObjectFlags(type) & ObjectFlags.Reference) {
writeTypeReference(type, nextFlags);
}
else if (type.flags & TypeFlags.EnumLiteral) {
buildSymbolDisplay(getParentOfSymbol(type.symbol), writer, enclosingDeclaration, SymbolFlags.Type, SymbolFormatFlags.None, nextFlags);
writePunctuation(writer, SyntaxKind.DotToken);
appendSymbolNameOnly(type.symbol, writer);
}
else if (getObjectFlags(type) & ObjectFlags.ClassOrInterface || type.flags & (TypeFlags.Enum | 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 &&
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 (getObjectFlags(type) & (ObjectFlags.Anonymous | ObjectFlags.Mapped)) {
writeAnonymousType(type, nextFlags);
}
else if (type.flags & TypeFlags.StringOrNumberLiteral) {
writer.writeStringLiteral(literalTypeToString(type));
}
else if (type.flags & TypeFlags.Index) {
writer.writeKeyword("keyof");
writeSpace(writer);
writeType((type).type, TypeFormatFlags.InElementType);
}
else if (type.flags & TypeFlags.IndexedAccess) {
writeType((type).objectType, TypeFormatFlags.InElementType);
writePunctuation(writer, SyntaxKind.OpenBracketToken);
writeType((type).indexType, TypeFormatFlags.None);
writePunctuation(writer, SyntaxKind.CloseBracketToken);
}
else {
// Should never get here
// { ... }
writePunctuation(writer, SyntaxKind.OpenBraceToken);
writeSpace(writer);
writePunctuation(writer, SyntaxKind.DotDotDotToken);
writeSpace(writer);
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) {
writeSpace(writer);
}
writePunctuation(writer, delimiter);
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 || !isReservedMemberName(symbol.name)) {
buildSymbolDisplay(symbol, writer, enclosingDeclaration, SymbolFlags.Type, SymbolFormatFlags.None, flags);
}
if (pos < end) {
writePunctuation(writer, SyntaxKind.LessThanToken);
writeType(typeArguments[pos], TypeFormatFlags.InFirstTypeArgument);
pos++;
while (pos < end) {
writePunctuation(writer, SyntaxKind.CommaToken);
writeSpace(writer);
writeType(typeArguments[pos], TypeFormatFlags.None);
pos++;
}
writePunctuation(writer, SyntaxKind.GreaterThanToken);
}
}
function writeTypeReference(type: TypeReference, flags: TypeFormatFlags) {
const typeArguments = type.typeArguments || emptyArray;
if (type.target === globalArrayType && !(flags & TypeFormatFlags.WriteArrayAsGenericType)) {
writeType(typeArguments[0], TypeFormatFlags.InElementType);
writePunctuation(writer, SyntaxKind.OpenBracketToken);
writePunctuation(writer, SyntaxKind.CloseBracketToken);
}
else if (type.target.objectFlags & ObjectFlags.Tuple) {
writePunctuation(writer, SyntaxKind.OpenBracketToken);
writeTypeList(type.typeArguments.slice(0, getTypeReferenceArity(type)), SyntaxKind.CommaToken);
writePunctuation(writer, SyntaxKind.CloseBracketToken);
}
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 = getParentSymbolOfTypeParameter(outerTypeParameters[i]);
do {
i++;
} while (i < length && getParentSymbolOfTypeParameter(outerTypeParameters[i]) === parent);
// When type parameters are their own type arguments for the whole group (i.e. we have
// the default outer type arguments), we don't show the group.
if (!rangeEquals(outerTypeParameters, typeArguments, start, i)) {
writeSymbolTypeReference(parent, typeArguments, start, i, flags);
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) {
writePunctuation(writer, SyntaxKind.OpenParenToken);
}
if (type.flags & TypeFlags.Union) {
writeTypeList(formatUnionTypes(type.types), SyntaxKind.BarToken);
}
else {
writeTypeList(type.types, SyntaxKind.AmpersandToken);
}
if (flags & TypeFormatFlags.InElementType) {
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 && !getBaseTypeVariableOfClass(symbol) ||
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 = 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
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
if (!symbolStack) {
symbolStack = [];
}
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 && // typeof static method
forEach(symbol.declarations, declaration => getModifierFlags(declaration) & ModifierFlags.Static));
const isNonLocalFunctionSymbol = !!(symbol.flags & SymbolFlags.Function) &&
(symbol.parent || // is exported function symbol
forEach(symbol.declarations, declaration =>
declaration.parent.kind === SyntaxKind.SourceFile || declaration.parent.kind === SyntaxKind.ModuleBlock));
if (isStaticMethodSymbol || isNonLocalFunctionSymbol) {
// typeof is allowed only for static/non local functions
return !!(flags & TypeFormatFlags.UseTypeOfFunction) || // use typeof if format flags specify it
(contains(symbolStack, symbol)); // it is type of the symbol uses itself recursively
}
}
}
function writeTypeOfSymbol(type: ObjectType, typeFormatFlags?: TypeFormatFlags) {
writeKeyword(writer, SyntaxKind.TypeOfKeyword);
writeSpace(writer);
buildSymbolDisplay(type.symbol, writer, enclosingDeclaration, SymbolFlags.Value, SymbolFormatFlags.None, typeFormatFlags);
}
function writePropertyWithModifiers(prop: Symbol) {
if (isReadonlySymbol(prop)) {
writeKeyword(writer, SyntaxKind.ReadonlyKeyword);
writeSpace(writer);
}
buildSymbolDisplay(prop, writer);
if (prop.flags & SymbolFlags.Optional) {
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 (type.objectFlags & ObjectFlags.Mapped) {
if (getConstraintTypeFromMappedType(type).flags & (TypeFlags.TypeParameter | TypeFlags.Index)) {
writeMappedType(type);
return;
}
}
const resolved = resolveStructuredTypeMembers(type);
if (!resolved.properties.length && !resolved.stringIndexInfo && !resolved.numberIndexInfo) {
if (!resolved.callSignatures.length && !resolved.constructSignatures.length) {
writePunctuation(writer, SyntaxKind.OpenBraceToken);
writePunctuation(writer, SyntaxKind.CloseBraceToken);
return;
}
if (resolved.callSignatures.length === 1 && !resolved.constructSignatures.length) {
const parenthesizeSignature = shouldAddParenthesisAroundFunctionType(resolved.callSignatures[0], flags);
if (parenthesizeSignature) {
writePunctuation(writer, SyntaxKind.OpenParenToken);
}
buildSignatureDisplay(resolved.callSignatures[0], writer, enclosingDeclaration, globalFlagsToPass | TypeFormatFlags.WriteArrowStyleSignature, /*kind*/ undefined, symbolStack);
if (parenthesizeSignature) {
writePunctuation(writer, SyntaxKind.CloseParenToken);
}
return;
}
if (resolved.constructSignatures.length === 1 && !resolved.callSignatures.length) {
if (flags & TypeFormatFlags.InElementType) {
writePunctuation(writer, SyntaxKind.OpenParenToken);
}
writeKeyword(writer, SyntaxKind.NewKeyword);
writeSpace(writer);
buildSignatureDisplay(resolved.constructSignatures[0], writer, enclosingDeclaration, globalFlagsToPass | TypeFormatFlags.WriteArrowStyleSignature, /*kind*/ undefined, symbolStack);
if (flags & TypeFormatFlags.InElementType) {
writePunctuation(writer, SyntaxKind.CloseParenToken);
}
return;
}
}
const saveInObjectTypeLiteral = inObjectTypeLiteral;
inObjectTypeLiteral = true;
writePunctuation(writer, SyntaxKind.OpenBraceToken);
writer.writeLine();
writer.increaseIndent();
writeObjectLiteralType(resolved);
writer.decreaseIndent();
writePunctuation(writer, SyntaxKind.CloseBraceToken);
inObjectTypeLiteral = saveInObjectTypeLiteral;
}
function writeObjectLiteralType(resolved: ResolvedType) {
for (const signature of resolved.callSignatures) {
buildSignatureDisplay(signature, writer, enclosingDeclaration, globalFlagsToPass, /*kind*/ undefined, symbolStack);
writePunctuation(writer, SyntaxKind.SemicolonToken);
writer.writeLine();
}
for (const signature of resolved.constructSignatures) {
buildSignatureDisplay(signature, writer, enclosingDeclaration, globalFlagsToPass, SignatureKind.Construct, symbolStack);
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) {
const t = getTypeOfSymbol(p);
if (p.flags & (SymbolFlags.Function | SymbolFlags.Method) && !getPropertiesOfObjectType(t).length) {
const signatures = getSignaturesOfType(t, SignatureKind.Call);
for (const signature of signatures) {
writePropertyWithModifiers(p);
buildSignatureDisplay(signature, writer, enclosingDeclaration, globalFlagsToPass, /*kind*/ undefined, symbolStack);
writePunctuation(writer, SyntaxKind.SemicolonToken);
writer.writeLine();
}
}
else {
writePropertyWithModifiers(p);
writePunctuation(writer, SyntaxKind.ColonToken);
writeSpace(writer);
writeType(t, TypeFormatFlags.None);
writePunctuation(writer, SyntaxKind.SemicolonToken);
writer.writeLine();
}
}
}
function writeMappedType(type: MappedType) {
writePunctuation(writer, SyntaxKind.OpenBraceToken);
writer.writeLine();
writer.increaseIndent();
if (type.declaration.readonlyToken) {
writeKeyword(writer, SyntaxKind.ReadonlyKeyword);
writeSpace(writer);
}
writePunctuation(writer, SyntaxKind.OpenBracketToken);
appendSymbolNameOnly(getTypeParameterFromMappedType(type).symbol, writer);
writeSpace(writer);
writeKeyword(writer, SyntaxKind.InKeyword);
writeSpace(writer);
writeType(getConstraintTypeFromMappedType(type), TypeFormatFlags.None);
writePunctuation(writer, SyntaxKind.CloseBracketToken);
if (type.declaration.questionToken) {
writePunctuation(writer, SyntaxKind.QuestionToken);
}
writePunctuation(writer, SyntaxKind.ColonToken);
writeSpace(writer);
writeType(getTemplateTypeFromMappedType(type), TypeFormatFlags.None);
writePunctuation(writer, SyntaxKind.SemicolonToken);
writer.writeLine();
writer.decreaseIndent();
writePunctuation(writer, SyntaxKind.CloseBraceToken);
}
}
function buildTypeParameterDisplayFromSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags) {
const targetSymbol = getTargetSymbol(symbol);
if (targetSymbol.flags & SymbolFlags.Class || targetSymbol.flags & SymbolFlags.Interface || targetSymbol.flags & SymbolFlags.TypeAlias) {
buildDisplayForTypeParametersAndDelimiters(getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol), writer, enclosingDeclaration, flags);
}
}
function buildTypeParameterDisplay(tp: TypeParameter, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) {
appendSymbolNameOnly(tp.symbol, writer);
const constraint = getConstraintOfTypeParameter(tp);
if (constraint) {
writeSpace(writer);
writeKeyword(writer, SyntaxKind.ExtendsKeyword);
writeSpace(writer);
buildTypeDisplay(constraint, writer, enclosingDeclaration, flags, symbolStack);
}
const defaultType = getDefaultFromTypeParameter(tp);
if (defaultType) {
writeSpace(writer);
writePunctuation(writer, SyntaxKind.EqualsToken);
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) : isTransientSymbol(p) && p.isRestParameter) {
writePunctuation(writer, SyntaxKind.DotDotDotToken);
}
if (parameterNode && isBindingPattern(parameterNode.name)) {
buildBindingPatternDisplay(parameterNode.name, writer, enclosingDeclaration, flags, symbolStack);
}
else {
appendSymbolNameOnly(p, writer);
}
if (parameterNode && isOptionalParameter(parameterNode)) {
writePunctuation(writer, SyntaxKind.QuestionToken);
}
writePunctuation(writer, SyntaxKind.ColonToken);
writeSpace(writer);
let type = getTypeOfSymbol(p);
if (parameterNode && isRequiredInitializedParameter(parameterNode)) {
type = includeFalsyTypes(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) {
writePunctuation(writer, SyntaxKind.OpenBraceToken);
buildDisplayForCommaSeparatedList(bindingPattern.elements, writer, e => buildBindingElementDisplay(e, writer, enclosingDeclaration, flags, symbolStack));
writePunctuation(writer, SyntaxKind.CloseBraceToken);
}
else if (bindingPattern.kind === SyntaxKind.ArrayBindingPattern) {
writePunctuation(writer, SyntaxKind.OpenBracketToken);
const elements = bindingPattern.elements;
buildDisplayForCommaSeparatedList(elements, writer, e => buildBindingElementDisplay(e, writer, enclosingDeclaration, flags, symbolStack));
if (elements && elements.hasTrailingComma) {
writePunctuation(writer, SyntaxKind.CommaToken);
}
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));
writePunctuation(writer, SyntaxKind.ColonToken);
writeSpace(writer);
}
if (isBindingPattern(bindingElement.name)) {
buildBindingPatternDisplay(bindingElement.name, writer, enclosingDeclaration, flags, symbolStack);
}
else {
if (bindingElement.dotDotDotToken) {
writePunctuation(writer, SyntaxKind.DotDotDotToken);
}
appendSymbolNameOnly(bindingElement.symbol, writer);
}
}
function buildDisplayForTypeParametersAndDelimiters(typeParameters: TypeParameter[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) {
if (typeParameters && typeParameters.length) {
writePunctuation(writer, SyntaxKind.LessThanToken);
buildDisplayForCommaSeparatedList(typeParameters, writer, p => buildTypeParameterDisplay(p, writer, enclosingDeclaration, flags, symbolStack));
writePunctuation(writer, SyntaxKind.GreaterThanToken);
}
}
function buildDisplayForCommaSeparatedList(list: T[], writer: SymbolWriter, action: (item: T) => void) {
for (let i = 0; i < list.length; i++) {
if (i > 0) {
writePunctuation(writer, SyntaxKind.CommaToken);
writeSpace(writer);
}
action(list[i]);
}
}
function buildDisplayForTypeArgumentsAndDelimiters(typeParameters: TypeParameter[], mapper: TypeMapper, writer: SymbolWriter, enclosingDeclaration?: Node) {
if (typeParameters && typeParameters.length) {
writePunctuation(writer, SyntaxKind.LessThanToken);
let flags = TypeFormatFlags.InFirstTypeArgument;
for (let i = 0; i < typeParameters.length; i++) {
if (i > 0) {
writePunctuation(writer, SyntaxKind.CommaToken);
writeSpace(writer);
flags = TypeFormatFlags.None;
}
buildTypeDisplay(mapper(typeParameters[i]), writer, enclosingDeclaration, flags);
}
writePunctuation(writer, SyntaxKind.GreaterThanToken);
}
}
function buildDisplayForParametersAndDelimiters(thisParameter: Symbol | undefined, parameters: Symbol[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) {
writePunctuation(writer, SyntaxKind.OpenParenToken);
if (thisParameter) {
buildParameterDisplay(thisParameter, writer, enclosingDeclaration, flags, symbolStack);
}
for (let i = 0; i < parameters.length; i++) {
if (i > 0 || thisParameter) {
writePunctuation(writer, SyntaxKind.CommaToken);
writeSpace(writer);
}
buildParameterDisplay(parameters[i], writer, enclosingDeclaration, flags, symbolStack);
}
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 {
writeKeyword(writer, SyntaxKind.ThisKeyword);
}
writeSpace(writer);
writeKeyword(writer, SyntaxKind.IsKeyword);
writeSpace(writer);
buildTypeDisplay(predicate.type, writer, enclosingDeclaration, flags, symbolStack);
}
function buildReturnTypeDisplay(signature: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) {
const returnType = getReturnTypeOfSignature(signature);
if (flags & TypeFormatFlags.SuppressAnyReturnType && isTypeAny(returnType)) {
return;
}
if (flags & TypeFormatFlags.WriteArrowStyleSignature) {
writeSpace(writer);
writePunctuation(writer, SyntaxKind.EqualsGreaterThanToken);
}
else {
writePunctuation(writer, SyntaxKind.ColonToken);
}
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) {
writeKeyword(writer, SyntaxKind.NewKeyword);
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) {
writeKeyword(writer, SyntaxKind.ReadonlyKeyword);
writeSpace(writer);
}
writePunctuation(writer, SyntaxKind.OpenBracketToken);
writer.writeParameter(info.declaration ? declarationNameToString(info.declaration.parameters[0].name) : "x");
writePunctuation(writer, SyntaxKind.ColonToken);
writeSpace(writer);
switch (kind) {
case IndexKind.Number:
writeKeyword(writer, SyntaxKind.NumberKeyword);
break;
case IndexKind.String:
writeKeyword(writer, SyntaxKind.StringKeyword);
break;
}
writePunctuation(writer, SyntaxKind.CloseBracketToken);
writePunctuation(writer, SyntaxKind.ColonToken);
writeSpace(writer);
buildTypeDisplay(info.type, writer, enclosingDeclaration, globalFlags, symbolStack);
writePunctuation(writer, SyntaxKind.SemicolonToken);
writer.writeLine();
}
}
return _displayBuilder || (_displayBuilder = {
buildSymbolDisplay,
buildTypeDisplay,
buildTypeParameterDisplay,
buildTypePredicateDisplay,
buildParameterDisplay,
buildDisplayForParametersAndDelimiters,
buildDisplayForTypeParametersAndDelimiters,
buildTypeParameterDisplayFromSymbol,
buildSignatureDisplay,
buildIndexSignatureDisplay,
buildReturnTypeDisplay
});
}
function isDeclarationVisible(node: Declaration): boolean {
if (node) {
const links = getNodeLinks(node);
if (links.isVisible === undefined) {
links.isVisible = !!determineIfDeclarationIsVisible();
}
return links.isVisible;
}
return false;
function determineIfDeclarationIsVisible() {
switch (node.kind) {
case SyntaxKind.BindingElement:
return isDeclarationVisible(node.parent.parent);
case SyntaxKind.VariableDeclaration:
if (isBindingPattern((node as VariableDeclaration).name) &&
!((node as VariableDeclaration).name as BindingPattern).elements.length) {
// If the binding pattern is empty, this variable declaration is not visible
return false;
}
// falls through
case SyntaxKind.ModuleDeclaration:
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.TypeAliasDeclaration:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.EnumDeclaration:
case SyntaxKind.ImportEqualsDeclaration:
// external module augmentation is always visible
if (isExternalModuleAugmentation(node)) {
return true;
}
const parent = getDeclarationContainer(node);
// If the node is not exported or it is not ambient module element (except import declaration)
if (!(getCombinedModifierFlags(node) & ModifierFlags.Export) &&
!(node.kind !== SyntaxKind.ImportEqualsDeclaration && parent.kind !== SyntaxKind.SourceFile && isInAmbientContext(parent))) {
return isGlobalSourceFile(parent);
}
// Exported members/ambient module elements (exception import declaration) are visible if parent is visible
return isDeclarationVisible(parent);
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.PropertySignature:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
if (getModifierFlags(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 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;
}
}
}
function collectLinkedAliases(node: Identifier): Node[] {
let exportSymbol: Symbol;
if (node.parent && node.parent.kind === SyntaxKind.ExportAssignment) {
exportSymbol = resolveName(node.parent, node.text, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias, Diagnostics.Cannot_find_name_0, node);
}
else if (node.parent.kind === SyntaxKind.ExportSpecifier) {
exportSymbol = 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 => {
getNodeLinks(declaration).isVisible = true;
const resultNode = 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 = getFirstIdentifier(internalModuleReference);
const importSymbol = resolveName(declaration, firstIdentifier.text, 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
*/
function pushTypeResolution(target: TypeSystemEntity, propertyName: TypeSystemPropertyName): boolean {
const resolutionCycleStartIndex = findResolutionCycleStartIndex(target, propertyName);
if (resolutionCycleStartIndex >= 0) {
// A cycle was found
const { length } = resolutionTargets;
for (let i = resolutionCycleStartIndex; i < length; i++) {
resolutionResults[i] = false;
}
return false;
}
resolutionTargets.push(target);
resolutionResults.push(/*items*/ true);
resolutionPropertyNames.push(propertyName);
return true;
}
function findResolutionCycleStartIndex(target: TypeSystemEntity, propertyName: TypeSystemPropertyName): number {
for (let i = resolutionTargets.length - 1; i >= 0; i--) {
if (hasType(resolutionTargets[i], resolutionPropertyNames[i])) {
return -1;
}
if (resolutionTargets[i] === target && resolutionPropertyNames[i] === propertyName) {
return i;
}
}
return -1;
}
function hasType(target: TypeSystemEntity, propertyName: TypeSystemPropertyName): Type {
if (propertyName === TypeSystemPropertyName.Type) {
return getSymbolLinks(target).type;
}
if (propertyName === TypeSystemPropertyName.DeclaredType) {
return 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.
function popTypeResolution(): boolean {
resolutionTargets.pop();
resolutionPropertyNames.pop();
return resolutionResults.pop();
}
function 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;
}
function getTypeOfPrototypeProperty(prototype: Symbol): Type {
// TypeScript 1.0 spec (April 2014): 8.4
// Every class automatically contains a static property member named 'prototype',
// the type of which is an instantiation of the class type with type Any supplied as a type argument for each type parameter.
// It is an error to explicitly declare a static property member with the name 'prototype'.
const classType = getDeclaredTypeOfSymbol(getParentOfSymbol(prototype));
return classType.typeParameters ? createTypeReference(classType, map(classType.typeParameters, _ => anyType)) : classType;
}
// Return the type of the given property in the given type, or undefined if no such property exists
function getTypeOfPropertyOfType(type: Type, name: string): Type {
const prop = getPropertyOfType(type, name);
return prop ? getTypeOfSymbol(prop) : undefined;
}
function 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.
function getTypeForBindingElementParent(node: VariableLikeDeclaration) {
const symbol = getSymbolOfNode(node);
return symbol && getSymbolLinks(symbol).type || getTypeForVariableLikeDeclaration(node, /*includeOptionality*/ false);
}
function isComputedNonLiteralName(name: PropertyName): boolean {
return name.kind === SyntaxKind.ComputedPropertyName && !isStringOrNumericLiteral((name).expression);
}
function getRestType(source: Type, properties: PropertyName[], symbol: Symbol): Type {
source = filterType(source, t => !(t.flags & TypeFlags.Nullable));
if (source.flags & TypeFlags.Never) {
return emptyObjectType;
}
if (source.flags & TypeFlags.Union) {
return mapType(source, t => getRestType(t, properties, symbol));
}
const members = createMap();
const names = createMap();
for (const name of properties) {
names.set(getTextOfPropertyName(name), true);
}
for (const prop of getPropertiesOfType(source)) {
const inNamesToRemove = names.has(prop.name);
const isPrivate = getDeclarationModifierFlagsFromSymbol(prop) & (ModifierFlags.Private | ModifierFlags.Protected);
const isSetOnlyAccessor = prop.flags & SymbolFlags.SetAccessor && !(prop.flags & SymbolFlags.GetAccessor);
if (!inNamesToRemove && !isPrivate && !isClassMethod(prop) && !isSetOnlyAccessor) {
members.set(prop.name, prop);
}
}
const stringIndexInfo = getIndexInfoOfType(source, IndexKind.String);
const numberIndexInfo = getIndexInfoOfType(source, IndexKind.Number);
return createAnonymousType(symbol, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo);
}
/** Return the inferred type for a binding element */
function getTypeForBindingElement(declaration: BindingElement): Type {
const pattern = declaration.parent;
const parentType = getTypeForBindingElementParent(pattern.parent);
// If parent has the unknown (error) type, then so does this binding element
if (parentType === unknownType) {
return 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 || isTypeAny(parentType)) {
if (declaration.initializer) {
return checkDeclarationInitializer(declaration);
}
return parentType;
}
let type: Type;
if (pattern.kind === SyntaxKind.ObjectBindingPattern) {
if (declaration.dotDotDotToken) {
if (!isValidSpreadType(parentType)) {
error(declaration, Diagnostics.Rest_types_may_only_be_created_from_object_types);
return unknownType;
}
const literalMembers: PropertyName[] = [];
for (const element of pattern.elements) {
if (!(element as BindingElement).dotDotDotToken) {
literalMembers.push(element.propertyName || element.name as Identifier);
}
}
type = getRestType(parentType, literalMembers, declaration.symbol);
}
else {
// Use explicitly specified property name ({ p: xxx } form), or otherwise the implied name ({ p } form)
const name = declaration.propertyName || declaration.name;
if (isComputedNonLiteralName(name)) {
// computed properties with non-literal names are treated as 'any'
return anyType;
}
if (declaration.initializer) {
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);
type = getTypeOfPropertyOfType(parentType, text) ||
isNumericLiteralName(text) && getIndexTypeOfType(parentType, IndexKind.Number) ||
getIndexTypeOfType(parentType, IndexKind.String);
if (!type) {
error(name, Diagnostics.Type_0_has_no_property_1_and_no_string_index_signature, typeToString(parentType), declarationNameToString(name));
return 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 = checkIteratedTypeOrElementType(parentType, pattern, /*allowStringInput*/ false, /*allowAsyncIterable*/ false);
if (declaration.dotDotDotToken) {
// Rest element has an array type with the same element type as the parent type
type = 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 = isTupleLikeType(parentType)
? getTypeOfPropertyOfType(parentType, propName)
: elementType;
if (!type) {
if (isTupleType(parentType)) {
error(declaration, Diagnostics.Tuple_type_0_with_length_1_cannot_be_assigned_to_tuple_with_length_2, typeToString(parentType), getTypeReferenceArity(parentType), pattern.elements.length);
}
else {
error(declaration, Diagnostics.Type_0_has_no_property_1, typeToString(parentType), propName);
}
return unknownType;
}
}
}
// In strict null checking mode, if a default value of a non-undefined type is specified, remove
// undefined from the final type.
if (strictNullChecks && declaration.initializer && !(getFalsyFlags(checkExpressionCached(declaration.initializer)) & TypeFlags.Undefined)) {
type = getTypeWithFacts(type, TypeFacts.NEUndefined);
}
return declaration.initializer ?
getUnionType([type, checkExpressionCached(declaration.initializer)], /*subtypeReduction*/ true) :
type;
}
function getTypeForDeclarationFromJSDocComment(declaration: Node) {
const jsdocType = getJSDocType(declaration);
if (jsdocType) {
return getTypeFromTypeNode(jsdocType);
}
return undefined;
}
function isNullOrUndefined(node: Expression) {
const expr = skipParentheses(node);
return expr.kind === SyntaxKind.NullKeyword || expr.kind === SyntaxKind.Identifier && getResolvedSymbol(expr) === undefinedSymbol;
}
function isEmptyArrayLiteral(node: Expression) {
const expr = skipParentheses(node);
return expr.kind === SyntaxKind.ArrayLiteralExpression && (expr).elements.length === 0;
}
function addOptionality(type: Type, optional: boolean): Type {
return strictNullChecks && optional ? includeFalsyTypes(type, TypeFlags.Undefined) : type;
}
// Return the inferred type for a variable, parameter, or property declaration
function getTypeForVariableLikeDeclaration(declaration: VariableLikeDeclaration, includeOptionality: boolean): Type {
if (declaration.flags & NodeFlags.JavaScriptFile) {
// If this is a variable in a JavaScript file, then use the JSDoc type (if it has
// one as its type), otherwise fallback to the below standard TS codepaths to
// try to figure it out.
const type = getTypeForDeclarationFromJSDocComment(declaration);
if (type && type !== unknownType) {
return 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 = getIndexType(checkNonNullExpression((declaration.parent.parent).expression));
return indexType.flags & (TypeFlags.TypeParameter | TypeFlags.Index) ? indexType : 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 checkRightHandSideOfForOf(forOfStatement.expression, forOfStatement.awaitModifier) || anyType;
}
if (isBindingPattern(declaration.parent)) {
return getTypeForBindingElement(declaration);
}
// Use type from type annotation if one is present
if (declaration.type) {
const declaredType = getTypeFromTypeNode(declaration.type);
return addOptionality(declaredType, /*optional*/ declaration.questionToken && includeOptionality);
}
if ((noImplicitAny || declaration.flags & NodeFlags.JavaScriptFile) &&
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 || isNullOrUndefined(declaration.initializer))) {
return autoType;
}
// Use control flow tracked 'any[]' type for non-ambient, non-exported variables with an empty array
// literal initializer.
if (declaration.initializer && isEmptyArrayLiteral(declaration.initializer)) {
return autoArrayType;
}
}
if (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 = getSignatureFromDeclaration(getter);
const thisParameter = getAccessorThisParameter(func as AccessorDeclaration);
if (thisParameter && declaration === thisParameter) {
// Use the type from the *getter*
Debug.assert(!thisParameter.type);
return getTypeOfSymbol(getterSignature.thisParameter);
}
return getReturnTypeOfSignature(getterSignature);
}
}
// Use contextual parameter type if one is available
let type: Type;
if (declaration.symbol.name === "this") {
type = getContextualThisParameterType(func);
}
else {
type = getContextuallyTypedParameterType(declaration);
}
if (type) {
return addOptionality(type, /*optional*/ declaration.questionToken && includeOptionality);
}
}
// Use the type of the initializer expression if one is present
if (declaration.initializer) {
const type = checkDeclarationInitializer(declaration);
return 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 trueType;
}
// If it is a short-hand property assignment, use the type of the identifier
if (declaration.kind === SyntaxKind.ShorthandPropertyAssignment) {
return checkIdentifier(declaration.name);
}
// If the declaration specifies a binding pattern, use the type implied by the binding pattern
if (isBindingPattern(declaration.name)) {
return getTypeFromBindingPattern(declaration.name, /*includePatternInType*/ false, /*reportErrors*/ true);
}
// No type specified and nothing can be inferred
return undefined;
}
function 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 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 = getTypeForDeclarationFromJSDocComment(expression.parent);
if (type) {
const declarationType = getWidenedType(type);
if (!jsDocType) {
jsDocType = declarationType;
}
else if (jsDocType !== unknownType && declarationType !== unknownType && !isTypeIdenticalTo(jsDocType, declarationType)) {
const name = getNameOfDeclaration(declaration);
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), typeToString(jsDocType), typeToString(declarationType));
}
}
else if (!jsDocType) {
// If we don't have an explicit JSDoc type, get the type from the expression.
types.push(getWidenedLiteralType(checkExpressionCached(expression.right)));
}
}
const type = jsDocType || getUnionType(types, /*subtypeReduction*/ true);
return getWidenedType(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.
function getTypeFromBindingElement(element: BindingElement, includePatternInType?: boolean, reportErrors?: boolean): Type {
if (element.initializer) {
return checkDeclarationInitializer(element);
}
if (isBindingPattern(element.name)) {
return getTypeFromBindingPattern(element.name, includePatternInType, reportErrors);
}
if (reportErrors && noImplicitAny && !declarationBelongsToPrivateAmbientMember(element)) {
reportImplicitAnyError(element, anyType);
}
return anyType;
}
// Return the type implied by an object binding pattern
function getTypeFromObjectBindingPattern(pattern: ObjectBindingPattern, includePatternInType: boolean, reportErrors: boolean): Type {
const members = createMap();
let stringIndexInfo: IndexInfo;
let hasComputedProperties = false;
forEach(pattern.elements, e => {
const name = e.propertyName || e.name;
if (isComputedNonLiteralName(name)) {
// do not include computed properties in the implied type
hasComputedProperties = true;
return;
}
if (e.dotDotDotToken) {
stringIndexInfo = createIndexInfo(anyType, /*isReadonly*/ false);
return;
}
const text = getTextOfPropertyName(name);
const flags = SymbolFlags.Property | (e.initializer ? SymbolFlags.Optional : 0);
const symbol = createSymbol(flags, text);
symbol.type = getTypeFromBindingElement(e, includePatternInType, reportErrors);
symbol.bindingElement = e;
members.set(symbol.name, symbol);
});
const result = 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
function getTypeFromArrayBindingPattern(pattern: BindingPattern, includePatternInType: boolean, reportErrors: boolean): Type {
const elements = pattern.elements;
const lastElement = lastOrUndefined(elements);
if (elements.length === 0 || (!isOmittedExpression(lastElement) && lastElement.dotDotDotToken)) {
return languageVersion >= ScriptTarget.ES2015 ? createIterableType(anyType) : 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) ? anyType : getTypeFromBindingElement(e, includePatternInType, reportErrors));
let result = createTupleType(elementTypes);
if (includePatternInType) {
result = 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.
function getTypeFromBindingPattern(pattern: BindingPattern, includePatternInType?: boolean, reportErrors?: boolean): Type {
return pattern.kind === SyntaxKind.ObjectBindingPattern
? getTypeFromObjectBindingPattern(pattern, includePatternInType, reportErrors)
: 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.
function getWidenedTypeForVariableLikeDeclaration(declaration: VariableLikeDeclaration, reportErrors?: boolean): Type {
let type = getTypeForVariableLikeDeclaration(declaration, /*includeOptionality*/ true);
if (type) {
if (reportErrors) {
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 getWidenedType(type);
}
// Rest parameters default to type any[], other parameters default to type any
type = declaration.dotDotDotToken ? anyArrayType : anyType;
// Report implicit any errors unless this is a private property within an ambient declaration
if (reportErrors && noImplicitAny) {
if (!declarationBelongsToPrivateAmbientMember(declaration)) {
reportImplicitAnyError(declaration, type);
}
}
return type;
}
function declarationBelongsToPrivateAmbientMember(declaration: VariableLikeDeclaration) {
const root = getRootDeclaration(declaration);
const memberDeclaration = root.kind === SyntaxKind.Parameter ? root.parent : root;
return isPrivateWithinAmbient(memberDeclaration);
}
function getTypeOfVariableOrParameterOrProperty(symbol: Symbol): Type {
const links = getSymbolLinks(symbol);
if (!links.type) {
// Handle prototype property
if (symbol.flags & SymbolFlags.Prototype) {
return links.type = getTypeOfPrototypeProperty(symbol);
}
// Handle catch clause variables
const declaration = symbol.valueDeclaration;
if (isCatchClauseVariableDeclarationOrBindingElement(declaration)) {
return links.type = anyType;
}
// Handle export default expressions
if (declaration.kind === SyntaxKind.ExportAssignment) {
return links.type = checkExpression((declaration).expression);
}
if (declaration.flags & NodeFlags.JavaScriptFile && declaration.kind === SyntaxKind.JSDocPropertyTag && (declaration).typeExpression) {
return links.type = getTypeFromTypeNode((declaration).typeExpression.type);
}
// Handle variable, parameter or property
if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) {
return 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 = getWidenedTypeFromJSSpecialPropertyDeclarations(symbol);
}
else {
type = getWidenedTypeForVariableLikeDeclaration(declaration, /*reportErrors*/ true);
}
if (!popTypeResolution()) {
type = reportCircularityError(symbol);
}
links.type = type;
}
return links.type;
}
function getAnnotatedAccessorType(accessor: AccessorDeclaration): Type {
if (accessor) {
if (accessor.kind === SyntaxKind.GetAccessor) {
return accessor.type && getTypeFromTypeNode(accessor.type);
}
else {
const setterTypeAnnotation = getSetAccessorTypeAnnotationNode(accessor);
return setterTypeAnnotation && getTypeFromTypeNode(setterTypeAnnotation);
}
}
return undefined;
}
function getAnnotatedAccessorThisParameter(accessor: AccessorDeclaration): Symbol | undefined {
const parameter = getAccessorThisParameter(accessor);
return parameter && parameter.symbol;
}
function getThisTypeOfDeclaration(declaration: SignatureDeclaration): Type | undefined {
return getThisTypeOfSignature(getSignatureFromDeclaration(declaration));
}
function getTypeOfAccessors(symbol: Symbol): Type {
const links = getSymbolLinks(symbol);
if (!links.type) {
const getter = getDeclarationOfKind(symbol, SyntaxKind.GetAccessor);
const setter = getDeclarationOfKind(symbol, SyntaxKind.SetAccessor);
if (getter && getter.flags & NodeFlags.JavaScriptFile) {
const jsDocType = getTypeForDeclarationFromJSDocComment(getter);
if (jsDocType) {
return links.type = jsDocType;
}
}
if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) {
return unknownType;
}
let type: Type;
// First try to see if the user specified a return type on the get-accessor.
const getterReturnType = 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 = 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 = getReturnTypeFromBody(getter);
}
// Otherwise, fall back to 'any'.
else {
if (noImplicitAny) {
if (setter) {
error(setter, Diagnostics.Property_0_implicitly_has_type_any_because_its_set_accessor_lacks_a_parameter_type_annotation, symbolToString(symbol));
}
else {
Debug.assert(!!getter, "there must existed getter as we are current checking either setter or getter in this function");
error(getter, Diagnostics.Property_0_implicitly_has_type_any_because_its_get_accessor_lacks_a_return_type_annotation, symbolToString(symbol));
}
}
type = anyType;
}
}
}
if (!popTypeResolution()) {
type = anyType;
if (noImplicitAny) {
const getter = getDeclarationOfKind(symbol, SyntaxKind.GetAccessor);
error(getter, Diagnostics._0_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions, symbolToString(symbol));
}
}
links.type = type;
}
return links.type;
}
function getBaseTypeVariableOfClass(symbol: Symbol) {
const baseConstructorType = getBaseConstructorTypeOfClass(getDeclaredTypeOfClassOrInterface(symbol));
return baseConstructorType.flags & TypeFlags.TypeVariable ? baseConstructorType : undefined;
}
function getTypeOfFuncClassEnumModule(symbol: Symbol): Type {
const links = getSymbolLinks(symbol);
if (!links.type) {
if (symbol.flags & SymbolFlags.Module && isShorthandAmbientModuleSymbol(symbol)) {
links.type = anyType;
}
else {
const type = createObjectType(ObjectFlags.Anonymous, symbol);
if (symbol.flags & SymbolFlags.Class) {
const baseTypeVariable = getBaseTypeVariableOfClass(symbol);
links.type = baseTypeVariable ? getIntersectionType([type, baseTypeVariable]) : type;
}
else {
links.type = strictNullChecks && symbol.flags & SymbolFlags.Optional ? includeFalsyTypes(type, TypeFlags.Undefined) : type;
}
}
}
return links.type;
}
function getTypeOfEnumMember(symbol: Symbol): Type {
const links = getSymbolLinks(symbol);
if (!links.type) {
links.type = getDeclaredTypeOfEnumMember(symbol);
}
return links.type;
}
function getTypeOfAlias(symbol: Symbol): Type {
const links = getSymbolLinks(symbol);
if (!links.type) {
const targetSymbol = 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
? getTypeOfSymbol(targetSymbol)
: unknownType;
}
return links.type;
}
function getTypeOfInstantiatedSymbol(symbol: Symbol): Type {
const links = getSymbolLinks(symbol);
if (!links.type) {
if (symbolInstantiationDepth === 100) {
error(symbol.valueDeclaration, Diagnostics.Generic_type_instantiation_is_excessively_deep_and_possibly_infinite);
links.type = unknownType;
}
else {
if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) {
return unknownType;
}
symbolInstantiationDepth++;
let type = instantiateType(getTypeOfSymbol(links.target), links.mapper);
symbolInstantiationDepth--;
if (!popTypeResolution()) {
type = reportCircularityError(symbol);
}
links.type = type;
}
}
return links.type;
}
function reportCircularityError(symbol: Symbol) {
// Check if variable has type annotation that circularly references the variable itself
if ((symbol.valueDeclaration).type) {
error(symbol.valueDeclaration, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_type_annotation,
symbolToString(symbol));
return unknownType;
}
// Otherwise variable has initializer that circularly references the variable itself
if (noImplicitAny) {
error(symbol.valueDeclaration, Diagnostics._0_implicitly_has_type_any_because_it_does_not_have_a_type_annotation_and_is_referenced_directly_or_indirectly_in_its_own_initializer,
symbolToString(symbol));
}
return anyType;
}
function getTypeOfSymbol(symbol: Symbol): Type {
if (getCheckFlags(symbol) & CheckFlags.Instantiated) {
return getTypeOfInstantiatedSymbol(symbol);
}
if (symbol.flags & (SymbolFlags.Variable | SymbolFlags.Property)) {
return getTypeOfVariableOrParameterOrProperty(symbol);
}
if (symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.Enum | SymbolFlags.ValueModule)) {
return getTypeOfFuncClassEnumModule(symbol);
}
if (symbol.flags & SymbolFlags.EnumMember) {
return getTypeOfEnumMember(symbol);
}
if (symbol.flags & SymbolFlags.Accessor) {
return getTypeOfAccessors(symbol);
}
if (symbol.flags & SymbolFlags.Alias) {
return getTypeOfAlias(symbol);
}
return unknownType;
}
function isReferenceToType(type: Type, target: Type) {
return type !== undefined
&& target !== undefined
&& (getObjectFlags(type) & ObjectFlags.Reference) !== 0
&& (type).target === target;
}
function getTargetType(type: Type): Type {
return getObjectFlags(type) & ObjectFlags.Reference ? (type).target : type;
}
function hasBaseType(type: Type, checkBase: Type) {
return check(type);
function check(type: Type): boolean {
if (getObjectFlags(type) & (ObjectFlags.ClassOrInterface | ObjectFlags.Reference)) {
const target = getTargetType(type);
return target === checkBase || forEach(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.
function appendTypeParameters(typeParameters: TypeParameter[], declarations: TypeParameterDeclaration[]): TypeParameter[] {
for (const declaration of declarations) {
const tp = getDeclaredTypeOfTypeParameter(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.
function 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 appendTypeParameters(appendOuterTypeParameters(typeParameters, node), declarations);
}
}
}
}
// The outer type parameters are those defined by enclosing generic classes, methods, or functions.
function getOuterTypeParametersOfClassOrInterface(symbol: Symbol): TypeParameter[] {
const declaration = symbol.flags & SymbolFlags.Class ? symbol.valueDeclaration : getDeclarationOfKind(symbol, SyntaxKind.InterfaceDeclaration);
return 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.
function 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 = 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.
function getTypeParametersOfClassOrInterface(symbol: Symbol): TypeParameter[] {
return concatenate(getOuterTypeParametersOfClassOrInterface(symbol), getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol));
}
// A type is a mixin constructor if it has a single construct signature taking no type parameters and a single
// rest parameter of type any[].
function isMixinConstructorType(type: Type) {
const signatures = getSignaturesOfType(type, SignatureKind.Construct);
if (signatures.length === 1) {
const s = signatures[0];
return !s.typeParameters && s.parameters.length === 1 && s.hasRestParameter && getTypeOfParameter(s.parameters[0]) === anyArrayType;
}
return false;
}
function isConstructorType(type: Type): boolean {
if (isValidBaseType(type) && getSignaturesOfType(type, SignatureKind.Construct).length > 0) {
return true;
}
if (type.flags & TypeFlags.TypeVariable) {
const constraint = getBaseConstraintOfType(type);
return constraint && isValidBaseType(constraint) && isMixinConstructorType(constraint);
}
return false;
}
function getBaseTypeNodeOfClass(type: InterfaceType): ExpressionWithTypeArguments {
return getClassExtendsHeritageClauseElement(type.symbol.valueDeclaration);
}
function getConstructorsForTypeArguments(type: Type, typeArgumentNodes: TypeNode[], location: Node): Signature[] {
const typeArgCount = length(typeArgumentNodes);
const isJavaScript = isInJavaScriptFile(location);
return filter(getSignaturesOfType(type, SignatureKind.Construct),
sig => (isJavaScript || typeArgCount >= getMinTypeArgumentCount(sig.typeParameters)) && typeArgCount <= length(sig.typeParameters));
}
function getInstantiatedConstructorsForTypeArguments(type: Type, typeArgumentNodes: TypeNode[], location: Node): Signature[] {
let signatures = getConstructorsForTypeArguments(type, typeArgumentNodes, location);
if (typeArgumentNodes) {
const typeArguments = map(typeArgumentNodes, getTypeFromTypeNode);
signatures = map(signatures, sig => getSignatureInstantiation(sig, typeArguments));
}
return signatures;
}
/**
* 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.
*/
function getBaseConstructorTypeOfClass(type: InterfaceType): Type {
if (!type.resolvedBaseConstructorType) {
const baseTypeNode = getBaseTypeNodeOfClass(type);
if (!baseTypeNode) {
return type.resolvedBaseConstructorType = undefinedType;
}
if (!pushTypeResolution(type, TypeSystemPropertyName.ResolvedBaseConstructorType)) {
return unknownType;
}
const baseConstructorType = 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.
resolveStructuredTypeMembers(baseConstructorType);
}
if (!popTypeResolution()) {
error(type.symbol.valueDeclaration, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_base_expression, symbolToString(type.symbol));
return type.resolvedBaseConstructorType = unknownType;
}
if (!(baseConstructorType.flags & TypeFlags.Any) && baseConstructorType !== nullWideningType && !isConstructorType(baseConstructorType)) {
error(baseTypeNode.expression, Diagnostics.Type_0_is_not_a_constructor_function_type, typeToString(baseConstructorType));
return type.resolvedBaseConstructorType = unknownType;
}
type.resolvedBaseConstructorType = baseConstructorType;
}
return type.resolvedBaseConstructorType;
}
function getBaseTypes(type: InterfaceType): BaseType[] {
if (!type.resolvedBaseTypes) {
if (type.objectFlags & ObjectFlags.Tuple) {
type.resolvedBaseTypes = [createArrayType(getUnionType(type.typeParameters))];
}
else if (type.symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) {
if (type.symbol.flags & SymbolFlags.Class) {
resolveBaseTypesOfClass(type);
}
if (type.symbol.flags & SymbolFlags.Interface) {
resolveBaseTypesOfInterface(type);
}
}
else {
Debug.fail("type must be class or interface");
}
}
return type.resolvedBaseTypes;
}
function resolveBaseTypesOfClass(type: InterfaceType): void {
type.resolvedBaseTypes = type.resolvedBaseTypes || emptyArray;
const baseConstructorType = getApparentType(getBaseConstructorTypeOfClass(type));
if (!(baseConstructorType.flags & (TypeFlags.Object | TypeFlags.Intersection | TypeFlags.Any))) {
return;
}
const baseTypeNode = getBaseTypeNodeOfClass(type);
let baseType: Type;
const originalBaseType = baseConstructorType && baseConstructorType.symbol ? getDeclaredTypeOfSymbol(baseConstructorType.symbol) : undefined;
if (baseConstructorType.symbol && baseConstructorType.symbol.flags & SymbolFlags.Class &&
areAllOuterTypeParametersApplied(originalBaseType)) {
// When base constructor type is a class with no captured type arguments we know that the constructors all have the same type parameters as the
// class and all return the instance type of the class. There is no need for further checks and we can apply the
// type arguments in the same manner as a type reference to get the same error reporting experience.
baseType = getTypeFromClassOrInterfaceReference(baseTypeNode, baseConstructorType.symbol, typeArgumentsFromTypeReferenceNode(baseTypeNode));
}
else if (baseConstructorType.flags & TypeFlags.Any) {
baseType = baseConstructorType;
}
else {
// The class derives from a "class-like" constructor function, check that we have at least one construct signature
// with a matching number of type parameters and use the return type of the first instantiated signature. Elsewhere
// we check that all instantiated signatures return the same type.
const constructors = getInstantiatedConstructorsForTypeArguments(baseConstructorType, baseTypeNode.typeArguments, baseTypeNode);
if (!constructors.length) {
error(baseTypeNode.expression, Diagnostics.No_base_constructor_has_the_specified_number_of_type_arguments);
return;
}
baseType = 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 = getTypeFromTypeNode(augTag.typeExpression.type);
}
}
if (baseType === unknownType) {
return;
}
if (!isValidBaseType(baseType)) {
error(baseTypeNode.expression, Diagnostics.Base_constructor_return_type_0_is_not_a_class_or_interface_type, typeToString(baseType));
return;
}
if (type === baseType || hasBaseType(baseType, type)) {
error(valueDecl, Diagnostics.Type_0_recursively_references_itself_as_a_base_type,
typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType));
return;
}
if (type.resolvedBaseTypes === emptyArray) {
type.resolvedBaseTypes = [baseType];
}
else {
type.resolvedBaseTypes.push(baseType);
}
}
function 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.
function isValidBaseType(type: Type): boolean {
return type.flags & (TypeFlags.Object | TypeFlags.NonPrimitive | TypeFlags.Any) && !isGenericMappedType(type) ||
type.flags & TypeFlags.Intersection && !forEach((type).types, t => !isValidBaseType(t));
}
function 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 = getTypeFromTypeNode(node);
if (baseType !== unknownType) {
if (isValidBaseType(baseType)) {
if (type !== baseType && !hasBaseType(baseType, type)) {
if (type.resolvedBaseTypes === emptyArray) {
type.resolvedBaseTypes = [baseType];
}
else {
type.resolvedBaseTypes.push(baseType);
}
}
else {
error(declaration, Diagnostics.Type_0_recursively_references_itself_as_a_base_type, typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType));
}
}
else {
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.
function 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 = resolveEntityName(node.expression, SymbolFlags.Type, /*ignoreErrors*/ true);
if (!baseSymbol || !(baseSymbol.flags & SymbolFlags.Interface) || getDeclaredTypeOfClassOrInterface(baseSymbol).thisType) {
return false;
}
}
}
}
}
}
return true;
}
function getDeclaredTypeOfClassOrInterface(symbol: Symbol): InterfaceType {
const links = getSymbolLinks(symbol);
if (!links.declaredType) {
const kind = symbol.flags & SymbolFlags.Class ? ObjectFlags.Class : ObjectFlags.Interface;
const type = links.declaredType = createObjectType(kind, symbol);
const outerTypeParameters = getOuterTypeParametersOfClassOrInterface(symbol);
const localTypeParameters = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol);
// A class or interface is generic if it has type parameters or a "this" type. We always give classes a "this" type
// because it is not feasible to analyze all members to determine if the "this" type escapes the class (in particular,
// property types inferred from initializers and method return types inferred from return statements are very hard
// to exhaustively analyze). We give interfaces a "this" type if we can't definitely determine that they are free of
// "this" references.
if (outerTypeParameters || localTypeParameters || kind === ObjectFlags.Class || !isIndependentInterface(symbol)) {
type.objectFlags |= ObjectFlags.Reference;
type.typeParameters = concatenate(outerTypeParameters, localTypeParameters);
type.outerTypeParameters = outerTypeParameters;
type.localTypeParameters = localTypeParameters;
(type).instantiations = createMap();
(type).instantiations.set(getTypeListId(type.typeParameters), type);
(type).target = type;
(type).typeArguments = type.typeParameters;
type.thisType = createType(TypeFlags.TypeParameter);
type.thisType.isThisType = true;
type.thisType.symbol = symbol;
type.thisType.constraint = type;
}
}
return links.declaredType;
}
function getDeclaredTypeOfTypeAlias(symbol: Symbol): Type {
const links = getSymbolLinks(symbol);
if (!links.declaredType) {
// Note that we use the links object as the target here because the symbol object is used as the unique
// identity for resolution of the 'type' property in SymbolLinks.
if (!pushTypeResolution(symbol, TypeSystemPropertyName.DeclaredType)) {
return unknownType;
}
let declaration: JSDocTypedefTag | TypeAliasDeclaration = getDeclarationOfKind(symbol, SyntaxKind.JSDocTypedefTag);
let type: Type;
if (declaration) {
if (declaration.jsDocTypeLiteral) {
type = getTypeFromTypeNode(declaration.jsDocTypeLiteral);
}
else {
type = getTypeFromTypeNode(declaration.typeExpression.type);
}
}
else {
declaration = getDeclarationOfKind(symbol, SyntaxKind.TypeAliasDeclaration);
type = getTypeFromTypeNode(declaration.type);
}
if (popTypeResolution()) {
const typeParameters = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol);
if (typeParameters) {
// Initialize the instantiation cache for generic type aliases. The declared type corresponds to
// an instantiation of the type alias with the type parameters supplied as type arguments.
links.typeParameters = typeParameters;
links.instantiations = createMap();
links.instantiations.set(getTypeListId(typeParameters), type);
}
}
else {
type = unknownType;
error(declaration.name, Diagnostics.Type_alias_0_circularly_references_itself, symbolToString(symbol));
}
links.declaredType = type;
}
return links.declaredType;
}
function isLiteralEnumMember(symbol: Symbol, member: EnumMember) {
const expr = member.initializer;
if (!expr) {
return !isInAmbientContext(member);
}
return expr.kind === SyntaxKind.NumericLiteral ||
expr.kind === SyntaxKind.PrefixUnaryExpression && (expr).operator === SyntaxKind.MinusToken &&
(expr).operand.kind === SyntaxKind.NumericLiteral ||
expr.kind === SyntaxKind.Identifier && !!symbol.exports.get((expr).text);
}
function enumHasLiteralMembers(symbol: Symbol) {
for (const declaration of symbol.declarations) {
if (declaration.kind === SyntaxKind.EnumDeclaration) {
for (const member of (declaration).members) {
if (!isLiteralEnumMember(symbol, member)) {
return false;
}
}
}
}
return true;
}
function createEnumLiteralType(symbol: Symbol, baseType: EnumType, text: string) {
const type = createType(TypeFlags.EnumLiteral);
type.symbol = symbol;
type.baseType = baseType;
type.text = text;
return type;
}
function getDeclaredTypeOfEnum(symbol: Symbol): Type {
const links = getSymbolLinks(symbol);
if (!links.declaredType) {
const enumType = links.declaredType = createType(TypeFlags.Enum);
enumType.symbol = symbol;
if (enumHasLiteralMembers(symbol)) {
const memberTypeList: Type[] = [];
const memberTypes: EnumLiteralType[] = [];
for (const declaration of enumType.symbol.declarations) {
if (declaration.kind === SyntaxKind.EnumDeclaration) {
computeEnumMemberValues(declaration);
for (const member of (declaration).members) {
const memberSymbol = getSymbolOfNode(member);
const value = getEnumMemberValue(member);
if (!memberTypes[value]) {
const memberType = memberTypes[value] = createEnumLiteralType(memberSymbol, enumType, "" + value);
memberTypeList.push(memberType);
}
}
}
}
enumType.memberTypes = memberTypes;
if (memberTypeList.length > 1) {
enumType.flags |= TypeFlags.Union;
(enumType).types = memberTypeList;
unionTypes.set(getTypeListId(memberTypeList), enumType);
}
}
}
return links.declaredType;
}
function getDeclaredTypeOfEnumMember(symbol: Symbol): Type {
const links = getSymbolLinks(symbol);
if (!links.declaredType) {
const enumType = getDeclaredTypeOfEnum(getParentOfSymbol(symbol));
links.declaredType = enumType.flags & TypeFlags.Union ?
enumType.memberTypes[getEnumMemberValue(symbol.valueDeclaration)] :
enumType;
}
return links.declaredType;
}
function getDeclaredTypeOfTypeParameter(symbol: Symbol): TypeParameter {
const links = getSymbolLinks(symbol);
if (!links.declaredType) {
const type = createType(TypeFlags.TypeParameter);
type.symbol = symbol;
links.declaredType = type;
}
return links.declaredType;
}
function getDeclaredTypeOfAlias(symbol: Symbol): Type {
const links = getSymbolLinks(symbol);
if (!links.declaredType) {
links.declaredType = getDeclaredTypeOfSymbol(resolveAlias(symbol));
}
return links.declaredType;
}
function getDeclaredTypeOfSymbol(symbol: Symbol): Type {
if (symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) {
return getDeclaredTypeOfClassOrInterface(symbol);
}
if (symbol.flags & SymbolFlags.TypeAlias) {
return getDeclaredTypeOfTypeAlias(symbol);
}
if (symbol.flags & SymbolFlags.TypeParameter) {
return getDeclaredTypeOfTypeParameter(symbol);
}
if (symbol.flags & SymbolFlags.Enum) {
return getDeclaredTypeOfEnum(symbol);
}
if (symbol.flags & SymbolFlags.EnumMember) {
return getDeclaredTypeOfEnumMember(symbol);
}
if (symbol.flags & SymbolFlags.Alias) {
return getDeclaredTypeOfAlias(symbol);
}
return unknownType;
}
// A type reference is considered independent if each type argument is considered independent.
function isIndependentTypeReference(node: TypeReferenceNode): boolean {
if (node.typeArguments) {
for (const typeNode of node.typeArguments) {
if (!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.
function 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 isIndependentType((node).elementType);
case SyntaxKind.TypeReference:
return 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).
function isIndependentVariableLikeDeclaration(node: VariableLikeDeclaration): boolean {
return node.type && isIndependentType(node.type) || !node.type && !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.
function isIndependentFunctionLikeDeclaration(node: FunctionLikeDeclaration): boolean {
if (node.kind !== SyntaxKind.Constructor && (!node.type || !isIndependentType(node.type))) {
return false;
}
for (const parameter of node.parameters) {
if (!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.
function 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 isIndependentVariableLikeDeclaration(declaration);
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
case SyntaxKind.Constructor:
return isIndependentFunctionLikeDeclaration(declaration);
}
}
}
return false;
}
function createSymbolTable(symbols: Symbol[]): SymbolTable {
const result = createMap();
for (const symbol of symbols) {
result.set(symbol.name, symbol);
}
return result;
}
// The mappingThisOnly flag indicates that the only type parameter being mapped is "this". When the flag is true,
// we check symbols to see if we can quickly conclude they are free of "this" references, thus needing no instantiation.
function createInstantiatedSymbolTable(symbols: Symbol[], mapper: TypeMapper, mappingThisOnly: boolean): SymbolTable {
const result = createMap();
for (const symbol of symbols) {
result.set(symbol.name, mappingThisOnly && isIndependentMember(symbol) ? symbol : instantiateSymbol(symbol, mapper));
}
return result;
}
function addInheritedMembers(symbols: SymbolTable, baseSymbols: Symbol[]) {
for (const s of baseSymbols) {
if (!symbols.has(s.name)) {
symbols.set(s.name, s);
}
}
}
function resolveDeclaredMembers(type: InterfaceType): InterfaceTypeWithDeclaredMembers {
if (!(type).declaredProperties) {
const symbol = type.symbol;
(type).declaredProperties = getNamedMembers(symbol.members);
(type).declaredCallSignatures = getSignaturesOfSymbol(symbol.members.get("__call"));
(type).declaredConstructSignatures = getSignaturesOfSymbol(symbol.members.get("__new"));
(type).declaredStringIndexInfo = getIndexInfoOfSymbol(symbol, IndexKind.String);
(type).declaredNumberIndexInfo = getIndexInfoOfSymbol(symbol, IndexKind.Number);
}
return type;
}
function getTypeWithThisArgument(type: Type, thisArgument?: Type): Type {
if (getObjectFlags(type) & ObjectFlags.Reference) {
const target = (type).target;
const typeArguments = (type).typeArguments;
if (length(target.typeParameters) === length(typeArguments)) {
return createTypeReference(target, concatenate(typeArguments, [thisArgument || target.thisType]));
}
}
else if (type.flags & TypeFlags.Intersection) {
return getIntersectionType(map((type).types, t => getTypeWithThisArgument(t, thisArgument)));
}
return type;
}
function 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 = identityMapper;
members = source.symbol ? source.symbol.members : createSymbolTable(source.declaredProperties);
callSignatures = source.declaredCallSignatures;
constructSignatures = source.declaredConstructSignatures;
stringIndexInfo = source.declaredStringIndexInfo;
numberIndexInfo = source.declaredNumberIndexInfo;
}
else {
mapper = createTypeMapper(typeParameters, typeArguments);
members = createInstantiatedSymbolTable(source.declaredProperties, mapper, /*mappingThisOnly*/ typeParameters.length === 1);
callSignatures = instantiateSignatures(source.declaredCallSignatures, mapper);
constructSignatures = instantiateSignatures(source.declaredConstructSignatures, mapper);
stringIndexInfo = instantiateIndexInfo(source.declaredStringIndexInfo, mapper);
numberIndexInfo = instantiateIndexInfo(source.declaredNumberIndexInfo, mapper);
}
const baseTypes = 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 ? getTypeWithThisArgument(instantiateType(baseType, mapper), thisArgument) : baseType;
addInheritedMembers(members, getPropertiesOfType(instantiatedBaseType));
callSignatures = concatenate(callSignatures, getSignaturesOfType(instantiatedBaseType, SignatureKind.Call));
constructSignatures = concatenate(constructSignatures, getSignaturesOfType(instantiatedBaseType, SignatureKind.Construct));
if (!stringIndexInfo) {
stringIndexInfo = instantiatedBaseType === anyType ?
createIndexInfo(anyType, /*isReadonly*/ false) :
getIndexInfoOfType(instantiatedBaseType, IndexKind.String);
}
numberIndexInfo = numberIndexInfo || getIndexInfoOfType(instantiatedBaseType, IndexKind.Number);
}
}
setStructuredTypeMembers(type, members, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo);
}
function resolveClassOrInterfaceMembers(type: InterfaceType): void {
resolveObjectTypeMembers(type, resolveDeclaredMembers(type), emptyArray, emptyArray);
}
function resolveTypeReferenceMembers(type: TypeReference): void {
const source = resolveDeclaredMembers(type.target);
const typeParameters = concatenate(source.typeParameters, [source.thisType]);
const typeArguments = type.typeArguments && type.typeArguments.length === typeParameters.length ?
type.typeArguments : concatenate(type.typeArguments, [type]);
resolveObjectTypeMembers(type, source, typeParameters, typeArguments);
}
function createSignature(declaration: SignatureDeclaration, typeParameters: TypeParameter[], thisParameter: Symbol | undefined, parameters: Symbol[],
resolvedReturnType: Type, typePredicate: TypePredicate, minArgumentCount: number, hasRestParameter: boolean, hasLiteralTypes: boolean): Signature {
const sig = new Signature(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;
}
function cloneSignature(sig: Signature): Signature {
return createSignature(sig.declaration, sig.typeParameters, sig.thisParameter, sig.parameters, sig.resolvedReturnType,
sig.typePredicate, sig.minArgumentCount, sig.hasRestParameter, sig.hasLiteralTypes);
}
function getDefaultConstructSignatures(classType: InterfaceType): Signature[] {
const baseConstructorType = getBaseConstructorTypeOfClass(classType);
const baseSignatures = getSignaturesOfType(baseConstructorType, SignatureKind.Construct);
if (baseSignatures.length === 0) {
return [createSignature(undefined, classType.localTypeParameters, undefined, emptyArray, classType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false)];
}
const baseTypeNode = getBaseTypeNodeOfClass(classType);
const isJavaScript = isInJavaScriptFile(baseTypeNode);
const typeArguments = typeArgumentsFromTypeReferenceNode(baseTypeNode);
const typeArgCount = length(typeArguments);
const result: Signature[] = [];
for (const baseSig of baseSignatures) {
const minTypeArgumentCount = getMinTypeArgumentCount(baseSig.typeParameters);
const typeParamCount = length(baseSig.typeParameters);
if ((isJavaScript || typeArgCount >= minTypeArgumentCount) && typeArgCount <= typeParamCount) {
const sig = typeParamCount ? createSignatureInstantiation(baseSig, fillMissingTypeArguments(typeArguments, baseSig.typeParameters, minTypeArgumentCount, baseTypeNode)) : cloneSignature(baseSig);
sig.typeParameters = classType.localTypeParameters;
sig.resolvedReturnType = classType;
result.push(sig);
}
}
return result;
}
function findMatchingSignature(signatureList: Signature[], signature: Signature, partialMatch: boolean, ignoreThisTypes: boolean, ignoreReturnTypes: boolean): Signature {
for (const s of signatureList) {
if (compareSignaturesIdentical(s, signature, partialMatch, ignoreThisTypes, ignoreReturnTypes, compareTypesIdentical)) {
return s;
}
}
}
function 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 (!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 : 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.
function getUnionSignatures(types: Type[], kind: SignatureKind): Signature[] {
const signatureLists = map(types, t => 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 || !findMatchingSignature(result, signature, /*partialMatch*/ false, /*ignoreThisTypes*/ true, /*ignoreReturnTypes*/ true)) {
const unionSignatures = findMatchingSignatures(signatureLists, signature, i);
if (unionSignatures) {
let s = signature;
// Union the result types when more than one signature matches
if (unionSignatures.length > 1) {
s = cloneSignature(signature);
if (forEach(unionSignatures, sig => sig.thisParameter)) {
const thisType = getUnionType(map(unionSignatures, sig => getTypeOfSymbol(sig.thisParameter) || anyType), /*subtypeReduction*/ true);
s.thisParameter = 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;
}
function getUnionIndexInfo(types: Type[], kind: IndexKind): IndexInfo {
const indexTypes: Type[] = [];
let isAnyReadonly = false;
for (const type of types) {
const indexInfo = getIndexInfoOfType(type, kind);
if (!indexInfo) {
return undefined;
}
indexTypes.push(indexInfo.type);
isAnyReadonly = isAnyReadonly || indexInfo.isReadonly;
}
return createIndexInfo(getUnionType(indexTypes, /*subtypeReduction*/ true), isAnyReadonly);
}
function resolveUnionTypeMembers(type: UnionType) {
// The members and properties collections are empty for union types. To get all properties of a union
// type use getPropertiesOfType (only the language service uses this).
const callSignatures = getUnionSignatures(type.types, SignatureKind.Call);
const constructSignatures = getUnionSignatures(type.types, SignatureKind.Construct);
const stringIndexInfo = getUnionIndexInfo(type.types, IndexKind.String);
const numberIndexInfo = getUnionIndexInfo(type.types, IndexKind.Number);
setStructuredTypeMembers(type, emptySymbols, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo);
}
function intersectTypes(type1: Type, type2: Type): Type {
return !type1 ? type2 : !type2 ? type1 : getIntersectionType([type1, type2]);
}
function intersectIndexInfos(info1: IndexInfo, info2: IndexInfo): IndexInfo {
return !info1 ? info2 : !info2 ? info1 : createIndexInfo(
getIntersectionType([info1.type, info2.type]), info1.isReadonly && info2.isReadonly);
}
function unionSpreadIndexInfos(info1: IndexInfo, info2: IndexInfo): IndexInfo {
return info1 && info2 && createIndexInfo(
getUnionType([info1.type, info2.type]), info1.isReadonly || info2.isReadonly);
}
function 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 (isMixinConstructorType(types[i])) {
mixedTypes.push(getReturnTypeOfSignature(getSignaturesOfType(types[i], SignatureKind.Construct)[0]));
}
}
return getIntersectionType(mixedTypes);
}
function resolveIntersectionTypeMembers(type: IntersectionType) {
// The members and properties collections are empty for intersection types. To get all properties of an
// intersection type use getPropertiesOfType (only the language service uses this).
let callSignatures: Signature[] = emptyArray;
let constructSignatures: Signature[] = emptyArray;
let stringIndexInfo: IndexInfo;
let numberIndexInfo: IndexInfo;
const types = type.types;
const mixinCount = countWhere(types, 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 || !isMixinConstructorType(t)) {
let signatures = getSignaturesOfType(t, SignatureKind.Construct);
if (signatures.length && mixinCount > 0) {
signatures = map(signatures, s => {
const clone = cloneSignature(s);
clone.resolvedReturnType = includeMixinType(getReturnTypeOfSignature(s), types, i);
return clone;
});
}
constructSignatures = concatenate(constructSignatures, signatures);
}
callSignatures = concatenate(callSignatures, getSignaturesOfType(t, SignatureKind.Call));
stringIndexInfo = intersectIndexInfos(stringIndexInfo, getIndexInfoOfType(t, IndexKind.String));
numberIndexInfo = intersectIndexInfos(numberIndexInfo, getIndexInfoOfType(t, IndexKind.Number));
}
setStructuredTypeMembers(type, emptySymbols, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo);
}
/**
* Converts an AnonymousType to a ResolvedType.
*/
function resolveAnonymousTypeMembers(type: AnonymousType) {
const symbol = type.symbol;
if (type.target) {
const members = createInstantiatedSymbolTable(getPropertiesOfObjectType(type.target), type.mapper, /*mappingThisOnly*/ false);
const callSignatures = instantiateSignatures(getSignaturesOfType(type.target, SignatureKind.Call), type.mapper);
const constructSignatures = instantiateSignatures(getSignaturesOfType(type.target, SignatureKind.Construct), type.mapper);
const stringIndexInfo = instantiateIndexInfo(getIndexInfoOfType(type.target, IndexKind.String), type.mapper);
const numberIndexInfo = instantiateIndexInfo(getIndexInfoOfType(type.target, IndexKind.Number), type.mapper);
setStructuredTypeMembers(type, members, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo);
}
else if (symbol.flags & SymbolFlags.TypeLiteral) {
const members = symbol.members;
const callSignatures = getSignaturesOfSymbol(members.get("__call"));
const constructSignatures = getSignaturesOfSymbol(members.get("__new"));
const stringIndexInfo = getIndexInfoOfSymbol(symbol, IndexKind.String);
const numberIndexInfo = getIndexInfoOfSymbol(symbol, IndexKind.Number);
setStructuredTypeMembers(type, members, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo);
}
else {
// Combinations of function, class, enum and module
let members = emptySymbols;
let constructSignatures: Signature[] = emptyArray;
let stringIndexInfo: IndexInfo = undefined;
if (symbol.exports) {
members = getExportsOfSymbol(symbol);
}
if (symbol.flags & SymbolFlags.Class) {
const classType = getDeclaredTypeOfClassOrInterface(symbol);
constructSignatures = getSignaturesOfSymbol(symbol.members.get("__constructor"));
if (!constructSignatures.length) {
constructSignatures = getDefaultConstructSignatures(classType);
}
const baseConstructorType = getBaseConstructorTypeOfClass(classType);
if (baseConstructorType.flags & (TypeFlags.Object | TypeFlags.Intersection | TypeFlags.TypeVariable)) {
members = createSymbolTable(getNamedMembers(members));
addInheritedMembers(members, getPropertiesOfType(baseConstructorType));
}
else if (baseConstructorType === anyType) {
stringIndexInfo = createIndexInfo(anyType, /*isReadonly*/ false);
}
}
const numberIndexInfo = symbol.flags & SymbolFlags.Enum ? enumNumberIndexInfo : undefined;
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 = getSignaturesOfSymbol(symbol);
}
}
}
/** Resolve the members of a mapped type { [P in K]: T } */
function resolveMappedTypeMembers(type: MappedType) {
const members: SymbolTable = createMap();
let stringIndexInfo: IndexInfo;
// Resolve upfront such that recursive references see an empty object type.
setStructuredTypeMembers(type, 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 = getTypeParameterFromMappedType(type);
const constraintType = getConstraintTypeFromMappedType(type);
const templateType = getTemplateTypeFromMappedType(type);
const modifiersType = getApparentType(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 getPropertiesOfType(modifiersType)) {
addMemberForKeyType(getLiteralTypeFromPropertyName(propertySymbol), propertySymbol);
}
if (getIndexInfoOfType(modifiersType, IndexKind.String)) {
addMemberForKeyType(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 ? getApparentType(constraintType) : constraintType;
const iterationType = keyType.flags & TypeFlags.Index ? getIndexType(getApparentType((keyType).type)) : keyType;
forEachType(iterationType, addMemberForKeyType);
}
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 = createTypeMapper([typeParameter], [t]);
const templateMapper = type.mapper ? combineTypeMappers(type.mapper, iterationMapper) : iterationMapper;
const propType = 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 = (t).text;
const modifiersProp = getPropertyOfType(modifiersType, propName);
const isOptional = templateOptional || !!(modifiersProp && modifiersProp.flags & SymbolFlags.Optional);
const prop = createSymbol(SymbolFlags.Property | (isOptional ? SymbolFlags.Optional : 0), propName);
prop.checkFlags = templateReadonly || modifiersProp && isReadonlySymbol(modifiersProp) ? CheckFlags.Readonly : 0;
prop.type = propType;
if (propertySymbol) {
prop.syntheticOrigin = propertySymbol;
}
members.set(propName, prop);
}
else if (t.flags & TypeFlags.String) {
stringIndexInfo = createIndexInfo(propType, templateReadonly);
}
}
}
function getTypeParameterFromMappedType(type: MappedType) {
return type.typeParameter ||
(type.typeParameter = getDeclaredTypeOfTypeParameter(getSymbolOfNode(type.declaration.typeParameter)));
}
function getConstraintTypeFromMappedType(type: MappedType) {
return type.constraintType ||
(type.constraintType = instantiateType(getConstraintOfTypeParameter(getTypeParameterFromMappedType(type)), type.mapper || identityMapper) || unknownType);
}
function getTemplateTypeFromMappedType(type: MappedType) {
return type.templateType ||
(type.templateType = type.declaration.type ?
instantiateType(addOptionality(getTypeFromTypeNode(type.declaration.type), !!type.declaration.questionToken), type.mapper || identityMapper) :
unknownType);
}
function 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 = instantiateType(getTypeFromTypeNode((constraintDeclaration).type), type.mapper || 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 = getTypeFromMappedTypeNode(type.declaration);
const constraint = getConstraintTypeFromMappedType(declaredType);
const extendedConstraint = constraint && constraint.flags & TypeFlags.TypeParameter ? getConstraintOfTypeParameter(constraint) : constraint;
type.modifiersType = extendedConstraint && extendedConstraint.flags & TypeFlags.Index ? instantiateType((extendedConstraint).type, type.mapper || identityMapper) : emptyObjectType;
}
}
return type.modifiersType;
}
function isGenericMappedType(type: Type) {
if (getObjectFlags(type) & ObjectFlags.Mapped) {
const constraintType = getConstraintTypeFromMappedType(type);
return maybeTypeOfKind(constraintType, TypeFlags.TypeVariable | TypeFlags.Index);
}
return false;
}
function resolveStructuredTypeMembers(type: StructuredType): ResolvedType {
if (!(