///
///
///
/* @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 isInstantiatedModule(node: ModuleDeclaration, preserveConstEnums: boolean) {
const moduleState = getModuleInstanceState(node);
return moduleState === ModuleInstanceState.Instantiated ||
(preserveConstEnums && moduleState === ModuleInstanceState.ConstEnumOnly);
}
export function createTypeChecker(host: TypeCheckerHost, produceDiagnostics: boolean): TypeChecker {
// 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;
// tslint:disable variable-name
const Symbol = objectAllocator.getSymbolConstructor();
const Type = objectAllocator.getTypeConstructor();
const Signature = objectAllocator.getSignatureConstructor();
// tslint:enable variable-name
let typeCount = 0;
let symbolCount = 0;
let enumCount = 0;
let symbolInstantiationDepth = 0;
const emptySymbols = createSymbolTable();
const identityMapper: (type: Type) => Type = identity;
const compilerOptions = host.getCompilerOptions();
const languageVersion = getEmitScriptTarget(compilerOptions);
const modulekind = getEmitModuleKind(compilerOptions);
const noUnusedIdentifiers = !!compilerOptions.noUnusedLocals || !!compilerOptions.noUnusedParameters;
const allowSyntheticDefaultImports = getAllowSyntheticDefaultImports(compilerOptions);
const strictNullChecks = getStrictOptionValue(compilerOptions, "strictNullChecks");
const strictFunctionTypes = getStrictOptionValue(compilerOptions, "strictFunctionTypes");
const strictPropertyInitialization = getStrictOptionValue(compilerOptions, "strictPropertyInitialization");
const noImplicitAny = getStrictOptionValue(compilerOptions, "noImplicitAny");
const noImplicitThis = getStrictOptionValue(compilerOptions, "noImplicitThis");
const emitResolver = createResolver();
const nodeBuilder = createNodeBuilder();
const undefinedSymbol = createSymbol(SymbolFlags.Property, "undefined" as __String);
undefinedSymbol.declarations = [];
const argumentsSymbol = createSymbol(SymbolFlags.Property, "arguments" as __String);
/** This will be set during calls to `getResolvedSignature` where services determines an apparent number of arguments greater than what is actually provided. */
let apparentArgumentCount: number | undefined;
// for public members that accept a Node or one of its subtypes, we must guard against
// synthetic nodes created during transformations by calling `getParseTreeNode`.
// for most of these, we perform the guard only on `checker` to avoid any possible
// extra cost of calling `getParseTreeNode` when calling these functions from inside the
// checker.
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, escapeLeadingUnderscores(parameterName));
},
getDeclaredTypeOfSymbol,
getPropertiesOfType,
getPropertyOfType: (type, name) => getPropertyOfType(type, escapeLeadingUnderscores(name)),
getIndexInfoOfType,
getSignaturesOfType,
getIndexTypeOfType,
getBaseTypes,
getBaseTypeOfLiteralType,
getWidenedType,
getTypeFromTypeNode: node => {
node = getParseTreeNode(node, isTypeNode);
return node ? getTypeFromTypeNode(node) : unknownType;
},
getParameterType: getTypeAtPosition,
getReturnTypeOfSignature,
getNullableType,
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;
},
getExportSymbolOfSymbol(symbol) {
return getMergedSymbol(symbol.exportSymbol || symbol);
},
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;
},
isContextSensitive,
getFullyQualifiedName,
getResolvedSignature: (node, candidatesOutArray, theArgumentCount) => {
node = getParseTreeNode(node, isCallLikeExpression);
apparentArgumentCount = theArgumentCount;
const res = node ? getResolvedSignature(node, candidatesOutArray) : undefined;
apparentArgumentCount = undefined;
return res;
},
getConstantValue: node => {
node = getParseTreeNode(node, canHaveConstantValue);
return node ? getConstantValue(node) : undefined;
},
isValidPropertyAccess: (node, propertyName) => {
node = getParseTreeNode(node, isPropertyAccessOrQualifiedName);
return !!node && isValidPropertyAccess(node, escapeLeadingUnderscores(propertyName));
},
isValidPropertyAccessForCompletions: (node, type, property) => {
node = getParseTreeNode(node, isPropertyAccessExpression);
return !!node && isValidPropertyAccessForCompletions(node, type, property);
},
getSignatureFromDeclaration: declaration => {
declaration = getParseTreeNode(declaration, isFunctionLike);
return declaration ? getSignatureFromDeclaration(declaration) : undefined;
},
isImplementationOfOverload: node => {
const parsed = getParseTreeNode(node, isFunctionLike);
return parsed ? isImplementationOfOverload(parsed) : 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,
getSymbolWalker: createGetSymbolWalker(
getRestTypeOfSignature,
getTypePredicateOfSignature,
getReturnTypeOfSignature,
getBaseTypes,
resolveStructuredTypeMembers,
getTypeOfSymbol,
getResolvedSymbol,
getIndexTypeOfStructuredType,
getConstraintFromTypeParameter,
getFirstIdentifier,
),
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: (name, symbol) => tryGetMemberInModuleExports(escapeLeadingUnderscores(name), symbol),
tryGetMemberInModuleExportsAndProperties: (name, symbol) => tryGetMemberInModuleExportsAndProperties(escapeLeadingUnderscores(name), symbol),
tryFindAmbientModuleWithoutAugmentations: moduleName => {
// we deliberately exclude augmentations
// since we are only interested in declarations of the module itself
return tryFindAmbientModule(moduleName, /*withAugmentations*/ false);
},
getApparentType,
getUnionType,
createAnonymousType,
createSignature,
createSymbol,
createIndexInfo,
getAnyType: () => anyType,
getStringType: () => stringType,
getNumberType: () => numberType,
createPromiseType,
createArrayType,
getBooleanType: () => booleanType,
getVoidType: () => voidType,
getUndefinedType: () => undefinedType,
getNullType: () => nullType,
getESSymbolType: () => esSymbolType,
getNeverType: () => neverType,
isSymbolAccessible,
isArrayLikeType,
getAllPossiblePropertiesOfTypes,
getSuggestionForNonexistentProperty: (node, type) => getSuggestionForNonexistentProperty(node, type),
getSuggestionForNonexistentSymbol: (location, name, meaning) => getSuggestionForNonexistentSymbol(location, escapeLeadingUnderscores(name), meaning),
getBaseConstraintOfType,
getDefaultFromTypeParameter: type => type && type.flags & TypeFlags.TypeParameter ? getDefaultFromTypeParameter(type as TypeParameter) : undefined,
resolveName(name, location, meaning) {
return resolveName(location, escapeLeadingUnderscores(name), meaning, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined, /*isUse*/ false);
},
getJsxNamespace: () => unescapeLeadingUnderscores(getJsxNamespace()),
getAccessibleSymbolChain,
};
const tupleTypes: GenericType[] = [];
const unionTypes = createMap();
const intersectionTypes = createMap();
const literalTypes = createMap();
const indexedAccessTypes = createMap();
const conditionalTypes = createMap();
const extendsTypes = createMap();
const evolvingArrayTypes: EvolvingArrayType[] = [];
const undefinedProperties = createMap() as UnderscoreEscapedMap;
const unknownSymbol = createSymbol(SymbolFlags.Property, "unknown" as __String);
const resolvingSymbol = createSymbol(0, InternalSymbolName.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 implicitNeverType = createIntrinsicType(TypeFlags.Never, "never");
const nonPrimitiveType = createIntrinsicType(TypeFlags.NonPrimitive, "object");
const emptyObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
const emptyTypeLiteralSymbol = createSymbol(SymbolFlags.TypeLiteral, InternalSymbolName.Type);
emptyTypeLiteralSymbol.members = createSymbolTable();
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 resolvingDefaultType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
const markerSuperType = createType(TypeFlags.TypeParameter);
const markerSubType = createType(TypeFlags.TypeParameter);
markerSubType.constraint = markerSuperType;
const markerOtherType = createType(TypeFlags.TypeParameter);
const noTypePredicate = createIdentifierTypePredicate("<>", 0, anyType);
const anySignature = createSignature(undefined, undefined, undefined, emptyArray, anyType, /*resolvedTypePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false);
const unknownSignature = createSignature(undefined, undefined, undefined, emptyArray, unknownType, /*resolvedTypePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false);
const resolvingSignature = createSignature(undefined, undefined, undefined, emptyArray, anyType, /*resolvedTypePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false);
const silentNeverSignature = createSignature(undefined, undefined, undefined, emptyArray, silentNeverType, /*resolvedTypePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false);
const enumNumberIndexInfo = createIndexInfo(stringType, /*isReadonly*/ true);
const jsObjectLiteralIndexInfo = createIndexInfo(anyType, /*isReadonly*/ false);
const globals = createSymbolTable();
let ambientModulesCache: Symbol[] | undefined;
/**
* List of every ambient module with a "*" wildcard.
* Unlike other ambient modules, these can't be stored in `globals` because symbol tables only deal with exact matches.
* This is only used if there is no exact match.
*/
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 sharedFlowCount = 0;
let flowAnalysisDisabled = false;
const emptyStringType = getLiteralType("");
const zeroType = getLiteralType(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 sharedFlowNodes: FlowNode[] = [];
const sharedFlowTypes: 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 = createUnderscoreEscapedMap();
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
Contextual = 3, // Normal type checking informed by a contextual type, therefore not cacheable
}
const enum CallbackCheck {
None,
Bivariant,
Strict,
}
const enum MappedTypeModifiers {
Readonly = 1 << 0,
Optional = 1 << 1,
}
const enum ExpandingFlags {
None = 0,
Source = 1,
Target = 1 << 1,
Both = Source | Target,
}
const enum MembersOrExportsResolutionKind {
resolvedExports = "resolvedExports",
resolvedMembers = "resolvedMembers"
}
const builtinGlobals = createSymbolTable();
builtinGlobals.set(undefinedSymbol.escapedName, undefinedSymbol);
const isNotOverloadAndNotAccessor = and(isNotOverload, isNotAccessor);
initializeTypeChecker();
return checker;
function getJsxNamespace(): __String {
if (!_jsxNamespace) {
_jsxNamespace = "React" as __String;
if (compilerOptions.jsxFactory) {
_jsxFactoryEntity = parseIsolatedEntityName(compilerOptions.jsxFactory, languageVersion);
if (_jsxFactoryEntity) {
_jsxNamespace = getFirstIdentifier(_jsxFactoryEntity).escapedText;
}
}
else if (compilerOptions.reactNamespace) {
_jsxNamespace = escapeLeadingUnderscores(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, arg3?: string | number): void {
const diagnostic = location
? createDiagnosticForNode(location, message, arg0, arg1, arg2, arg3)
: createCompilerDiagnostic(message, arg0, arg1, arg2, arg3);
diagnostics.add(diagnostic);
}
function createSymbol(flags: SymbolFlags, name: __String, checkFlags?: CheckFlags) {
symbolCount++;
const symbol = (new Symbol(flags | SymbolFlags.Transient, name));
symbol.checkFlags = 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.escapedName);
result.declarations = symbol.declarations ? symbol.declarations.slice() : [];
result.parent = symbol.parent;
if (symbol.valueDeclaration) result.valueDeclaration = symbol.valueDeclaration;
if (symbol.constEnumOnlyModule) result.constEnumOnlyModule = true;
if (symbol.members) result.members = 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)) ||
source.flags & SymbolFlags.JSContainer || target.flags & SymbolFlags.JSContainer) {
// Javascript static-property-assignment declarations always merge, even though they are also values
if (source.flags & SymbolFlags.ValueModule && target.flags & SymbolFlags.ValueModule && target.constEnumOnlyModule && !source.constEnumOnlyModule) {
// reset flag when merging instantiated module into value module that has only const enums
target.constEnumOnlyModule = false;
}
target.flags |= source.flags;
if (source.valueDeclaration &&
(!target.valueDeclaration ||
(target.valueDeclaration.kind === SyntaxKind.ModuleDeclaration && source.valueDeclaration.kind !== SyntaxKind.ModuleDeclaration))) {
// other kinds of value declarations take precedence over modules
target.valueDeclaration = source.valueDeclaration;
}
addRange(target.declarations, source.declarations);
if (source.members) {
if (!target.members) target.members = createSymbolTable();
mergeSymbolTable(target.members, source.members);
}
if (source.exports) {
if (!target.exports) target.exports = createSymbolTable();
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 combineSymbolTables(first: SymbolTable | undefined, second: SymbolTable | undefined): SymbolTable | undefined {
if (!first || first.size === 0) return second;
if (!second || second.size === 0) return first;
const combined = createSymbolTable();
mergeSymbolTable(combined, first);
mergeSymbolTable(combined, second);
return combined;
}
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: StringLiteral | Identifier): 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 = !(moduleName.parent.parent.flags & NodeFlags.Ambient)
? 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 {
// moduleName will be a StringLiteral since this is not `declare global`.
error(moduleName, Diagnostics.Cannot_augment_module_0_because_it_resolves_to_a_non_module_entity, (moduleName as StringLiteral).text);
}
}
}
function 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(unescapeLeadingUnderscores(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 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(getMembersOfSymbol(classDeclaration.symbol), 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) ||
isInTypeQuery(usage) ||
declaration.flags & NodeFlags.Ambient) {
// nodes are in different files and order cannot be determined
return true;
}
// declaration is after usage
// can be legal if usage is deferred (i.e. inside function or in initializer of instance property)
if (isUsedInFunctionOrInstanceProperty(usage, declaration)) {
return true;
}
const sourceFiles = host.getSourceFiles();
return 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
// 5. inside a TS export= declaration (since we will move the export statement during emit to avoid TDZ)
// or if usage is in a type context:
// 1. inside a type query (typeof in type position)
if (usage.parent.kind === SyntaxKind.ExportSpecifier || (usage.parent.kind === SyntaxKind.ExportAssignment && (usage.parent as ExportAssignment).isExportEquals)) {
// export specifiers do not use the variable, they only make it available for use
return true;
}
// When resolving symbols for exports, the `usage` location passed in can be the export site directly
if (usage.kind === SyntaxKind.ExportAssignment && (usage as ExportAssignment).isExportEquals) {
return true;
}
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;
}
// ForIn/ForOf case - use site should not be used in expression part
return isForInOrOfStatement(declaration.parent.parent) && isSameScopeDescendentOf(usage, declaration.parent.parent.expression, container);
}
function isUsedInFunctionOrInstanceProperty(usage: Node, declaration: Node, container?: Node): boolean {
return !!findAncestor(usage, current => {
if (current === container) {
return "quit";
}
if (isFunctionLike(current)) {
return true;
}
const initializerOfProperty = current.parent &&
current.parent.kind === SyntaxKind.PropertyDeclaration &&
(current.parent).initializer === current;
if (initializerOfProperty) {
if (hasModifier(current.parent, ModifierFlags.Static)) {
if (declaration.kind === SyntaxKind.MethodDeclaration) {
return true;
}
}
else {
const isDeclarationInstanceProperty = declaration.kind === SyntaxKind.PropertyDeclaration && !hasModifier(declaration, ModifierFlags.Static);
if (!isDeclarationInstanceProperty || getContainingClass(usage) !== getContainingClass(declaration)) {
return true;
}
}
}
});
}
}
/**
* Resolve a given name for a given meaning at a given location. An error is reported if the name was not found and
* the nameNotFoundMessage argument is not undefined. Returns the resolved symbol, or undefined if no symbol with
* the given name can be found.
*
* @param isUse If true, this will count towards --noUnusedLocals / --noUnusedParameters.
*/
function resolveName(
location: Node | undefined,
name: __String,
meaning: SymbolFlags,
nameNotFoundMessage: DiagnosticMessage | undefined,
nameArg: __String | Identifier,
isUse: boolean,
suggestedNameNotFoundMessage?: DiagnosticMessage): Symbol {
return resolveNameHelper(location, name, meaning, nameNotFoundMessage, nameArg, isUse, getSymbol, suggestedNameNotFoundMessage);
}
function resolveNameHelper(
location: Node | undefined,
name: __String,
meaning: SymbolFlags,
nameNotFoundMessage: DiagnosticMessage,
nameArg: __String | Identifier,
isUse: boolean,
lookup: typeof getSymbol,
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 lastNonBlockLocation: 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(InternalSymbolName.Default)) {
const localSymbol = getLocalSymbolForExportDefault(result);
if (localSymbol && (result.flags & meaning) && localSymbol.escapedName === name) {
break loop;
}
result = undefined;
}
// Because of module/namespace merging, a module's exports are in scope,
// yet we never want to treat an export specifier as putting a member in scope.
// Therefore, if the name we find is purely an export specifier, it is not actually considered in scope.
// Two things to note about this:
// 1. We have to check this without calling getSymbol. The problem with calling getSymbol
// on an export specifier is that it might find the export specifier itself, and try to
// resolve it as an alias. This will cause the checker to consider the export specifier
// a circular alias reference when it might not be.
// 2. We check === SymbolFlags.Alias in order to check that the symbol is *purely*
// an alias. If we used &, we'd be throwing out symbols that have non alias aspects,
// which is not the desired behavior.
const moduleExport = moduleExports.get(name);
if (moduleExport &&
moduleExport.flags === SymbolFlags.Alias &&
getDeclarationOfKind(moduleExport, SyntaxKind.ExportSpecifier)) {
break;
}
}
if (result = lookup(moduleExports, name, meaning & SymbolFlags.ModuleMember)) {
break loop;
}
break;
case SyntaxKind.EnumDeclaration:
if (result = lookup(getSymbolOfNode(location).exports, name, meaning & SymbolFlags.EnumMember)) {
break loop;
}
break;
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.PropertySignature:
// TypeScript 1.0 spec (April 2014): 8.4.1
// Initializer expressions for instance member variables are evaluated in the scope
// of the class constructor body but are not permitted to reference parameters or
// local variables of the constructor. This effectively means that entities from outer scopes
// by the same name as a constructor parameter or local variable are inaccessible
// in initializer expressions for instance member variables.
if (isClassLike(location.parent) && !hasModifier(location, ModifierFlags.Static)) {
const ctor = 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(getMembersOfSymbol(getSymbolOfNode(location)), name, meaning & SymbolFlags.Type)) {
if (!isTypeParameterSymbolDeclaredInContainer(result, location)) {
// ignore type parameters not declared in this container
result = undefined;
break;
}
if (lastLocation && hasModifier(lastLocation, ModifierFlags.Static)) {
// TypeScript 1.0 spec (April 2014): 3.4.1
// The scope of a type parameter extends over the entire declaration with which the type
// parameter list is associated, with the exception of static member declarations in classes.
error(errorLocation, Diagnostics.Static_members_cannot_reference_class_type_parameters);
return undefined;
}
break loop;
}
if (location.kind === SyntaxKind.ClassExpression && meaning & SymbolFlags.Class) {
const className = (location).name;
if (className && name === className.escapedText) {
result = location.symbol;
break loop;
}
}
break;
case SyntaxKind.ExpressionWithTypeArguments:
// The type parameters of a class are not in scope in the base class expression.
if (lastLocation === (location).expression && (location.parent).token === SyntaxKind.ExtendsKeyword) {
const container = location.parent.parent;
if (isClassLike(container) && (result = lookup(getSymbolOfNode(container).members, name, meaning & SymbolFlags.Type))) {
if (nameNotFoundMessage) {
error(errorLocation, Diagnostics.Base_class_expressions_cannot_reference_class_type_parameters);
}
return undefined;
}
}
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.escapedText) {
result = location.symbol;
break loop;
}
}
break;
case SyntaxKind.Decorator:
// Decorators are resolved at the class declaration. Resolving at the parameter
// or member would result in looking up locals in the method.
//
// function y() {}
// class C {
// method(@y x, y) {} // <-- decorator y should be resolved at the class declaration, not the parameter.
// }
//
if (location.parent && location.parent.kind === SyntaxKind.Parameter) {
location = location.parent;
}
//
// function y() {}
// class C {
// @y method(x, y) {} // <-- decorator y should be resolved at the class declaration, not the method.
// }
//
if (location.parent && isClassElement(location.parent)) {
location = location.parent;
}
break;
}
if (location.kind !== SyntaxKind.Block) {
lastNonBlockLocation = location;
}
lastLocation = location;
location = location.parent;
}
// We just climbed up parents looking for the name, meaning that we started in a descendant node of `lastLocation`.
// If `result === lastLocation.symbol`, that means that we are somewhere inside `lastLocation` looking up a name, and resolving to `lastLocation` itself.
// That means that this is a self-reference of `lastLocation`, and shouldn't count this when considering whether `lastLocation` is used.
if (isUse && result && nameNotFoundMessage && noUnusedIdentifiers && result !== lastNonBlockLocation.symbol) {
result.isReferenced = true;
}
if (!result) {
if (lastLocation) {
Debug.assert(lastLocation.kind === SyntaxKind.SourceFile);
if ((lastLocation as SourceFile).commonJsModuleIndicator && name === "exports") {
return lastLocation.symbol;
}
}
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) {
error(errorLocation, suggestedNameNotFoundMessage, diagnosticName(nameArg), suggestion);
}
}
if (!suggestion) {
error(errorLocation, nameNotFoundMessage, diagnosticName(nameArg));
}
suggestionCount++;
}
}
return undefined;
}
// Perform extra checks only if error reporting was requested
if (nameNotFoundMessage) {
if (propertyWithInvalidInitializer) {
// We have a match, but the reference occurred within a property initializer and the identifier also binds
// to a local variable in the constructor where the code will be emitted.
const propertyName = (propertyWithInvalidInitializer).name;
error(errorLocation, Diagnostics.Initializer_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor,
declarationNameToString(propertyName), diagnosticName(nameArg));
return undefined;
}
// Only check for block-scoped variable if we have an error location and are looking for the
// name with variable meaning
// For example,
// declare module foo {
// interface bar {}
// }
// const foo/*1*/: foo/*2*/.bar;
// The foo at /*1*/ and /*2*/ will share same symbol with two meanings:
// block-scoped variable and namespace module. However, only when we
// try to resolve name in /*1*/ which is used in variable position,
// we want to check for block-scoped
if (errorLocation &&
(meaning & SymbolFlags.BlockScopedVariable ||
((meaning & SymbolFlags.Class || meaning & SymbolFlags.Enum) && (meaning & SymbolFlags.Value) === SymbolFlags.Value))) {
const exportOrLocalSymbol = 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, unescapeLeadingUnderscores(name));
}
}
}
return result;
}
function diagnosticName(nameArg: __String | Identifier) {
return isString(nameArg) ? unescapeLeadingUnderscores(nameArg as __String) : declarationNameToString(nameArg as Identifier);
}
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, diagnosticName(nameArg), symbolToString(classSymbol));
return true;
}
// No static member is present.
// Check if we're in an instance method and look for a relevant instance member.
if (location === container && !hasModifier(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, diagnosticName(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:
if (isEntityNameExpression((node).expression)) {
return (node).expression;
}
// falls through
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.Namespace, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false));
const parent = errorLocation.parent;
if (symbol) {
if (isQualifiedName(parent)) {
Debug.assert(parent.left === errorLocation, "Should only be resolving left side of qualified name as a namespace");
const propName = parent.right.escapedText;
const propType = getPropertyOfType(getDeclaredTypeOfSymbol(symbol), propName);
if (propType) {
error(
parent,
Diagnostics.Cannot_access_0_1_because_0_is_a_type_but_not_a_namespace_Did_you_mean_to_retrieve_the_type_of_the_property_1_in_0_with_0_1,
unescapeLeadingUnderscores(name),
unescapeLeadingUnderscores(propName),
);
return true;
}
}
error(errorLocation, Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_namespace_here, unescapeLeadingUnderscores(name));
return true;
}
}
return false;
}
function 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, unescapeLeadingUnderscores(name));
return true;
}
const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.Type & ~SymbolFlags.Value, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false));
if (symbol && !(symbol.flags & SymbolFlags.NamespaceModule)) {
error(errorLocation, Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_value_here, unescapeLeadingUnderscores(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, /*isUse*/ false));
if (symbol) {
error(errorLocation, Diagnostics.Cannot_use_namespace_0_as_a_value, unescapeLeadingUnderscores(name));
return true;
}
}
else if (meaning & (SymbolFlags.Type & ~SymbolFlags.NamespaceModule & ~SymbolFlags.Value)) {
const symbol = resolveSymbol(resolveName(errorLocation, name, (SymbolFlags.ValueModule | SymbolFlags.NamespaceModule) & ~SymbolFlags.Type, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false));
if (symbol) {
error(errorLocation, Diagnostics.Cannot_use_namespace_0_as_a_type, unescapeLeadingUnderscores(name));
return true;
}
}
return false;
}
function checkResolvedBlockScopedVariable(result: Symbol, errorLocation: Node): void {
Debug.assert(!!(result.flags & SymbolFlags.BlockScopedVariable || result.flags & SymbolFlags.Class || result.flags & SymbolFlags.Enum));
// 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 (!(declaration.flags & NodeFlags.Ambient) && !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 | undefined {
switch (node.kind) {
case SyntaxKind.ImportEqualsDeclaration:
return node as ImportEqualsDeclaration;
case SyntaxKind.ImportClause:
return (node as ImportClause).parent;
case SyntaxKind.NamespaceImport:
return (node as NamespaceImport).parent.parent;
case SyntaxKind.ImportSpecifier:
return (node as ImportSpecifier).parent.parent.parent;
default:
return undefined;
}
}
function getDeclarationOfAliasSymbol(symbol: Symbol): Declaration | undefined {
return 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=" as __String);
exportDefaultSymbol = exportValue
? getPropertyOfType(getTypeOfSymbol(exportValue), InternalSymbolName.Default)
: resolveSymbol(moduleSymbol.exports.get(InternalSymbolName.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 === unknownSymbol && typeSymbol === unknownSymbol) {
return unknownSymbol;
}
if (valueSymbol.flags & (SymbolFlags.Type | SymbolFlags.Namespace)) {
return valueSymbol;
}
const result = createSymbol(valueSymbol.flags | typeSymbol.flags, valueSymbol.escapedName);
result.declarations = concatenate(valueSymbol.declarations, typeSymbol.declarations);
result.parent = valueSymbol.parent || typeSymbol.parent;
if (valueSymbol.valueDeclaration) result.valueDeclaration = valueSymbol.valueDeclaration;
if (typeSymbol.members) result.members = typeSymbol.members;
if (valueSymbol.exports) result.exports = valueSymbol.exports;
return result;
}
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.escapedText) {
if (isShorthandAmbientModuleSymbol(moduleSymbol)) {
return moduleSymbol;
}
let symbolFromVariable: Symbol;
// First check if module was specified with "export=". If so, get the member from the resolved type
if (moduleSymbol && moduleSymbol.exports && moduleSymbol.exports.get("export=" as __String)) {
symbolFromVariable = getPropertyOfType(getTypeOfSymbol(targetSymbol), name.escapedText);
}
else {
symbolFromVariable = getPropertyOfVariable(targetSymbol, name.escapedText);
}
// if symbolFromVariable is export - get its final target
symbolFromVariable = resolveSymbol(symbolFromVariable, dontResolveAlias);
let symbolFromModule = getExportOfModule(targetSymbol, name.escapedText, dontResolveAlias);
// If the export member we're looking for is default, and there is no real default but allowSyntheticDefaultImports is on, return the entire module as the default
if (!symbolFromModule && allowSyntheticDefaultImports && name.escapedText === InternalSymbolName.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);
}
}
/**
* Indicates that a symbol is an alias that does not merge with a local declaration.
*/
function isNonLocalAlias(symbol: Symbol, excludes = SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace) {
return symbol && (symbol.flags & (SymbolFlags.Alias | excludes)) === SymbolFlags.Alias;
}
function resolveSymbol(symbol: Symbol, dontResolveAlias?: boolean): Symbol {
const shouldResolve = !dontResolveAlias && isNonLocalAlias(symbol);
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.escapedText, meaning, ignoreErrors ? undefined : message, name, /*isUse*/ true);
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;
let namespace = resolveEntityName(left, SymbolFlags.Namespace, ignoreErrors, /*dontResolveAlias*/ false, location);
if (!namespace || nodeIsMissing(right)) {
return undefined;
}
else if (namespace === unknownSymbol) {
return namespace;
}
if (isInJavaScriptFile(name) && isDeclarationOfFunctionOrClassExpression(namespace)) {
namespace = getSymbolOfNode((namespace.valueDeclaration as VariableDeclaration).initializer);
}
symbol = getSymbol(getExportsOfSymbol(namespace), right.escapedText, 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.assertNever(name, "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 {
if (moduleReference === undefined) {
return;
}
if (startsWith(moduleReference, "@types/")) {
const diag = Diagnostics.Cannot_import_type_declaration_files_Consider_importing_0_instead_of_1;
const withoutAtTypePrefix = removePrefix(moduleReference, "@types/");
error(errorNode, diag, withoutAtTypePrefix, moduleReference);
}
const ambientModule = tryFindAmbientModule(moduleReference, /*withAugmentations*/ true);
if (ambientModule) {
return ambientModule;
}
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, moduleReference);
if (pattern) {
return getMergedSymbol(pattern.symbol);
}
}
// May be an untyped module. If so, ignore resolutionDiagnostic.
if (resolvedModule && !extensionIsTypeScript(resolvedModule.extension) && resolutionDiagnostic === undefined || resolutionDiagnostic === Diagnostics.Could_not_find_a_declaration_file_for_module_0_1_implicitly_has_an_any_type) {
if (isForAugmentation) {
const diag = Diagnostics.Invalid_module_name_in_augmentation_Module_0_resolves_to_an_untyped_module_at_1_which_cannot_be_augmented;
error(errorNode, diag, moduleReference, resolvedModule.resolvedFileName);
}
else if (noImplicitAny && moduleNotFoundError) {
let errorInfo = !resolvedModule.isExternalLibraryImport ? undefined : 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, moduleReference, resolvedModule.resolvedFileName);
}
else {
const tsExtension = tryExtractTypeScriptExtension(moduleReference);
if (tsExtension) {
const diag = Diagnostics.An_import_path_cannot_end_with_a_0_extension_Consider_importing_1_instead;
error(errorNode, diag, tsExtension, removeExtension(moduleReference, tsExtension));
}
else {
error(errorNode, moduleNotFoundError, moduleReference);
}
}
}
return undefined;
}
// An external module with an 'export =' declaration resolves to the target of the 'export =' declaration,
// and an external module with no 'export =' declaration resolves to the module itself.
function resolveExternalModuleSymbol(moduleSymbol: Symbol, dontResolveAlias?: boolean): Symbol {
return moduleSymbol && getMergedSymbol(resolveSymbol(moduleSymbol.exports.get(InternalSymbolName.ExportEquals), dontResolveAlias)) || moduleSymbol;
}
// An external module with an 'export =' declaration may be referenced as an ES6 module provided the 'export ='
// references a symbol that is at least declared as a module or a variable. The target of the 'export =' may
// combine other declarations with the module or variable (e.g. a class/module, function/module, interface/variable).
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(InternalSymbolName.ExportEquals) !== undefined;
}
function getExportsOfModuleAsArray(moduleSymbol: Symbol): Symbol[] {
return symbolsToArray(getExportsOfModule(moduleSymbol));
}
function getExportsAndPropertiesOfModule(moduleSymbol: Symbol): Symbol[] {
const exports = getExportsOfModuleAsArray(moduleSymbol);
const exportEquals = resolveExternalModuleSymbol(moduleSymbol);
if (exportEquals !== moduleSymbol) {
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 tryGetMemberInModuleExportsAndProperties(memberName: __String, moduleSymbol: Symbol): Symbol | undefined {
const symbol = tryGetMemberInModuleExports(memberName, moduleSymbol);
if (symbol) {
return symbol;
}
const exportEquals = resolveExternalModuleSymbol(moduleSymbol);
if (exportEquals === moduleSymbol) {
return undefined;
}
const type = getTypeOfSymbol(exportEquals);
return type.flags & TypeFlags.Primitive ? undefined : getPropertyOfType(type, memberName);
}
function getExportsOfSymbol(symbol: Symbol): SymbolTable {
return symbol.flags & SymbolFlags.Class ? getResolvedMembersOrExportsOfSymbol(symbol, MembersOrExportsResolutionKind.resolvedExports) :
symbol.flags & SymbolFlags.Module ? getExportsOfModule(symbol) :
symbol.exports || emptySymbols;
}
function getExportsOfModule(moduleSymbol: Symbol): SymbolTable {
const links = getSymbolLinks(moduleSymbol);
return links.resolvedExports || (links.resolvedExports = getExportsOfModuleWorker(moduleSymbol));
}
interface ExportCollisionTracker {
specifierText: string;
exportsWithDuplicate: ExportDeclaration[];
}
type ExportCollisionTrackerTable = UnderscoreEscapedMap;
/**
* Extends one symbol table with another while collecting information on name collisions for error message generation into the `lookupTable` argument
* Not passing `lookupTable` and `exportNode` disables this collection, and just extends the tables
*/
function extendExportSymbols(target: SymbolTable, source: SymbolTable | undefined, lookupTable?: ExportCollisionTrackerTable, exportNode?: ExportDeclaration) {
if (!source) return;
source.forEach((sourceSymbol, id) => {
if (id === InternalSymbolName.Default) return;
const targetSymbol = target.get(id);
if (!targetSymbol) {
target.set(id, sourceSymbol);
if (lookupTable && exportNode) {
lookupTable.set(id, {
specifierText: getTextOfNode(exportNode.moduleSpecifier)
} 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 getExportsOfModuleWorker(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) || emptySymbols;
// The ES6 spec permits export * declarations in a module to circularly reference the module itself. For example,
// module 'a' can 'export * from "b"' and 'b' can 'export * from "a"' without error.
function visit(symbol: Symbol): SymbolTable {
if (!(symbol && symbol.flags & SymbolFlags.HasExports && pushIfUnique(visitedSymbols, symbol))) {
return;
}
const symbols = cloneMap(symbol.exports);
// All export * declarations are collected in an __export symbol by the binder
const exportStars = symbol.exports.get(InternalSymbolName.ExportStar);
if (exportStars) {
const nestedSymbols = createSymbolTable();
const lookupTable = createMap() as ExportCollisionTrackerTable;
for (const node of exportStars.declarations) {
const resolvedModule = 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,
unescapeLeadingUnderscores(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 && getLateBoundSymbol(node.symbol));
}
function getParentOfSymbol(symbol: Symbol): Symbol {
return getMergedSymbol(symbol.parent && getLateBoundSymbol(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(arrayFrom(typeofEQFacts.keys(), getLiteralType));
}
// A reserved member name starts with two underscores, but the third character cannot be an underscore
// or the @ symbol. A third underscore indicates an escaped form of an identifer that started
// with at least two underscores. The @ character indicates that the name is denoted by a well known ES
// Symbol instance.
function isReservedMemberName(name: __String) {
return (name as string).charCodeAt(0) === CharacterCodes._ &&
(name as string).charCodeAt(1) === CharacterCodes._ &&
(name as string).charCodeAt(2) !== CharacterCodes._ &&
(name as string).charCodeAt(2) !== CharacterCodes.at;
}
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 | undefined, meaning: SymbolFlags, useOnlyExternalAliasing: boolean): Symbol[] | undefined {
if (!(symbol && !isPropertyOrMethodDeclarationSymbol(symbol))) {
return undefined;
}
const visitedSymbolTables: SymbolTable[] = [];
return forEachSymbolTableInScope(enclosingDeclaration, getAccessibleSymbolChainFromSymbolTable);
function getAccessibleSymbolChainFromSymbolTable(symbols: SymbolTable): Symbol[] | undefined {
if (!pushIfUnique(visitedSymbolTables, symbols)) {
return undefined;
}
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
return !needsQualification(symbolFromSymbolTable, enclosingDeclaration, meaning) ||
// If symbol needs qualification, make sure that parent is accessible, if it is then this symbol is accessible too
!!getAccessibleSymbolChain(symbolFromSymbolTable.parent, enclosingDeclaration, getQualifiedLeftMeaning(meaning), useOnlyExternalAliasing);
}
function isAccessible(symbolFromSymbolTable: Symbol, resolvedAliasSymbol?: Symbol) {
return 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
!some(symbolFromSymbolTable.declarations, hasExternalModuleSymbol) &&
canQualifySymbol(symbolFromSymbolTable, meaning);
}
function isUMDExportSymbol(symbol: Symbol) {
return symbol && symbol.declarations && symbol.declarations[0] && isNamespaceExportDeclaration(symbol.declarations[0]);
}
function trySymbolTable(symbols: SymbolTable) {
// If symbol is directly available by its name in the symbol table
if (isAccessible(symbols.get(symbol.escapedName))) {
return [symbol];
}
// Check if symbol is any of the alias
return forEachEntry(symbols, symbolFromSymbolTable => {
if (symbolFromSymbolTable.flags & SymbolFlags.Alias
&& symbolFromSymbolTable.escapedName !== "export="
&& !(isUMDExportSymbol(symbolFromSymbolTable) && enclosingDeclaration && isExternalModule(getSourceFileOfNode(enclosingDeclaration)))
// If `!useOnlyExternalAliasing`, we can use any type of alias to get the name
&& (!useOnlyExternalAliasing || some(symbolFromSymbolTable.declarations, isExternalModuleImportEqualsDeclaration))) {
const resolvedImportedSymbol = resolveAlias(symbolFromSymbolTable);
if (isAccessible(symbolFromSymbolTable, resolvedImportedSymbol)) {
return [symbolFromSymbolTable];
}
// Look in the exported members, if we can find accessibleSymbolChain, symbol is accessible using this chain
// but only if the symbolFromSymbolTable can be qualified
const candidateTable = getExportsOfSymbol(resolvedImportedSymbol);
const accessibleSymbolsFromExports = candidateTable && getAccessibleSymbolChainFromSymbolTable(candidateTable);
if (accessibleSymbolsFromExports && canQualifySymbol(symbolFromSymbolTable, getQualifiedLeftMeaning(meaning))) {
return [symbolFromSymbolTable].concat(accessibleSymbolsFromExports);
}
}
});
}
}
function needsQualification(symbol: Symbol, enclosingDeclaration: Node | undefined, meaning: SymbolFlags) {
let qualify = false;
forEachSymbolTableInScope(enclosingDeclaration, symbolTable => {
// If symbol of this name is not available in the symbol table we are ok
let symbolFromSymbolTable = symbolTable.get(symbol.escapedName);
if (!symbolFromSymbolTable) {
// Continue to the next symbol table
return false;
}
// If the symbol with this name is present it should refer to the symbol
if (symbolFromSymbolTable === symbol) {
// No need to qualify
return true;
}
// Qualify if the symbol from symbol table has same meaning as expected
symbolFromSymbolTable = (symbolFromSymbolTable.flags & SymbolFlags.Alias && !getDeclarationOfKind(symbolFromSymbolTable, SyntaxKind.ExportSpecifier)) ? 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;
}
function isTypeSymbolAccessible(typeSymbol: Symbol, enclosingDeclaration: Node): boolean {
const access = isSymbolAccessible(typeSymbol, enclosingDeclaration, SymbolFlags.Type, /*shouldComputeAliasesToMakeVisible*/ false);
return access.accessibility === SymbolAccessibility.Accessible;
}
/**
* 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 &&
!hasModifier(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;
aliasesToMakeVisible = appendIfUnique(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) ||
entityName.parent.kind === SyntaxKind.ComputedPropertyName) {
// 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.escapedText, meaning, /*nodeNotFoundErrorMessage*/ undefined, /*nameArg*/ undefined, /*isUse*/ false);
// 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 {
return usingSingleLineStringWriter(writer => {
getSymbolDisplayBuilder().buildSymbolDisplay(symbol, writer, enclosingDeclaration, meaning);
});
}
function signatureToString(signature: Signature, enclosingDeclaration?: Node, flags?: TypeFormatFlags, kind?: SignatureKind): string {
return usingSingleLineStringWriter(writer => {
getSymbolDisplayBuilder().buildSignatureDisplay(signature, writer, enclosingDeclaration, flags, kind);
});
}
function typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string {
const typeNode = nodeBuilder.typeToTypeNode(type, enclosingDeclaration, toNodeBuilderFlags(flags) | NodeBuilderFlags.IgnoreErrors | NodeBuilderFlags.WriteTypeParametersInQualifiedName);
Debug.assert(typeNode !== undefined, "should always get typenode");
const options = { removeComments: true };
const writer = createTextWriter("");
const printer = createPrinter(options);
const sourceFile = enclosingDeclaration && getSourceFileOfNode(enclosingDeclaration);
printer.writeNode(EmitHint.Unspecified, typeNode, /*sourceFile*/ sourceFile, writer);
const result = writer.getText();
const maxLength = compilerOptions.noErrorTruncation || flags & TypeFormatFlags.NoTruncation ? undefined : 100;
if (maxLength && result.length >= maxLength) {
return result.substr(0, maxLength - "...".length) + "...";
}
return result;
function toNodeBuilderFlags(flags?: TypeFormatFlags): NodeBuilderFlags {
let result = NodeBuilderFlags.None;
if (!flags) {
return result;
}
if (flags & TypeFormatFlags.NoTruncation) {
result |= NodeBuilderFlags.NoTruncation;
}
if (flags & TypeFormatFlags.UseFullyQualifiedType) {
result |= NodeBuilderFlags.UseFullyQualifiedType;
}
if (flags & TypeFormatFlags.SuppressAnyReturnType) {
result |= NodeBuilderFlags.SuppressAnyReturnType;
}
if (flags & TypeFormatFlags.WriteArrayAsGenericType) {
result |= NodeBuilderFlags.WriteArrayAsGenericType;
}
if (flags & TypeFormatFlags.WriteTypeArgumentsOfSignature) {
result |= NodeBuilderFlags.WriteTypeArgumentsOfSignature;
}
return result;
}
}
function createNodeBuilder() {
return {
typeToTypeNode: (type: Type, enclosingDeclaration?: Node, flags?: NodeBuilderFlags) => {
Debug.assert(enclosingDeclaration === undefined || (enclosingDeclaration.flags & NodeFlags.Synthesized) === 0);
const context = createNodeBuilderContext(enclosingDeclaration, flags);
const resultingNode = typeToTypeNodeHelper(type, context);
const result = context.encounteredError ? undefined : resultingNode;
return result;
},
indexInfoToIndexSignatureDeclaration: (indexInfo: IndexInfo, kind: IndexKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags) => {
Debug.assert(enclosingDeclaration === undefined || (enclosingDeclaration.flags & NodeFlags.Synthesized) === 0);
const context = createNodeBuilderContext(enclosingDeclaration, flags);
const resultingNode = indexInfoToIndexSignatureDeclarationHelper(indexInfo, kind, context);
const result = context.encounteredError ? undefined : resultingNode;
return result;
},
signatureToSignatureDeclaration: (signature: Signature, kind: SyntaxKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags) => {
Debug.assert(enclosingDeclaration === undefined || (enclosingDeclaration.flags & NodeFlags.Synthesized) === 0);
const context = createNodeBuilderContext(enclosingDeclaration, flags);
const resultingNode = signatureToSignatureDeclarationHelper(signature, kind, context);
const result = context.encounteredError ? undefined : resultingNode;
return result;
}
};
function createNodeBuilderContext(enclosingDeclaration: Node | undefined, flags: NodeBuilderFlags | undefined): NodeBuilderContext {
return {
enclosingDeclaration,
flags,
encounteredError: false,
symbolStack: undefined
};
}
function typeToTypeNodeHelper(type: Type, context: NodeBuilderContext): TypeNode {
const inTypeAlias = context.flags & NodeBuilderFlags.InTypeAlias;
context.flags &= ~NodeBuilderFlags.InTypeAlias;
if (!type) {
context.encounteredError = true;
return undefined;
}
if (type.flags & TypeFlags.Any) {
return createKeywordTypeNode(SyntaxKind.AnyKeyword);
}
if (type.flags & TypeFlags.String) {
return createKeywordTypeNode(SyntaxKind.StringKeyword);
}
if (type.flags & TypeFlags.Number) {
return createKeywordTypeNode(SyntaxKind.NumberKeyword);
}
if (type.flags & TypeFlags.Boolean) {
return createKeywordTypeNode(SyntaxKind.BooleanKeyword);
}
if (type.flags & TypeFlags.EnumLiteral && !(type.flags & TypeFlags.Union)) {
const parentSymbol = getParentOfSymbol(type.symbol);
const parentName = symbolToName(parentSymbol, context, SymbolFlags.Type, /*expectsIdentifier*/ false);
const enumLiteralName = getDeclaredTypeOfSymbol(parentSymbol) === type ? parentName : createQualifiedName(parentName, symbolName(type.symbol));
return createTypeReferenceNode(enumLiteralName, /*typeArguments*/ undefined);
}
if (type.flags & TypeFlags.EnumLike) {
const name = symbolToName(type.symbol, context, SymbolFlags.Type, /*expectsIdentifier*/ false);
return createTypeReferenceNode(name, /*typeArguments*/ undefined);
}
if (type.flags & (TypeFlags.StringLiteral)) {
return createLiteralTypeNode(setEmitFlags(createLiteral((type).value), EmitFlags.NoAsciiEscaping));
}
if (type.flags & (TypeFlags.NumberLiteral)) {
return createLiteralTypeNode((createLiteral((type).value)));
}
if (type.flags & TypeFlags.BooleanLiteral) {
return (type).intrinsicName === "true" ? createTrue() : createFalse();
}
if (type.flags & TypeFlags.UniqueESSymbol) {
return createTypeOperatorNode(SyntaxKind.UniqueKeyword, createKeywordTypeNode(SyntaxKind.SymbolKeyword));
}
if (type.flags & TypeFlags.Void) {
return createKeywordTypeNode(SyntaxKind.VoidKeyword);
}
if (type.flags & TypeFlags.Undefined) {
return createKeywordTypeNode(SyntaxKind.UndefinedKeyword);
}
if (type.flags & TypeFlags.Null) {
return createKeywordTypeNode(SyntaxKind.NullKeyword);
}
if (type.flags & TypeFlags.Never) {
return createKeywordTypeNode(SyntaxKind.NeverKeyword);
}
if (type.flags & TypeFlags.ESSymbol) {
return createKeywordTypeNode(SyntaxKind.SymbolKeyword);
}
if (type.flags & TypeFlags.NonPrimitive) {
return createKeywordTypeNode(SyntaxKind.ObjectKeyword);
}
if (type.flags & TypeFlags.TypeParameter && (type as TypeParameter).isThisType) {
if (context.flags & NodeBuilderFlags.InObjectTypeLiteral) {
if (!context.encounteredError && !(context.flags & NodeBuilderFlags.AllowThisInObjectLiteral)) {
context.encounteredError = true;
}
}
return createThis();
}
const objectFlags = getObjectFlags(type);
if (objectFlags & ObjectFlags.Reference) {
Debug.assert(!!(type.flags & TypeFlags.Object));
return typeReferenceToTypeNode(type);
}
if (type.flags & TypeFlags.TypeParameter || objectFlags & ObjectFlags.ClassOrInterface) {
const name = type.symbol ? symbolToName(type.symbol, context, SymbolFlags.Type, /*expectsIdentifier*/ false) : createIdentifier("?");
// Ignore constraint/default when creating a usage (as opposed to declaration) of a type parameter.
return createTypeReferenceNode(name, /*typeArguments*/ undefined);
}
if (!inTypeAlias && type.aliasSymbol && isTypeSymbolAccessible(type.aliasSymbol, context.enclosingDeclaration)) {
const name = symbolToTypeReferenceName(type.aliasSymbol);
const typeArgumentNodes = mapToTypeNodes(type.aliasTypeArguments, context);
return createTypeReferenceNode(name, typeArgumentNodes);
}
if (type.flags & (TypeFlags.Union | TypeFlags.Intersection)) {
const types = type.flags & TypeFlags.Union ? formatUnionTypes((type).types) : (type).types;
const typeNodes = mapToTypeNodes(types, context);
if (typeNodes && typeNodes.length > 0) {
const unionOrIntersectionTypeNode = createUnionOrIntersectionTypeNode(type.flags & TypeFlags.Union ? SyntaxKind.UnionType : SyntaxKind.IntersectionType, typeNodes);
return unionOrIntersectionTypeNode;
}
else {
if (!context.encounteredError && !(context.flags & NodeBuilderFlags.AllowEmptyUnionOrIntersection)) {
context.encounteredError = true;
}
return undefined;
}
}
if (objectFlags & (ObjectFlags.Anonymous | ObjectFlags.Mapped)) {
Debug.assert(!!(type.flags & TypeFlags.Object));
// The type is an object literal type.
return createAnonymousTypeNode(type);
}
if (type.flags & TypeFlags.Index) {
const indexedType = (type).type;
const indexTypeNode = typeToTypeNodeHelper(indexedType, context);
return createTypeOperatorNode(indexTypeNode);
}
if (type.flags & TypeFlags.IndexedAccess) {
const objectTypeNode = typeToTypeNodeHelper((type).objectType, context);
const indexTypeNode = typeToTypeNodeHelper((type).indexType, context);
return createIndexedAccessTypeNode(objectTypeNode, indexTypeNode);
}
if (type.flags & TypeFlags.Conditional) {
const conditionTypeNode = typeToTypeNodeHelper((type).conditionType, context);
const trueTypeNode = typeToTypeNodeHelper((type).trueType, context);
const falseTypeNode = typeToTypeNodeHelper((type).falseType, context);
return createConditionalTypeNode(conditionTypeNode, trueTypeNode, falseTypeNode);
}
if (type.flags & TypeFlags.Extends) {
const leftTypeNode = typeToTypeNodeHelper((type).checkType, context);
const rightTypeNode = typeToTypeNodeHelper((type).extendsType, context);
return createBinaryTypeNode(leftTypeNode, SyntaxKind.ExtendsKeyword, rightTypeNode);
}
Debug.fail("Should be unreachable.");
function createMappedTypeNodeFromType(type: MappedType) {
Debug.assert(!!(type.flags & TypeFlags.Object));
const readonlyToken = type.declaration && type.declaration.readonlyToken ? createToken(SyntaxKind.ReadonlyKeyword) : undefined;
const questionToken = type.declaration && type.declaration.questionToken ? createToken(SyntaxKind.QuestionToken) : undefined;
const typeParameterNode = typeParameterToDeclaration(getTypeParameterFromMappedType(type), context);
const templateTypeNode = typeToTypeNodeHelper(getTemplateTypeFromMappedType(type), context);
const mappedTypeNode = createMappedTypeNode(readonlyToken, typeParameterNode, questionToken, templateTypeNode);
return setEmitFlags(mappedTypeNode, EmitFlags.SingleLine);
}
function createAnonymousTypeNode(type: ObjectType): TypeNode {
const symbol = type.symbol;
if (symbol) {
// Always use 'typeof T' for type of class, enum, and module objects
if (symbol.flags & SymbolFlags.Class && !getBaseTypeVariableOfClass(symbol) ||
symbol.flags & (SymbolFlags.Enum | SymbolFlags.ValueModule) ||
shouldWriteTypeOfFunctionSymbol()) {
return createTypeQueryNodeFromSymbol(symbol, SymbolFlags.Value);
}
else if (contains(context.symbolStack, symbol)) {
// If type is an anonymous type literal in a type alias declaration, use type alias name
const typeAlias = getTypeAliasForTypeLiteral(type);
if (typeAlias) {
// The specified symbol flags need to be reinterpreted as type flags
const entityName = symbolToName(typeAlias, context, SymbolFlags.Type, /*expectsIdentifier*/ false);
return createTypeReferenceNode(entityName, /*typeArguments*/ undefined);
}
else {
return createKeywordTypeNode(SyntaxKind.AnyKeyword);
}
}
else {
// Since instantiations of the same anonymous type have the same symbol, tracking symbols instead
// of types allows us to catch circular references to instantiations of the same anonymous type
if (!context.symbolStack) {
context.symbolStack = [];
}
context.symbolStack.push(symbol);
const result = createTypeNodeFromObjectType(type);
context.symbolStack.pop();
return result;
}
}
else {
// Anonymous types without a symbol are never circular.
return createTypeNodeFromObjectType(type);
}
function shouldWriteTypeOfFunctionSymbol() {
const isStaticMethodSymbol = !!(symbol.flags & SymbolFlags.Method) && // typeof static method
some(symbol.declarations, declaration => hasModifier(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 (isGenericMappedType(type)) {
return createMappedTypeNodeFromType(type);
}
const resolved = resolveStructuredTypeMembers(type);
if (!resolved.properties.length && !resolved.stringIndexInfo && !resolved.numberIndexInfo) {
if (!resolved.callSignatures.length && !resolved.constructSignatures.length) {
return setEmitFlags(createTypeLiteralNode(/*members*/ undefined), EmitFlags.SingleLine);
}
if (resolved.callSignatures.length === 1 && !resolved.constructSignatures.length) {
const signature = resolved.callSignatures[0];
const signatureNode = signatureToSignatureDeclarationHelper(signature, SyntaxKind.FunctionType, context);
return signatureNode;
}
if (resolved.constructSignatures.length === 1 && !resolved.callSignatures.length) {
const signature = resolved.constructSignatures[0];
const signatureNode = signatureToSignatureDeclarationHelper(signature, SyntaxKind.ConstructorType, context);
return signatureNode;
}
}
const savedFlags = context.flags;
context.flags |= NodeBuilderFlags.InObjectTypeLiteral;
const members = createTypeNodesFromResolvedType(resolved);
context.flags = savedFlags;
const typeLiteralNode = createTypeLiteralNode(members);
return setEmitFlags(typeLiteralNode, EmitFlags.SingleLine);
}
function createTypeQueryNodeFromSymbol(symbol: Symbol, symbolFlags: SymbolFlags) {
const entityName = symbolToName(symbol, context, symbolFlags, /*expectsIdentifier*/ false);
return createTypeQueryNode(entityName);
}
function symbolToTypeReferenceName(symbol: Symbol) {
// Unnamed function expressions and arrow functions have reserved names that we don't want to display
const entityName = symbol.flags & SymbolFlags.Class || !isReservedMemberName(symbol.escapedName) ? symbolToName(symbol, context, SymbolFlags.Type, /*expectsIdentifier*/ false) : createIdentifier("");
return entityName;
}
function typeReferenceToTypeNode(type: TypeReference) {
const typeArguments: Type[] = type.typeArguments || emptyArray;
if (type.target === globalArrayType) {
if (context.flags & NodeBuilderFlags.WriteArrayAsGenericType) {
const typeArgumentNode = typeToTypeNodeHelper(typeArguments[0], context);
return createTypeReferenceNode("Array", [typeArgumentNode]);
}
const elementType = typeToTypeNodeHelper(typeArguments[0], context);
return createArrayTypeNode(elementType);
}
else if (type.target.objectFlags & ObjectFlags.Tuple) {
if (typeArguments.length > 0) {
const tupleConstituentNodes = mapToTypeNodes(typeArguments.slice(0, getTypeReferenceArity(type)), context);
if (tupleConstituentNodes && tupleConstituentNodes.length > 0) {
return createTupleTypeNode(tupleConstituentNodes);
}
}
if (context.encounteredError || (context.flags & NodeBuilderFlags.AllowEmptyTuple)) {
return createTupleTypeNode([]);
}
context.encounteredError = true;
return undefined;
}
else {
const outerTypeParameters = type.target.outerTypeParameters;
let i = 0;
let qualifiedName: QualifiedName | undefined;
if (outerTypeParameters) {
const length = outerTypeParameters.length;
while (i < length) {
// Find group of type arguments for type parameters with the same declaring container.
const start = i;
const parent = getParentSymbolOfTypeParameter(outerTypeParameters[i]);
do {
i++;
} while (i < length && getParentSymbolOfTypeParameter(outerTypeParameters[i]) === parent);
// When type parameters are their own type arguments for the whole group (i.e. we have
// the default outer type arguments), we don't show the group.
if (!rangeEquals(outerTypeParameters, typeArguments, start, i)) {
const typeArgumentSlice = mapToTypeNodes(typeArguments.slice(start, i), context);
const typeArgumentNodes = typeArgumentSlice && createNodeArray(typeArgumentSlice);
const namePart = symbolToTypeReferenceName(parent);
(namePart.kind === SyntaxKind.Identifier ? namePart : namePart.right).typeArguments = typeArgumentNodes;
if (qualifiedName) {
Debug.assert(!qualifiedName.right);
qualifiedName = addToQualifiedNameMissingRightIdentifier(qualifiedName, namePart);
qualifiedName = createQualifiedName(qualifiedName, /*right*/ undefined);
}
else {
qualifiedName = createQualifiedName(namePart, /*right*/ undefined);
}
}
}
}
let entityName: EntityName = undefined;
const nameIdentifier = symbolToTypeReferenceName(type.symbol);
if (qualifiedName) {
Debug.assert(!qualifiedName.right);
qualifiedName = addToQualifiedNameMissingRightIdentifier(qualifiedName, nameIdentifier);
entityName = qualifiedName;
}
else {
entityName = nameIdentifier;
}
let typeArgumentNodes: ReadonlyArray | undefined;
if (typeArguments.length > 0) {
const typeParameterCount = (type.target.typeParameters || emptyArray).length;
typeArgumentNodes = mapToTypeNodes(typeArguments.slice(i, typeParameterCount), context);
}
if (typeArgumentNodes) {
const lastIdentifier = entityName.kind === SyntaxKind.Identifier ? entityName : entityName.right;
lastIdentifier.typeArguments = undefined;
}
return createTypeReferenceNode(entityName, typeArgumentNodes);
}
}
function addToQualifiedNameMissingRightIdentifier(left: QualifiedName, right: Identifier | QualifiedName) {
Debug.assert(left.right === undefined);
if (right.kind === SyntaxKind.Identifier) {
left.right = right;
return left;
}
let rightPart = right;
while (rightPart.left.kind !== SyntaxKind.Identifier) {
rightPart = rightPart.left;
}
left.right = rightPart.left;
rightPart.left = left;
return right;
}
function createTypeNodesFromResolvedType(resolvedType: ResolvedType): TypeElement[] {
const typeElements: TypeElement[] = [];
for (const signature of resolvedType.callSignatures) {
typeElements.push(signatureToSignatureDeclarationHelper(signature, SyntaxKind.CallSignature, context));
}
for (const signature of resolvedType.constructSignatures) {
typeElements.push(signatureToSignatureDeclarationHelper(signature, SyntaxKind.ConstructSignature, context));
}
if (resolvedType.stringIndexInfo) {
typeElements.push(indexInfoToIndexSignatureDeclarationHelper(resolvedType.stringIndexInfo, IndexKind.String, context));
}
if (resolvedType.numberIndexInfo) {
typeElements.push(indexInfoToIndexSignatureDeclarationHelper(resolvedType.numberIndexInfo, IndexKind.Number, context));
}
const properties = resolvedType.properties;
if (!properties) {
return typeElements;
}
for (const propertySymbol of properties) {
const propertyType = getTypeOfSymbol(propertySymbol);
const saveEnclosingDeclaration = context.enclosingDeclaration;
context.enclosingDeclaration = undefined;
const propertyName = symbolToName(propertySymbol, context, SymbolFlags.Value, /*expectsIdentifier*/ true);
context.enclosingDeclaration = saveEnclosingDeclaration;
const optionalToken = propertySymbol.flags & SymbolFlags.Optional ? createToken(SyntaxKind.QuestionToken) : undefined;
if (propertySymbol.flags & (SymbolFlags.Function | SymbolFlags.Method) && !getPropertiesOfObjectType(propertyType).length) {
const signatures = getSignaturesOfType(propertyType, SignatureKind.Call);
for (const signature of signatures) {
const methodDeclaration = signatureToSignatureDeclarationHelper(signature, SyntaxKind.MethodSignature, context);
methodDeclaration.name = propertyName;
methodDeclaration.questionToken = optionalToken;
typeElements.push(methodDeclaration);
}
}
else {
const propertyTypeNode = propertyType ? typeToTypeNodeHelper(propertyType, context) : createKeywordTypeNode(SyntaxKind.AnyKeyword);
const modifiers = isReadonlySymbol(propertySymbol) ? [createToken(SyntaxKind.ReadonlyKeyword)] : undefined;
const propertySignature = createPropertySignature(
modifiers,
propertyName,
optionalToken,
propertyTypeNode,
/*initializer*/ undefined);
typeElements.push(propertySignature);
}
}
return typeElements.length ? typeElements : undefined;
}
}
function mapToTypeNodes(types: Type[], context: NodeBuilderContext): TypeNode[] {
if (some(types)) {
const result = [];
for (const type of types) {
const typeNode = typeToTypeNodeHelper(type, context);
if (typeNode) {
result.push(typeNode);
}
}
return result;
}
}
function indexInfoToIndexSignatureDeclarationHelper(indexInfo: IndexInfo, kind: IndexKind, context: NodeBuilderContext): IndexSignatureDeclaration {
const name = getNameFromIndexInfo(indexInfo) || "x";
const indexerTypeNode = createKeywordTypeNode(kind === IndexKind.String ? SyntaxKind.StringKeyword : SyntaxKind.NumberKeyword);
const indexingParameter = createParameter(
/*decorators*/ undefined,
/*modifiers*/ undefined,
/*dotDotDotToken*/ undefined,
name,
/*questionToken*/ undefined,
indexerTypeNode,
/*initializer*/ undefined);
const typeNode = typeToTypeNodeHelper(indexInfo.type, context);
return createIndexSignature(
/*decorators*/ undefined,
indexInfo.isReadonly ? [createToken(SyntaxKind.ReadonlyKeyword)] : undefined,
[indexingParameter],
typeNode);
}
function signatureToSignatureDeclarationHelper(signature: Signature, kind: SyntaxKind, context: NodeBuilderContext): SignatureDeclaration {
const typeParameters = signature.typeParameters && signature.typeParameters.map(parameter => typeParameterToDeclaration(parameter, context));
const parameters = signature.parameters.map(parameter => symbolToParameterDeclaration(parameter, context));
if (signature.thisParameter) {
const thisParameter = symbolToParameterDeclaration(signature.thisParameter, context);
parameters.unshift(thisParameter);
}
let returnTypeNode: TypeNode;
const typePredicate = getTypePredicateOfSignature(signature);
if (typePredicate) {
const parameterName = typePredicate.kind === TypePredicateKind.Identifier ?
setEmitFlags(createIdentifier((typePredicate).parameterName), EmitFlags.NoAsciiEscaping) :
createThisTypeNode();
const typeNode = typeToTypeNodeHelper(typePredicate.type, context);
returnTypeNode = createTypePredicateNode(parameterName, typeNode);
}
else {
const returnType = getReturnTypeOfSignature(signature);
returnTypeNode = returnType && typeToTypeNodeHelper(returnType, context);
}
if (context.flags & NodeBuilderFlags.SuppressAnyReturnType) {
if (returnTypeNode && returnTypeNode.kind === SyntaxKind.AnyKeyword) {
returnTypeNode = undefined;
}
}
else if (!returnTypeNode) {
returnTypeNode = createKeywordTypeNode(SyntaxKind.AnyKeyword);
}
return createSignatureDeclaration(kind, typeParameters, parameters, returnTypeNode);
}
function typeParameterToDeclaration(type: TypeParameter, context: NodeBuilderContext): TypeParameterDeclaration {
const name = symbolToName(type.symbol, context, SymbolFlags.Type, /*expectsIdentifier*/ true);
const constraint = getConstraintFromTypeParameter(type);
const constraintNode = constraint && typeToTypeNodeHelper(constraint, context);
const defaultParameter = getDefaultFromTypeParameter(type);
const defaultParameterNode = defaultParameter && typeToTypeNodeHelper(defaultParameter, context);
return createTypeParameterDeclaration(name, constraintNode, defaultParameterNode);
}
function symbolToParameterDeclaration(parameterSymbol: Symbol, context: NodeBuilderContext): ParameterDeclaration {
const parameterDeclaration = getDeclarationOfKind(parameterSymbol, SyntaxKind.Parameter);
Debug.assert(!!parameterDeclaration || isTransientSymbol(parameterSymbol) && !!parameterSymbol.isRestParameter);
let parameterType = getTypeOfSymbol(parameterSymbol);
if (parameterDeclaration && isRequiredInitializedParameter(parameterDeclaration)) {
parameterType = getOptionalType(parameterType);
}
const parameterTypeNode = typeToTypeNodeHelper(parameterType, context);
const modifiers = parameterDeclaration && parameterDeclaration.modifiers && parameterDeclaration.modifiers.map(getSynthesizedClone);
const dotDotDotToken = !parameterDeclaration || isRestParameter(parameterDeclaration) ? createToken(SyntaxKind.DotDotDotToken) : undefined;
const name = parameterDeclaration
? parameterDeclaration.name ?
parameterDeclaration.name.kind === SyntaxKind.Identifier ?
setEmitFlags(getSynthesizedClone(parameterDeclaration.name), EmitFlags.NoAsciiEscaping) :
cloneBindingName(parameterDeclaration.name) :
symbolName(parameterSymbol)
: symbolName(parameterSymbol);
const questionToken = parameterDeclaration && isOptionalParameter(parameterDeclaration) ? createToken(SyntaxKind.QuestionToken) : undefined;
const parameterNode = createParameter(
/*decorators*/ undefined,
modifiers,
dotDotDotToken,
name,
questionToken,
parameterTypeNode,
/*initializer*/ undefined);
return parameterNode;
function cloneBindingName(node: BindingName): BindingName {
return elideInitializerAndSetEmitFlags(node);
function elideInitializerAndSetEmitFlags(node: Node): Node {
const visited = visitEachChild(node, elideInitializerAndSetEmitFlags, nullTransformationContext, /*nodesVisitor*/ undefined, elideInitializerAndSetEmitFlags);
const clone = nodeIsSynthesized(visited) ? visited : getSynthesizedClone(visited);
if (clone.kind === SyntaxKind.BindingElement) {
(clone).initializer = undefined;
}
return setEmitFlags(clone, EmitFlags.SingleLine | EmitFlags.NoAsciiEscaping);
}
}
}
function symbolToName(symbol: Symbol, context: NodeBuilderContext, meaning: SymbolFlags, expectsIdentifier: true): Identifier;
function symbolToName(symbol: Symbol, context: NodeBuilderContext, meaning: SymbolFlags, expectsIdentifier: false): EntityName;
function symbolToName(symbol: Symbol, context: NodeBuilderContext, meaning: SymbolFlags, expectsIdentifier: boolean): EntityName {
// Try to get qualified name if the symbol is not a type parameter and there is an enclosing declaration.
let chain: Symbol[];
const isTypeParameter = symbol.flags & SymbolFlags.TypeParameter;
if (!isTypeParameter && (context.enclosingDeclaration || context.flags & NodeBuilderFlags.UseFullyQualifiedType)) {
chain = getSymbolChain(symbol, meaning, /*endOfChain*/ true);
Debug.assert(chain && chain.length > 0);
}
else {
chain = [symbol];
}
if (expectsIdentifier && chain.length !== 1
&& !context.encounteredError
&& !(context.flags & NodeBuilderFlags.AllowQualifedNameInPlaceOfIdentifier)) {
context.encounteredError = true;
}
return createEntityNameFromSymbolChain(chain, chain.length - 1);
function createEntityNameFromSymbolChain(chain: Symbol[], index: number): EntityName {
Debug.assert(chain && 0 <= index && index < chain.length);
const symbol = chain[index];
let typeParameterNodes: ReadonlyArray | undefined;
if (context.flags & NodeBuilderFlags.WriteTypeParametersInQualifiedName && index > 0) {
const parentSymbol = chain[index - 1];
let typeParameters: TypeParameter[];
if (getCheckFlags(symbol) & CheckFlags.Instantiated) {
typeParameters = getTypeParametersOfClassOrInterface(parentSymbol);
}
else {
const targetSymbol = getTargetSymbol(parentSymbol);
if (targetSymbol.flags & (SymbolFlags.Class | SymbolFlags.Interface | SymbolFlags.TypeAlias)) {
typeParameters = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol);
}
}
typeParameterNodes = mapToTypeNodes(typeParameters, context);
}
const identifier = setEmitFlags(createIdentifier(getNameOfSymbolAsWritten(symbol, context), typeParameterNodes), EmitFlags.NoAsciiEscaping);
return index > 0 ? createQualifiedName(createEntityNameFromSymbolChain(chain, index - 1), identifier) : identifier;
}
/** @param endOfChain Set to false for recursive calls; non-recursive calls should always output something. */
function getSymbolChain(symbol: Symbol, meaning: SymbolFlags, endOfChain: boolean): Symbol[] | undefined {
let accessibleSymbolChain = 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 typePredicateToString(typePredicate: TypePredicate, enclosingDeclaration?: Declaration, flags?: TypeFormatFlags): string {
return usingSingleLineStringWriter(writer => {
getSymbolDisplayBuilder().buildTypePredicateDisplay(typePredicate, writer, enclosingDeclaration, flags);
});
}
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 : getBaseTypeOfEnumLiteralType(t);
if (baseType.flags & TypeFlags.Union) {
const count = (baseType).types.length;
if (i + count <= types.length && types[i + count - 1] === (baseType).types[count - 1]) {
result.push(baseType);
i += count - 1;
continue;
}
}
}
result.push(t);
}
}
if (flags & TypeFlags.Null) result.push(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).value) + '"' : "" + (type).value;
}
interface NodeBuilderContext {
enclosingDeclaration: Node | undefined;
flags: NodeBuilderFlags | undefined;
// State
encounteredError: boolean;
symbolStack: Symbol[] | undefined;
}
/**
* Gets a human-readable name for a symbol.
* Should *not* be used for the right-hand side of a `.` -- use `symbolName(symbol)` for that instead.
*
* Unlike `symbolName(symbol)`, this will include quotes if the name is from a string literal.
* It will also use a representation of a number as written instead of a decimal form, e.g. `0o11` instead of `9`.
*/
function getNameOfSymbolAsWritten(symbol: Symbol, context?: NodeBuilderContext): string {
if (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);
}
if (context && !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)";
}
}
if ((symbol as TransientSymbol).syntheticLiteralTypeOrigin) {
const stringValue = (symbol as TransientSymbol).syntheticLiteralTypeOrigin.value;
if (!isIdentifierText(stringValue, compilerOptions.target)) {
return `"${escapeString(stringValue, CharacterCodes.doubleQuote)}"`;
}
}
return symbolName(symbol);
}
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(getNameOfSymbolAsWritten(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 = symbol.escapedName === InternalSymbolName.Default ? InternalSymbolName.Default : getNameOfSymbolAsWritten(symbol);
const firstChar = symbolName.charCodeAt(0);
const needsElementAccess = !isIdentifierStart(firstChar, languageVersion);
if (needsElementAccess) {
if (firstChar !== CharacterCodes.openBracket) {
writePunctuation(writer, SyntaxKind.OpenBracketToken);
}
if (isSingleOrDoubleQuote(firstChar)) {
writer.writeStringLiteral(symbolName);
}
else {
writer.writeSymbol(symbolName, symbol);
}
if (firstChar !== CharacterCodes.openBracket) {
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) {
const params = getTypeParametersOfClassOrInterface(parentSymbol.flags & SymbolFlags.Alias ? resolveAlias(parentSymbol) : parentSymbol);
buildDisplayForTypeArgumentsAndDelimiters(params, (symbol).mapper, writer, enclosingDeclaration);
}
else {
buildTypeParameterDisplayFromSymbol(parentSymbol, writer, enclosingDeclaration);
}
}
appendPropertyOrElementAccessForSymbol(symbol, writer);
}
else {
appendSymbolNameOnly(symbol, writer);
}
parentSymbol = symbol;
}
// Let the writer know we just wrote out a symbol. The declaration emitter writer uses
// this to determine if an import it has previously seen (and not written out) needs
// to be written to the file once the walk of the tree is complete.
//
// NOTE(cyrusn): This approach feels somewhat unfortunate. A simple pass over the tree
// up front (for example, during checking) could determine if we need to emit the imports
// and we could then access that data during declaration emit.
writer.trackSymbol(symbol, enclosingDeclaration, meaning);
/** @param endOfChain Set to false for recursive calls; non-recursive calls should always output something. */
function walkSymbol(symbol: Symbol, meaning: SymbolFlags, endOfChain: boolean): void {
const accessibleSymbolChain = 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 | TypeFormatFlags.WriteClassExpressionAsTypeLiteral);
let inObjectTypeLiteral = false;
return writeType(type, globalFlags);
function writeType(type: Type, flags: TypeFormatFlags) {
const nextFlags = flags & ~TypeFormatFlags.InTypeAlias;
// Write undefined/null type as any
if (type.flags & TypeFlags.Intrinsic) {
// Special handling for unknown / resolving types, they should show up as any and not unknown or __resolving
writer.writeKeyword(!(globalFlags & TypeFormatFlags.WriteOwnNameForAnyLike) && 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 && !(type.flags & TypeFlags.Union)) {
const parent = getParentOfSymbol(type.symbol);
buildSymbolDisplay(parent, writer, enclosingDeclaration, SymbolFlags.Type, SymbolFormatFlags.None, nextFlags);
// In a literal enum type with a single member E { A }, E and E.A denote the
// same type. We always display this type simply as E.
if (getDeclaredTypeOfSymbol(parent) !== type) {
writePunctuation(writer, SyntaxKind.DotToken);
appendSymbolNameOnly(type.symbol, writer);
}
}
else if (getObjectFlags(type) & ObjectFlags.ClassOrInterface || type.flags & (TypeFlags.EnumLike | TypeFlags.TypeParameter)) {
// The specified symbol flags need to be reinterpreted as type flags
buildSymbolDisplay(type.symbol, writer, enclosingDeclaration, SymbolFlags.Type, SymbolFormatFlags.None, nextFlags);
}
else if (!(flags & TypeFormatFlags.InTypeAlias) && type.aliasSymbol &&
((flags & TypeFormatFlags.UseAliasDefinedOutsideCurrentScope) || isTypeSymbolAccessible(type.aliasSymbol, enclosingDeclaration))) {
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.UniqueESSymbol) {
if (flags & TypeFormatFlags.AllowUniqueESSymbolType) {
writeKeyword(writer, SyntaxKind.UniqueKeyword);
writeSpace(writer);
}
else {
writer.reportInaccessibleUniqueSymbolError();
}
writeKeyword(writer, SyntaxKind.SymbolKeyword);
}
else if (type.flags & TypeFlags.StringOrNumberLiteral) {
writer.writeStringLiteral(literalTypeToString(type));
}
else if (type.flags & TypeFlags.Index) {
if (flags & TypeFormatFlags.InElementType) {
writePunctuation(writer, SyntaxKind.OpenParenToken);
}
writer.writeKeyword("keyof");
writeSpace(writer);
writeType((type).type, TypeFormatFlags.InElementType);
if (flags & TypeFormatFlags.InElementType) {
writePunctuation(writer, SyntaxKind.CloseParenToken);
}
}
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 if (type.flags & TypeFlags.Conditional) {
writeType((type).conditionType, TypeFormatFlags.InElementType);
writeSpace(writer);
writePunctuation(writer, SyntaxKind.QuestionToken);
writeSpace(writer);
writeType((type).trueType, TypeFormatFlags.InElementType);
writeSpace(writer);
writePunctuation(writer, SyntaxKind.ColonToken);
writeSpace(writer);
writeType((type).falseType, TypeFormatFlags.InElementType);
}
else if (type.flags & TypeFlags.Extends) {
writeType((type).checkType, TypeFormatFlags.InElementType);
writeSpace(writer);
writer.writeKeyword("extends");
writeSpace(writer);
writeType((type).extendsType, TypeFormatFlags.InElementType);
}
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.escapedName)) {
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 | TypeFormatFlags.InArrayType);
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 if (flags & TypeFormatFlags.WriteClassExpressionAsTypeLiteral &&
type.symbol.valueDeclaration &&
type.symbol.valueDeclaration.kind === SyntaxKind.ClassExpression) {
writeAnonymousType(type, flags);
}
else {
// Write the type reference in the format f.g.C where A and B are type arguments
// for outer type parameters, and f and g are the respective declaring containers of those
// type parameters.
const outerTypeParameters = type.target.outerTypeParameters;
let i = 0;
if (outerTypeParameters) {
const length = outerTypeParameters.length;
while (i < length) {
// Find group of type arguments for type parameters with the same declaring container.
const start = i;
const parent = 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.valueDeclaration.kind === SyntaxKind.ClassExpression && flags & TypeFormatFlags.WriteClassExpressionAsTypeLiteral) ||
symbol.flags & (SymbolFlags.Enum | SymbolFlags.ValueModule)) {
writeTypeOfSymbol(type.symbol, flags);
}
else if (shouldWriteTypeOfFunctionSymbol()) {
writeTypeOfSymbol(type.symbol, 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
// However, in case of class expressions, we want to write both the static side and the instance side.
// We skip adding the static side so that the instance side has a chance to be written
// before checking for circular references.
if (!symbolStack) {
symbolStack = [];
}
const isConstructorObject = type.objectFlags & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & SymbolFlags.Class;
if (isConstructorObject) {
writeLiteralType(type, flags);
}
else {
symbolStack.push(symbol);
writeLiteralType(type, flags);
symbolStack.pop();
}
}
}
else {
// Anonymous types with no symbol are never circular
writeLiteralType(type, flags);
}
function shouldWriteTypeOfFunctionSymbol() {
const isStaticMethodSymbol = !!(symbol.flags & SymbolFlags.Method) && // typeof static method
some(symbol.declarations, declaration => hasModifier(declaration, ModifierFlags.Static));
const isNonLocalFunctionSymbol = !!(symbol.flags & SymbolFlags.Function) &&
(symbol.parent || // is exported function symbol
some(symbol.declarations, declaration =>
declaration.parent.kind === SyntaxKind.SourceFile || declaration.parent.kind === SyntaxKind.ModuleBlock));
if (isStaticMethodSymbol || isNonLocalFunctionSymbol) {
// typeof is allowed only for static/non local functions
return !!(flags & TypeFormatFlags.UseTypeOfFunction) || // use typeof if format flags specify it
contains(symbolStack, symbol); // it is type of the symbol uses itself recursively
}
}
}
function writeTypeOfSymbol(symbol: Symbol, typeFormatFlags?: TypeFormatFlags) {
if (typeFormatFlags & TypeFormatFlags.InArrayType) {
writePunctuation(writer, SyntaxKind.OpenParenToken);
}
writeKeyword(writer, SyntaxKind.TypeOfKeyword);
writeSpace(writer);
buildSymbolDisplay(symbol, writer, enclosingDeclaration, SymbolFlags.Value, SymbolFormatFlags.None, typeFormatFlags);
if (typeFormatFlags & TypeFormatFlags.InArrayType) {
writePunctuation(writer, SyntaxKind.CloseParenToken);
}
}
function writePropertyWithModifiers(prop: Symbol) {
if (isReadonlySymbol(prop)) {
writeKeyword(writer, SyntaxKind.ReadonlyKeyword);
writeSpace(writer);
}
if (getCheckFlags(prop) & CheckFlags.Late) {
const decl = firstOrUndefined(prop.declarations);
const name = hasLateBindableName(decl) && resolveEntityName(decl.name.expression, SymbolFlags.Value);
if (name) {
writer.trackSymbol(name, enclosingDeclaration, SymbolFlags.Value);
}
}
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 (isGenericMappedType(type)) {
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) {
if (globalFlags & TypeFormatFlags.WriteClassExpressionAsTypeLiteral) {
if (p.flags & SymbolFlags.Prototype) {
continue;
}
if (getDeclarationModifierFlagsFromSymbol(p) & (ModifierFlags.Private | ModifierFlags.Protected)) {
writer.reportPrivateInBaseOfClassExpression(symbolName(p));
}
}
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, globalFlags & TypeFormatFlags.WriteClassExpressionAsTypeLiteral);
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 = getOptionalType(type);
}
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: ReadonlyArray, 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: ReadonlyArray, 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: ReadonlyArray, 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);
const typePredicate = getTypePredicateOfSignature(signature);
if (typePredicate) {
buildTypePredicateDisplay(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);
if (info.type) {
buildTypeDisplay(info.type, writer, enclosingDeclaration, globalFlags, symbolStack);
}
else {
writeKeyword(writer, SyntaxKind.AnyKeyword);
}
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 && parent.flags & NodeFlags.Ambient)) {
return isGlobalSourceFile(parent);
}
// Exported members/ambient module elements (exception import declaration) are visible if parent is visible
return isDeclarationVisible(parent);
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.PropertySignature:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
if (hasModifier(node, ModifierFlags.Private | ModifierFlags.Protected)) {
// Private/protected properties/methods are not visible
return false;
}
// Public properties/methods are visible if its parents are visible, so:
// falls through
case SyntaxKind.Constructor:
case SyntaxKind.ConstructSignature:
case SyntaxKind.CallSignature:
case SyntaxKind.IndexSignature:
case SyntaxKind.Parameter:
case SyntaxKind.ModuleBlock:
case SyntaxKind.FunctionType:
case SyntaxKind.ConstructorType:
case SyntaxKind.TypeLiteral:
case SyntaxKind.TypeReference:
case SyntaxKind.ArrayType:
case SyntaxKind.TupleType:
case SyntaxKind.UnionType:
case SyntaxKind.IntersectionType:
case SyntaxKind.ParenthesizedType:
return 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, setVisibility?: boolean): Node[] | undefined {
let exportSymbol: Symbol;
if (node.parent && node.parent.kind === SyntaxKind.ExportAssignment) {
exportSymbol = resolveName(node, node.escapedText, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias, /*nameNotFoundMessage*/ undefined, node, /*isUse*/ false);
}
else if (node.parent.kind === SyntaxKind.ExportSpecifier) {
exportSymbol = getTargetOfExportSpecifier(node.parent, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias);
}
let result: Node[];
if (exportSymbol) {
buildVisibleNodeList(exportSymbol.declarations);
}
return result;
function buildVisibleNodeList(declarations: Declaration[]) {
forEach(declarations, declaration => {
const resultNode = getAnyImportSyntax(declaration) || declaration;
if (setVisibility) {
getNodeLinks(declaration).isVisible = true;
}
else {
result = result || [];
pushIfUnique(result, resultNode);
}
if (isInternalModuleImportEqualsDeclaration(declaration)) {
// Add the referenced top container visible
const internalModuleReference = (declaration).moduleReference;
const firstIdentifier = getFirstIdentifier(internalModuleReference);
const importSymbol = resolveName(declaration, firstIdentifier.escapedText, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace,
undefined, undefined, /*isUse*/ false);
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: BindingElementGrandparent) {
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 = createSymbolTable();
const names = createUnderscoreEscapedMap();
for (const name of properties) {
names.set(getTextOfPropertyName(name), true);
}
for (const prop of getPropertiesOfType(source)) {
const inNamesToRemove = names.has(prop.escapedName);
const isPrivate = getDeclarationModifierFlagsFromSymbol(prop) & (ModifierFlags.Private | ModifierFlags.Protected);
const isSetOnlyAccessor = prop.flags & SymbolFlags.SetAccessor && !(prop.flags & SymbolFlags.GetAccessor);
if (!inNamesToRemove && !isPrivate && !isClassMethod(prop) && !isSetOnlyAccessor) {
members.set(prop.escapedName, 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;
let 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,
// infer from the initializer of the binding element if one is present.
// Otherwise, go with the undefined type of the parent.
if (!parentType) {
return declaration.initializer ? checkDeclarationInitializer(declaration) : parentType;
}
if (isTypeAny(parentType)) {
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;
}
// 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);
// Relax null check on ambient destructuring parameters, since the parameters have no implementation and are just documentation
if (strictNullChecks && declaration.flags & NodeFlags.Ambient && isParameterDeclaration(declaration)) {
parentType = getNonNullableType(parentType);
}
const declaredType = getTypeOfPropertyOfType(parentType, text);
type = declaredType && getFlowTypeOfReference(declaration, declaredType) ||
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, /*allowAsyncIterables*/ 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 as __String)
: 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)], UnionReduction.Subtype) :
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 = true): Type {
return strictNullChecks && optional ? getOptionalType(type) : type;
}
// Return the inferred type for a variable, parameter, or property declaration
function getTypeForVariableLikeDeclaration(declaration: ParameterDeclaration | PropertyDeclaration | PropertySignature | VariableDeclaration | BindingElement, includeOptionality: boolean): Type {
// A variable declared in a for..in statement is of type string, or of type keyof T when the
// right hand expression is of a type parameter type.
if (isVariableDeclaration(declaration) && declaration.parent.parent.kind === SyntaxKind.ForInStatement) {
const indexType = getIndexType(checkNonNullExpression((declaration.parent.parent).expression));
return indexType.flags & (TypeFlags.TypeParameter | TypeFlags.Index) ? indexType : stringType;
}
if (isVariableDeclaration(declaration) && declaration.parent.parent.kind === SyntaxKind.ForOfStatement) {
// checkRightHandSideOfForOf will return undefined if the for-of expression type was
// missing properties/signatures required to get its iteratedType (like
// [Symbol.iterator] or next). This may be because we accessed properties from anyType,
// or it may have led to an error inside getElementTypeOfIterable.
const forOfStatement = declaration.parent.parent;
return checkRightHandSideOfForOf(forOfStatement.expression, forOfStatement.awaitModifier) || anyType;
}
if (isBindingPattern(declaration.parent)) {
return getTypeForBindingElement(declaration);
}
const isOptional = !isBindingElement(declaration) && !isVariableDeclaration(declaration) && !!declaration.questionToken && includeOptionality;
// Use type from type annotation if one is present
const declaredType = tryGetTypeFromEffectiveTypeNode(declaration);
if (declaredType) {
return addOptionality(declaredType, isOptional);
}
if ((noImplicitAny || isInJavaScriptFile(declaration)) &&
declaration.kind === SyntaxKind.VariableDeclaration && !isBindingPattern(declaration.name) &&
!(getCombinedModifierFlags(declaration) & ModifierFlags.Export) && !(declaration.flags & NodeFlags.Ambient)) {
// If --noImplicitAny is on or the declaration is in a Javascript file,
// use control flow tracked 'any' type for non-ambient, non-exported var or let variables with no
// initializer or a 'null' or 'undefined' initializer.
if (!(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 && !hasNonBindableDynamicName(func)) {
const getter = getDeclarationOfKind(getSymbolOfNode(declaration.parent), SyntaxKind.GetAccessor);
if (getter) {
const getterSignature = getSignatureFromDeclaration(getter);
const thisParameter = getAccessorThisParameter(func as AccessorDeclaration);
if (thisParameter && declaration === thisParameter) {
// Use the type from the *getter*
Debug.assert(!thisParameter.type);
return getTypeOfSymbol(getterSignature.thisParameter);
}
return getReturnTypeOfSignature(getterSignature);
}
}
// Use contextual parameter type if one is available
let type: Type;
if (declaration.symbol.escapedName === "this") {
type = getContextualThisParameterType(func);
}
else {
type = getContextuallyTypedParameterType(declaration);
}
if (type) {
return addOptionality(type, isOptional);
}
}
// Use the type of the initializer expression if one is present
if (declaration.initializer) {
const type = checkDeclarationInitializer(declaration);
return addOptionality(type, isOptional);
}
if (isJsxAttribute(declaration)) {
// if JSX attribute doesn't have initializer, by default the attribute will have boolean value of true.
// I.e is sugar for
return trueType;
}
// If the declaration specifies a binding pattern, 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) &&
!(symbol.flags & SymbolFlags.JSContainer)) {
errorNextVariableOrPropertyDeclarationMustHaveSameType(jsDocType, declaration, 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, UnionReduction.Subtype);
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 = createSymbolTable();
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.escapedName, 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: ParameterDeclaration | PropertyDeclaration | PropertySignature | VariableDeclaration | BindingElement, reportErrors?: boolean): Type {
let type = getTypeForVariableLikeDeclaration(declaration, /*includeOptionality*/ true);
if (type) {
if (reportErrors) {
reportErrorsFromWidening(declaration, type);
}
// always widen a 'unique symbol' type if the type was created for a different declaration.
if (type.flags & TypeFlags.UniqueESSymbol && (isBindingElement(declaration) || !declaration.type) && type.symbol !== getSymbolOfNode(declaration)) {
type = esSymbolType;
}
return getWidenedType(type);
}
// Rest parameters default to type any[], other parameters default to type any
type = isParameter(declaration) && declaration.dotDotDotToken ? anyArrayType : anyType;
// Report implicit any errors unless this is a private property within an ambient declaration
if (reportErrors && 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 tryGetTypeFromEffectiveTypeNode(declaration: Declaration) {
const typeNode = getEffectiveTypeAnnotationNode(declaration);
if (typeNode) {
return getTypeFromTypeNode(typeNode);
}
}
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 (isInJavaScriptFile(declaration) && isJSDocPropertyLikeTag(declaration) && 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 if (isJSDocPropertyTag(declaration)
|| isPropertyAccessExpression(declaration)
|| isIdentifier(declaration)
|| (isMethodDeclaration(declaration) && !isObjectLiteralMethod(declaration))
|| isMethodSignature(declaration)) {
// Symbol is property of some kind that is merged with something - should use `getTypeOfFuncClassEnumModule` and not `getTypeOfVariableOrParameterOrProperty`
if (symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.Enum | SymbolFlags.ValueModule)) {
return getTypeOfFuncClassEnumModule(symbol);
}
type = tryGetTypeFromEffectiveTypeNode(declaration) || anyType;
}
else if (isPropertyAssignment(declaration)) {
type = tryGetTypeFromEffectiveTypeNode(declaration) || checkPropertyAssignment(declaration);
}
else if (isJsxAttribute(declaration)) {
type = tryGetTypeFromEffectiveTypeNode(declaration) || checkJsxAttribute(declaration);
}
else if (isShorthandPropertyAssignment(declaration)) {
type = tryGetTypeFromEffectiveTypeNode(declaration) || checkExpressionForMutableLocation(declaration.name, CheckMode.Normal);
}
else if (isObjectLiteralMethod(declaration)) {
type = tryGetTypeFromEffectiveTypeNode(declaration) || checkObjectLiteralMethod(declaration, CheckMode.Normal);
}
else if (isParameter(declaration)
|| isPropertyDeclaration(declaration)
|| isPropertySignature(declaration)
|| isVariableDeclaration(declaration)
|| isBindingElement(declaration)) {
type = getWidenedTypeForVariableLikeDeclaration(declaration, /*reportErrors*/ true);
}
else {
Debug.fail("Unhandled declaration kind! " + (ts as any).SyntaxKind[declaration.kind]);
}
if (!popTypeResolution()) {
type = reportCircularityError(symbol);
}
links.type = type;
}
return links.type;
}
function getAnnotatedAccessorType(accessor: AccessorDeclaration): Type {
if (accessor) {
if (accessor.kind === SyntaxKind.GetAccessor) {
const getterTypeAnnotation = getEffectiveReturnTypeNode(accessor);
return getterTypeAnnotation && getTypeFromTypeNode(getterTypeAnnotation);
}
else {
const setterTypeAnnotation = getEffectiveSetAccessorTypeAnnotationNode(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 && isInJavaScriptFile(getter)) {
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 ? getOptionalType(type) : 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 (getEffectiveTypeAnnotationNode(symbol.valueDeclaration)) {
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: ReadonlyArray): TypeParameter[] {
for (const declaration of declarations) {
const tp = getDeclaredTypeOfTypeParameter(getSymbolOfNode(declaration));
typeParameters = appendIfUnique(typeParameters, tp);
}
return typeParameters;
}
// Return the outer type parameters of a node or undefined if the node has no outer type parameters.
function getOuterTypeParameters(node: Node, includeThisTypes?: boolean): TypeParameter[] {
while (true) {
node = node.parent;
if (!node) {
return undefined;
}
switch (node.kind) {
case SyntaxKind.ClassDeclaration:
case SyntaxKind.ClassExpression:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.CallSignature:
case SyntaxKind.ConstructSignature:
case SyntaxKind.MethodSignature:
case SyntaxKind.FunctionType:
case SyntaxKind.ConstructorType:
case SyntaxKind.JSDocFunctionType:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
case SyntaxKind.TypeAliasDeclaration:
case SyntaxKind.JSDocTemplateTag:
case SyntaxKind.MappedType:
const outerTypeParameters = getOuterTypeParameters(node, includeThisTypes);
if (node.kind === SyntaxKind.MappedType) {
return append(outerTypeParameters, getDeclaredTypeOfTypeParameter(getSymbolOfNode((node).typeParameter)));
}
const outerAndOwnTypeParameters = appendTypeParameters(outerTypeParameters, getEffectiveTypeParameterDeclarations(node) || emptyArray);
const thisType = includeThisTypes &&
(node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.ClassExpression || node.kind === SyntaxKind.InterfaceDeclaration) &&
getDeclaredTypeOfClassOrInterface(getSymbolOfNode(node)).thisType;
return thisType ? append(outerAndOwnTypeParameters, thisType) : outerAndOwnTypeParameters;
}
}
}
// 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 getOuterTypeParameters(declaration);
}
// The local type parameters are the combined set of type parameters from all declarations of the class,
// interface, or type alias.
function getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol: Symbol): TypeParameter[] {
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 {
const decl = type.symbol.valueDeclaration;
if (isInJavaScriptFile(decl)) {
// Prefer an @augments tag because it may have type parameters.
const tag = getJSDocAugmentsTag(decl);
if (tag) {
return tag.class;
}
}
return getClassExtendsHeritageClauseElement(decl);
}
function getConstructorsForTypeArguments(type: Type, typeArgumentNodes: ReadonlyArray, 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: ReadonlyArray, location: Node): Signature[] {
const signatures = getConstructorsForTypeArguments(type, typeArgumentNodes, location);
const typeArguments = map(typeArgumentNodes, getTypeFromTypeNode);
return sameMap(signatures, sig => some(sig.typeParameters) ? getSignatureInstantiation(sig, typeArguments, isInJavaScriptFile(location)) : sig);
}
/**
* The base constructor of a class can resolve to
* * undefinedType if the class has no extends clause,
* * unknownType if an error occurred during resolution of the extends expression,
* * nullType if the extends expression is the null value,
* * anyType if the extends expression has type any, or
* * an object type with at least one construct signature.
*/
function getBaseConstructorTypeOfClass(type: InterfaceType): Type {
if (!type.resolvedBaseConstructorType) {
const decl = type.symbol.valueDeclaration;
const extended = getClassExtendsHeritageClauseElement(decl);
const baseTypeNode = getBaseTypeNodeOfClass(type);
if (!baseTypeNode) {
return type.resolvedBaseConstructorType = undefinedType;
}
if (!pushTypeResolution(type, TypeSystemPropertyName.ResolvedBaseConstructorType)) {
return unknownType;
}
const baseConstructorType = checkExpression(baseTypeNode.expression);
if (extended && baseTypeNode !== extended) {
Debug.assert(!extended.typeArguments); // Because this is in a JS file, and baseTypeNode is in an @extends tag
checkExpression(extended.expression);
}
if (baseConstructorType.flags & (TypeFlags.Object | TypeFlags.Intersection)) {
// Resolving the members of a class requires us to resolve the base class of that class.
// We force resolution here such that we catch circularities now.
resolveStructuredTypeMembers(baseConstructorType);
}
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) {
type.resolvedBaseTypes = resolvingEmptyArray;
const baseConstructorType = getApparentType(getBaseConstructorTypeOfClass(type));
if (!(baseConstructorType.flags & (TypeFlags.Object | TypeFlags.Intersection | TypeFlags.Any))) {
return type.resolvedBaseTypes = emptyArray;
}
const baseTypeNode = getBaseTypeNodeOfClass(type);
const typeArgs = typeArgumentsFromTypeReferenceNode(baseTypeNode);
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, typeArgs);
}
else if (baseConstructorType.flags & TypeFlags.Any) {
baseType = baseConstructorType;
}
else {
// The class derives from a "class-like" constructor function, check that we have at least one construct signature
// with a matching number of type parameters and use the return type of the first instantiated signature. Elsewhere
// we check that all instantiated signatures return the same type.
const constructors = getInstantiatedConstructorsForTypeArguments(baseConstructorType, baseTypeNode.typeArguments, baseTypeNode);
if (!constructors.length) {
error(baseTypeNode.expression, Diagnostics.No_base_constructor_has_the_specified_number_of_type_arguments);
return type.resolvedBaseTypes = emptyArray;
}
baseType = getReturnTypeOfSignature(constructors[0]);
}
if (baseType === unknownType) {
return type.resolvedBaseTypes = emptyArray;
}
if (!isValidBaseType(baseType)) {
error(baseTypeNode.expression, Diagnostics.Base_constructor_return_type_0_is_not_a_class_or_interface_type, typeToString(baseType));
return type.resolvedBaseTypes = emptyArray;
}
if (type === baseType || hasBaseType(baseType, type)) {
error(type.symbol.valueDeclaration, Diagnostics.Type_0_recursively_references_itself_as_a_base_type,
typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType));
return type.resolvedBaseTypes = emptyArray;
}
if (type.resolvedBaseTypes === resolvingEmptyArray) {
// Circular reference, likely through instantiation of default parameters
// (otherwise there'd be an error from hasBaseType) - this is fine, but `.members` should be reset
// as `getIndexedAccessType` via `instantiateType` via `getTypeFromClassOrInterfaceReference` forces a
// partial instantiation of the members without the base types fully resolved
(type as Type as ResolvedType).members = undefined;
}
return type.resolvedBaseTypes = [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): type is BaseType {
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 isThislessInterface(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 || !isThislessInterface(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;
}
const declaration = find(symbol.declarations, d =>
d.kind === SyntaxKind.JSDocTypedefTag || d.kind === SyntaxKind.TypeAliasDeclaration);
const typeNode = declaration.kind === SyntaxKind.JSDocTypedefTag ? declaration.typeExpression : declaration.type;
// If typeNode is missing, we will error in checkJSDocTypedefTag.
let type = typeNode ? getTypeFromTypeNode(typeNode) : unknownType;
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(member: EnumMember) {
const expr = member.initializer;
if (!expr) {
return !(member.flags & NodeFlags.Ambient);
}
switch (expr.kind) {
case SyntaxKind.StringLiteral:
case SyntaxKind.NumericLiteral:
return true;
case SyntaxKind.PrefixUnaryExpression:
return (expr).operator === SyntaxKind.MinusToken &&
(expr).operand.kind === SyntaxKind.NumericLiteral;
case SyntaxKind.Identifier:
return nodeIsMissing(expr) || !!getSymbolOfNode(member.parent).exports.get((expr).escapedText);
default:
return false;
}
}
function getEnumKind(symbol: Symbol): EnumKind {
const links = getSymbolLinks(symbol);
if (links.enumKind !== undefined) {
return links.enumKind;
}
let hasNonLiteralMember = false;
for (const declaration of symbol.declarations) {
if (declaration.kind === SyntaxKind.EnumDeclaration) {
for (const member of (declaration).members) {
if (member.initializer && member.initializer.kind === SyntaxKind.StringLiteral) {
return links.enumKind = EnumKind.Literal;
}
if (!isLiteralEnumMember(member)) {
hasNonLiteralMember = true;
}
}
}
}
return links.enumKind = hasNonLiteralMember ? EnumKind.Numeric : EnumKind.Literal;
}
function getBaseTypeOfEnumLiteralType(type: Type) {
return type.flags & TypeFlags.EnumLiteral && !(type.flags & TypeFlags.Union) ? getDeclaredTypeOfSymbol(getParentOfSymbol(type.symbol)) : type;
}
function getDeclaredTypeOfEnum(symbol: Symbol): Type {
const links = getSymbolLinks(symbol);
if (links.declaredType) {
return links.declaredType;
}
if (getEnumKind(symbol) === EnumKind.Literal) {
enumCount++;
const memberTypeList: Type[] = [];
for (const declaration of symbol.declarations) {
if (declaration.kind === SyntaxKind.EnumDeclaration) {
for (const member of (declaration).members) {
const memberType = getLiteralType(getEnumMemberValue(member), enumCount, getSymbolOfNode(member));
getSymbolLinks(getSymbolOfNode(member)).declaredType = memberType;
memberTypeList.push(memberType);
}
}
}
if (memberTypeList.length) {
const enumType = getUnionType(memberTypeList, UnionReduction.Literal, symbol, /*aliasTypeArguments*/ undefined);
if (enumType.flags & TypeFlags.Union) {
enumType.flags |= TypeFlags.EnumLiteral;
enumType.symbol = symbol;
}
return links.declaredType = enumType;
}
}
const enumType = createType(TypeFlags.Enum);
enumType.symbol = symbol;
return links.declaredType = enumType;
}
function getDeclaredTypeOfEnumMember(symbol: Symbol): Type {
const links = getSymbolLinks(symbol);
if (!links.declaredType) {
const enumType = getDeclaredTypeOfEnum(getParentOfSymbol(symbol));
if (!links.declaredType) {
links.declaredType = enumType;
}
}
return links.declaredType;
}
function getDeclaredTypeOfTypeParameter(symbol: Symbol): TypeParameter {
const links = getSymbolLinks(symbol);
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 {
return tryGetDeclaredTypeOfSymbol(symbol) || unknownType;
}
function tryGetDeclaredTypeOfSymbol(symbol: Symbol): Type | undefined {
if (symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) {
return getDeclaredTypeOfClassOrInterface(symbol);
}
if (symbol.flags & SymbolFlags.TypeAlias) {
return getDeclaredTypeOfTypeAlias(symbol);
}
if (symbol.flags & SymbolFlags.TypeParameter) {
return getDeclaredTypeOfTypeParameter(symbol);
}
if (symbol.flags & SymbolFlags.Enum) {
return getDeclaredTypeOfEnum(symbol);
}
if (symbol.flags & SymbolFlags.EnumMember) {
return getDeclaredTypeOfEnumMember(symbol);
}
if (symbol.flags & SymbolFlags.Alias) {
return getDeclaredTypeOfAlias(symbol);
}
return undefined;
}
/**
* A type is free of this references if it's the any, string, number, boolean, symbol, or void keyword, a string
* literal type, an array with an element type that is free of this references, or a type reference that is
* free of this references.
*/
function isThislessType(node: TypeNode): boolean {
switch (node.kind) {
case SyntaxKind.AnyKeyword:
case SyntaxKind.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 isThislessType((node).elementType);
case SyntaxKind.TypeReference:
return !(node as TypeReferenceNode).typeArguments || (node as TypeReferenceNode).typeArguments.every(isThislessType);
}
return false;
}
/** A type parameter is thisless if its contraint is thisless, or if it has no constraint. */
function isThislessTypeParameter(node: TypeParameterDeclaration) {
return !node.constraint || isThislessType(node.constraint);
}
/**
* A variable-like declaration is free of this references if it has a type annotation
* that is thisless, or if it has no type annotation and no initializer (and is thus of type any).
*/
function isThislessVariableLikeDeclaration(node: VariableLikeDeclaration): boolean {
const typeNode = getEffectiveTypeAnnotationNode(node);
return typeNode ? isThislessType(typeNode) : !hasInitializer(node);
}
/**
* A function-like declaration is considered free of `this` references if it has a return type
* annotation that is free of this references and if each parameter is thisless and if
* each type parameter (if present) is thisless.
*/
function isThislessFunctionLikeDeclaration(node: FunctionLikeDeclaration): boolean {
const returnType = getEffectiveReturnTypeNode(node);
return (node.kind === SyntaxKind.Constructor || (returnType && isThislessType(returnType))) &&
node.parameters.every(isThislessVariableLikeDeclaration) &&
(!node.typeParameters || node.typeParameters.every(isThislessTypeParameter));
}
/**
* Returns true if the class or interface member given by the symbol is free of "this" references. The
* function may return false for symbols that are actually free of "this" references because it is not
* feasible to perform a complete analysis in all cases. In particular, property members with types
* inferred from their initializers and function members with inferred return types are conservatively
* assumed not to be free of "this" references.
*/
function isThisless(symbol: Symbol): boolean {
if (symbol.declarations && symbol.declarations.length === 1) {
const declaration = symbol.declarations[0];
if (declaration) {
switch (declaration.kind) {
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.PropertySignature:
return isThislessVariableLikeDeclaration(declaration);
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
case SyntaxKind.Constructor:
return isThislessFunctionLikeDeclaration(declaration);
}
}
}
return false;
}
// The mappingThisOnly flag indicates that the only type parameter being mapped is "this". When the flag is true,
// we check symbols to see if we can quickly conclude they are free of "this" references, thus needing no instantiation.
function createInstantiatedSymbolTable(symbols: Symbol[], mapper: TypeMapper, mappingThisOnly: boolean): SymbolTable {
const result = createSymbolTable();
for (const symbol of symbols) {
result.set(symbol.escapedName, mappingThisOnly && isThisless(symbol) ? symbol : instantiateSymbol(symbol, mapper));
}
return result;
}
function addInheritedMembers(symbols: SymbolTable, baseSymbols: Symbol[]) {
for (const s of baseSymbols) {
if (!symbols.has(s.escapedName)) {
symbols.set(s.escapedName, s);
}
}
}
function resolveDeclaredMembers(type: InterfaceType): InterfaceTypeWithDeclaredMembers {
if (!(type).declaredProperties) {
const symbol = type.symbol;
const members = getMembersOfSymbol(symbol);
(type).declaredProperties = getNamedMembers(members);
(type).declaredCallSignatures = getSignaturesOfSymbol(members.get(InternalSymbolName.Call));
(type).declaredConstructSignatures = getSignaturesOfSymbol(members.get(InternalSymbolName.New));
(type).declaredStringIndexInfo = getIndexInfoOfSymbol(symbol, IndexKind.String);
(type).declaredNumberIndexInfo = getIndexInfoOfSymbol(symbol, IndexKind.Number);
}
return type;
}
/**
* Indicates whether a type can be used as a late-bound name.
*/
function isTypeUsableAsLateBoundName(type: Type): type is LiteralType | UniqueESSymbolType {
return !!(type.flags & TypeFlags.StringOrNumberLiteralOrUnique);
}
/**
* Indicates whether a declaration name is definitely late-bindable.
* A declaration name is only late-bindable if:
* - It is a `ComputedPropertyName`.
* - Its expression is an `Identifier` or either a `PropertyAccessExpression` an
* `ElementAccessExpression` consisting only of these same three types of nodes.
* - The type of its expression is a string or numeric literal type, or is a `unique symbol` type.
*/
function isLateBindableName(node: DeclarationName): node is LateBoundName {
return isComputedPropertyName(node)
&& isEntityNameExpression(node.expression)
&& isTypeUsableAsLateBoundName(checkComputedPropertyName(node));
}
/**
* Indicates whether a declaration has a late-bindable dynamic name.
*/
function hasLateBindableName(node: Declaration): node is LateBoundDeclaration {
const name = getNameOfDeclaration(node);
return name && isLateBindableName(name);
}
/**
* Indicates whether a declaration has a dynamic name that cannot be late-bound.
*/
function hasNonBindableDynamicName(node: Declaration) {
return hasDynamicName(node) && !hasLateBindableName(node);
}
/**
* Indicates whether a declaration name is a dynamic name that cannot be late-bound.
*/
function isNonBindableDynamicName(node: DeclarationName) {
return isDynamicName(node) && !isLateBindableName(node);
}
/**
* Gets the symbolic name for a late-bound member from its type.
*/
function getLateBoundNameFromType(type: LiteralType | UniqueESSymbolType) {
if (type.flags & TypeFlags.UniqueESSymbol) {
return `__@${type.symbol.escapedName}@${getSymbolId(type.symbol)}` as __String;
}
if (type.flags & TypeFlags.StringOrNumberLiteral) {
return escapeLeadingUnderscores("" + (type).value);
}
}
/**
* Adds a declaration to a late-bound dynamic member. This performs the same function for
* late-bound members that `addDeclarationToSymbol` in binder.ts performs for early-bound
* members.
*/
function addDeclarationToLateBoundSymbol(symbol: Symbol, member: LateBoundDeclaration, symbolFlags: SymbolFlags) {
Debug.assert(!!(getCheckFlags(symbol) & CheckFlags.Late), "Expected a late-bound symbol.");
symbol.flags |= symbolFlags;
getSymbolLinks(member.symbol).lateSymbol = symbol;
if (!symbol.declarations) {
symbol.declarations = [member];
}
else {
symbol.declarations.push(member);
}
if (symbolFlags & SymbolFlags.Value) {
const valueDeclaration = symbol.valueDeclaration;
if (!valueDeclaration || valueDeclaration.kind !== member.kind) {
symbol.valueDeclaration = member;
}
}
}
/**
* Performs late-binding of a dynamic member. This performs the same function for
* late-bound members that `declareSymbol` in binder.ts performs for early-bound
* members.
*
* If a symbol is a dynamic name from a computed property, we perform an additional "late"
* binding phase to attempt to resolve the name for the symbol from the type of the computed
* property's expression. If the type of the expression is a string-literal, numeric-literal,
* or unique symbol type, we can use that type as the name of the symbol.
*
* For example, given:
*
* const x = Symbol();
*
* interface I {
* [x]: number;
* }
*
* The binder gives the property `[x]: number` a special symbol with the name "__computed".
* In the late-binding phase we can type-check the expression `x` and see that it has a
* unique symbol type which we can then use as the name of the member. This allows users
* to define custom symbols that can be used in the members of an object type.
*
* @param parent The containing symbol for the member.
* @param earlySymbols The early-bound symbols of the parent.
* @param lateSymbols The late-bound symbols of the parent.
* @param decl The member to bind.
*/
function lateBindMember(parent: Symbol, earlySymbols: SymbolTable | undefined, lateSymbols: SymbolTable, decl: LateBoundDeclaration) {
Debug.assert(!!decl.symbol, "The member is expected to have a symbol.");
const links = getNodeLinks(decl);
if (!links.resolvedSymbol) {
// In the event we attempt to resolve the late-bound name of this member recursively,
// fall back to the early-bound name of this member.
links.resolvedSymbol = decl.symbol;
const type = checkComputedPropertyName(decl.name);
if (isTypeUsableAsLateBoundName(type)) {
const memberName = getLateBoundNameFromType(type);
const symbolFlags = decl.symbol.flags;
// Get or add a late-bound symbol for the member. This allows us to merge late-bound accessor declarations.
let lateSymbol = lateSymbols.get(memberName);
if (!lateSymbol) lateSymbols.set(memberName, lateSymbol = createSymbol(SymbolFlags.None, memberName, CheckFlags.Late));
// Report an error if a late-bound member has the same name as an early-bound member,
// or if we have another early-bound symbol declaration with the same name and
// conflicting flags.
const earlySymbol = earlySymbols && earlySymbols.get(memberName);
if (lateSymbol.flags & getExcludedSymbolFlags(symbolFlags) || earlySymbol) {
// If we have an existing early-bound member, combine its declarations so that we can
// report an error at each declaration.
const declarations = earlySymbol ? concatenate(earlySymbol.declarations, lateSymbol.declarations) : lateSymbol.declarations;
const name = declarationNameToString(decl.name);
forEach(declarations, declaration => error(getNameOfDeclaration(declaration) || declaration, Diagnostics.Duplicate_declaration_0, name));
error(decl.name || decl, Diagnostics.Duplicate_declaration_0, name);
lateSymbol = createSymbol(SymbolFlags.None, memberName, CheckFlags.Late);
}
addDeclarationToLateBoundSymbol(lateSymbol, decl, symbolFlags);
lateSymbol.parent = parent;
return links.resolvedSymbol = lateSymbol;
}
}
return links.resolvedSymbol;
}
function getResolvedMembersOrExportsOfSymbol(symbol: Symbol, resolutionKind: MembersOrExportsResolutionKind) {
const links = getSymbolLinks(symbol);
if (!links[resolutionKind]) {
const isStatic = resolutionKind === MembersOrExportsResolutionKind.resolvedExports;
const earlySymbols = !isStatic ? symbol.members :
symbol.flags & SymbolFlags.Module ? getExportsOfModuleWorker(symbol) :
symbol.exports;
// In the event we recursively resolve the members/exports of the symbol, we
// set the initial value of resolvedMembers/resolvedExports to the early-bound
// members/exports of the symbol.
links[resolutionKind] = earlySymbols || emptySymbols;
// fill in any as-yet-unresolved late-bound members.
const lateSymbols = createSymbolTable();
for (const decl of symbol.declarations) {
const members = getMembersOfDeclaration(decl);
if (members) {
for (const member of members) {
if (isStatic === hasStaticModifier(member) && hasLateBindableName(member)) {
lateBindMember(symbol, earlySymbols, lateSymbols, member);
}
}
}
}
links[resolutionKind] = combineSymbolTables(earlySymbols, lateSymbols) || emptySymbols;
}
return links[resolutionKind];
}
/**
* Gets a SymbolTable containing both the early- and late-bound members of a symbol.
*
* For a description of late-binding, see `lateBindMember`.
*/
function getMembersOfSymbol(symbol: Symbol) {
return symbol.flags & SymbolFlags.LateBindingContainer
? getResolvedMembersOrExportsOfSymbol(symbol, MembersOrExportsResolutionKind.resolvedMembers)
: symbol.members || emptySymbols;
}
/**
* If a symbol is the dynamic name of the member of an object type, get the late-bound
* symbol of the member.
*
* For a description of late-binding, see `lateBindMember`.
*/
function getLateBoundSymbol(symbol: Symbol): Symbol {
if (symbol.flags & SymbolFlags.ClassMember && symbol.escapedName === InternalSymbolName.Computed) {
const links = getSymbolLinks(symbol);
if (!links.lateSymbol && some(symbol.declarations, hasLateBindableName)) {
// force late binding of members/exports. This will set the late-bound symbol
if (some(symbol.declarations, hasStaticModifier)) {
getExportsOfSymbol(symbol.parent);
}
else {
getMembersOfSymbol(symbol.parent);
}
}
return links.lateSymbol || (links.lateSymbol = symbol);
}
return symbol;
}
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 ? getMembersOfSymbol(source.symbol) : 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 === getMembersOfSymbol(source.symbol)) {
members = createSymbolTable(source.declaredProperties);
}
setStructuredTypeMembers(type, members, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo);
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 | undefined,
resolvedTypePredicate: TypePredicate | undefined,
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.resolvedTypePredicate = resolvedTypePredicate;
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, /*resolvedReturnType*/ undefined,
/*resolvedTypePredicate*/ undefined, 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, /*resolvedTypePredicate*/ 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, isJavaScript)) : 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;
}
result = appendIfUnique(result, match);
}
return result;
}
// The signatures of a union type are those signatures that are present in each of the constituent types.
// Generic signatures must match exactly, but non-generic signatures are allowed to have extra optional
// parameters and may differ in return types. When signatures differ in return types, the resulting return
// type is the union of the constituent return types.
function getUnionSignatures(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) {
let thisParameter = signature.thisParameter;
if (forEach(unionSignatures, sig => sig.thisParameter)) {
const thisType = getUnionType(map(unionSignatures, sig => sig.thisParameter ? getTypeOfSymbol(sig.thisParameter) : anyType), UnionReduction.Subtype);
thisParameter = createSymbolWithType(signature.thisParameter, thisType);
}
s = cloneSignature(signature);
s.thisParameter = thisParameter;
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, UnionReduction.Subtype), 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 = getMembersOfSymbol(symbol);
const callSignatures = getSignaturesOfSymbol(members.get(InternalSymbolName.Call));
const constructSignatures = getSignaturesOfSymbol(members.get(InternalSymbolName.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 stringIndexInfo: IndexInfo = undefined;
if (symbol.exports) {
members = getExportsOfSymbol(symbol);
}
setStructuredTypeMembers(type, members, emptyArray, emptyArray, undefined, undefined);
if (symbol.flags & SymbolFlags.Class) {
const classType = getDeclaredTypeOfClassOrInterface(symbol);
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, emptyArray, 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);
}
// And likewise for construct signatures for classes
if (symbol.flags & SymbolFlags.Class) {
const classType = getDeclaredTypeOfClassOrInterface(symbol);
let constructSignatures = getSignaturesOfSymbol(symbol.members.get(InternalSymbolName.Constructor));
if (!constructSignatures.length) {
constructSignatures = getDefaultConstructSignatures(classType);
}
(type).constructSignatures = constructSignatures;
}
}
}
/** Resolve the members of a mapped type { [P in K]: T } */
function resolveMappedTypeMembers(type: MappedType) {
const members: SymbolTable = createSymbolTable();
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;
const constraintDeclaration = type.declaration.typeParameter.constraint;
if (constraintDeclaration.kind === SyntaxKind.TypeOperator &&
(constraintDeclaration).operator === SyntaxKind.KeyOfKeyword) {
// We have a { [P in keyof T]: X }
for (const propertySymbol of getPropertiesOfType(modifiersType)) {
addMemberForKeyType(getLiteralTypeFromPropertyName(propertySymbol), propertySymbol);
}
if (modifiersType.flags & TypeFlags.Any || 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.InstantiableNonPrimitive ? 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, propertySymbolOrIndex?: Symbol | number) {
let propertySymbol: Symbol;
// forEachType delegates to forEach, which calls with a numeric second argument
// the type system currently doesn't catch this incompatibility, so we annotate
// the function ourselves to indicate the runtime behavior and deal with it here
if (typeof propertySymbolOrIndex === "object") {
propertySymbol = propertySymbolOrIndex;
}
// 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 = escapeLeadingUnderscores((t).value);
const modifiersProp = getPropertyOfType(modifiersType, propName);
const isOptional = templateOptional || !!(modifiersProp && modifiersProp.flags & SymbolFlags.Optional);
const checkFlags = templateReadonly || modifiersProp && isReadonlySymbol(modifiersProp) ? CheckFlags.Readonly : 0;
const prop = createSymbol(SymbolFlags.Property | (isOptional ? SymbolFlags.Optional : 0), propName, checkFlags);
prop.type = propType;
if (propertySymbol) {
prop.syntheticOrigin = propertySymbol;
prop.declarations = propertySymbol.declarations;
}
prop.syntheticLiteralTypeOrigin = t as StringLiteralType;
members.set(propName, prop);
}
else if (t.flags & (TypeFlags.Any | 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 &&
(constraintDeclaration).operator === SyntaxKind.KeyOfKeyword) {
// 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 getMappedTypeModifiers(type: MappedType): MappedTypeModifiers {
return (type.declaration.readonlyToken ? MappedTypeModifiers.Readonly : 0) |
(type.declaration.questionToken ? MappedTypeModifiers.Optional : 0);
}
function getCombinedMappedTypeModifiers(type: MappedType): MappedTypeModifiers {
const modifiersType = getModifiersTypeFromMappedType(type);
return getMappedTypeModifiers(type) |
(isGenericMappedType(modifiersType) ? getMappedTypeModifiers(modifiersType) : 0);
}
function isPartialMappedType(type: Type) {
return getObjectFlags(type) & ObjectFlags.Mapped && !!(type).declaration.questionToken;
}
function isGenericMappedType(type: Type) {
return getObjectFlags(type) & ObjectFlags.Mapped && isGenericIndexType(getConstraintTypeFromMappedType(type));
}
function resolveStructuredTypeMembers(type: StructuredType): ResolvedType {
if (!(type).members) {
if (type.flags & TypeFlags.Object) {
if ((