///
/* @internal */
namespace ts {
let nextSymbolId = 1;
let nextNodeId = 1;
let nextMergeId = 1;
export function getNodeId(node: Node): number {
if (!node.id) node.id = nextNodeId++;
return node.id;
}
export let checkTime = 0;
export function getSymbolId(symbol: Symbol): number {
if (!symbol.id) {
symbol.id = nextSymbolId++;
}
return symbol.id;
}
export function createTypeChecker(host: TypeCheckerHost, produceDiagnostics: boolean): TypeChecker {
// Cancellation that controls whether or not we can cancel in the middle of type checking.
// In general cancelling is *not* safe for the type checker. We might be in the middle of
// computing something, and we will leave our internals in an inconsistent state. Callers
// who set the cancellation token should catch if a cancellation exception occurs, and
// should throw away and create a new TypeChecker.
//
// Currently we only support setting the cancellation token when getting diagnostics. This
// is because diagnostics can be quite expensive, and we want to allow hosts to bail out if
// they no longer need the information (for example, if the user started editing again).
let cancellationToken: CancellationToken;
let Symbol = objectAllocator.getSymbolConstructor();
let Type = objectAllocator.getTypeConstructor();
let Signature = objectAllocator.getSignatureConstructor();
let typeCount = 0;
let emptyArray: any[] = [];
let emptySymbols: SymbolTable = {};
let compilerOptions = host.getCompilerOptions();
let languageVersion = compilerOptions.target || ScriptTarget.ES3;
let emitResolver = createResolver();
let undefinedSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "undefined");
let argumentsSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "arguments");
let checker: TypeChecker = {
getNodeCount: () => sum(host.getSourceFiles(), "nodeCount"),
getIdentifierCount: () => sum(host.getSourceFiles(), "identifierCount"),
getSymbolCount: () => sum(host.getSourceFiles(), "symbolCount"),
getTypeCount: () => typeCount,
isUndefinedSymbol: symbol => symbol === undefinedSymbol,
isArgumentsSymbol: symbol => symbol === argumentsSymbol,
getDiagnostics,
getGlobalDiagnostics,
getTypeOfSymbolAtLocation,
getDeclaredTypeOfSymbol,
getPropertiesOfType,
getPropertyOfType,
getSignaturesOfType,
getIndexTypeOfType,
getReturnTypeOfSignature,
getSymbolsInScope,
getSymbolAtLocation,
getShorthandAssignmentValueSymbol,
getTypeAtLocation,
typeToString,
getSymbolDisplayBuilder,
symbolToString,
getAugmentedPropertiesOfType,
getRootSymbols,
getContextualType,
getFullyQualifiedName,
getResolvedSignature,
getConstantValue,
isValidPropertyAccess,
getSignatureFromDeclaration,
isImplementationOfOverload,
getAliasedSymbol: resolveAlias,
getEmitResolver,
getExportsOfModule: getExportsOfModuleAsArray,
getJsxElementAttributesType,
getJsxIntrinsicTagNames
};
let unknownSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "unknown");
let resolvingSymbol = createSymbol(SymbolFlags.Transient, "__resolving__");
let anyType = createIntrinsicType(TypeFlags.Any, "any");
let stringType = createIntrinsicType(TypeFlags.String, "string");
let numberType = createIntrinsicType(TypeFlags.Number, "number");
let booleanType = createIntrinsicType(TypeFlags.Boolean, "boolean");
let esSymbolType = createIntrinsicType(TypeFlags.ESSymbol, "symbol");
let voidType = createIntrinsicType(TypeFlags.Void, "void");
let undefinedType = createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsUndefinedOrNull, "undefined");
let nullType = createIntrinsicType(TypeFlags.Null | TypeFlags.ContainsUndefinedOrNull, "null");
let unknownType = createIntrinsicType(TypeFlags.Any, "unknown");
let circularType = createIntrinsicType(TypeFlags.Any, "__circular__");
let emptyObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
let emptyGenericType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
emptyGenericType.instantiations = {};
let anyFunctionType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
let noConstraintType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
let anySignature = createSignature(undefined, undefined, emptyArray, anyType, undefined, 0, false, false);
let unknownSignature = createSignature(undefined, undefined, emptyArray, unknownType, undefined, 0, false, false);
let globals: SymbolTable = {};
let globalESSymbolConstructorSymbol: Symbol;
let getGlobalPromiseConstructorSymbol: () => Symbol;
let globalObjectType: ObjectType;
let globalFunctionType: ObjectType;
let globalArrayType: GenericType;
let globalStringType: ObjectType;
let globalNumberType: ObjectType;
let globalBooleanType: ObjectType;
let globalRegExpType: ObjectType;
let globalTemplateStringsArrayType: ObjectType;
let globalESSymbolType: ObjectType;
let jsxElementType: ObjectType;
/** Lazily loaded, use getJsxIntrinsicElementType() */
let jsxIntrinsicElementsType: ObjectType;
let globalIterableType: GenericType;
let globalIteratorType: GenericType;
let globalIterableIteratorType: GenericType;
let anyArrayType: Type;
let getGlobalClassDecoratorType: () => ObjectType;
let getGlobalParameterDecoratorType: () => ObjectType;
let getGlobalPropertyDecoratorType: () => ObjectType;
let getGlobalMethodDecoratorType: () => ObjectType;
let getGlobalTypedPropertyDescriptorType: () => ObjectType;
let getGlobalPromiseType: () => ObjectType;
let tryGetGlobalPromiseType: () => ObjectType;
let getGlobalPromiseLikeType: () => ObjectType;
let getInstantiatedGlobalPromiseLikeType: () => ObjectType;
let getGlobalPromiseConstructorLikeType: () => ObjectType;
let getGlobalThenableType: () => ObjectType;
let tupleTypes: Map = {};
let unionTypes: Map = {};
let intersectionTypes: Map = {};
let stringLiteralTypes: Map = {};
let emitExtends = false;
let emitDecorate = false;
let emitParam = false;
let emitAwaiter = false;
let emitGenerator = false;
let resolutionTargets: Object[] = [];
let resolutionResults: boolean[] = [];
let mergedSymbols: Symbol[] = [];
let symbolLinks: SymbolLinks[] = [];
let nodeLinks: NodeLinks[] = [];
let potentialThisCollisions: Node[] = [];
let awaitedTypeStack: number[] = [];
let diagnostics = createDiagnosticCollection();
let primitiveTypeInfo: Map<{ type: Type; flags: TypeFlags }> = {
"string": {
type: stringType,
flags: TypeFlags.StringLike
},
"number": {
type: numberType,
flags: TypeFlags.NumberLike
},
"boolean": {
type: booleanType,
flags: TypeFlags.Boolean
},
"symbol": {
type: esSymbolType,
flags: TypeFlags.ESSymbol
}
};
const JsxNames = {
JSX: "JSX",
IntrinsicElements: "IntrinsicElements",
ElementClass: "ElementClass",
ElementAttributesPropertyNameContainer: "ElementAttributesProperty",
Element: "Element"
};
let subtypeRelation: Map = {};
let assignableRelation: Map = {};
let identityRelation: Map = {};
initializeTypeChecker();
return checker;
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?: any, arg1?: any, arg2?: any): void {
let diagnostic = location
? createDiagnosticForNode(location, message, arg0, arg1, arg2)
: createCompilerDiagnostic(message, arg0, arg1, arg2);
diagnostics.add(diagnostic);
}
function createSymbol(flags: SymbolFlags, name: string): Symbol {
return new Symbol(flags, name);
}
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++;
mergedSymbols[source.mergeId] = target;
}
function cloneSymbol(symbol: Symbol): Symbol {
let result = createSymbol(symbol.flags | SymbolFlags.Merged, symbol.name);
result.declarations = symbol.declarations.slice(0);
result.parent = symbol.parent;
if (symbol.valueDeclaration) result.valueDeclaration = symbol.valueDeclaration;
if (symbol.constEnumOnlyModule) result.constEnumOnlyModule = true;
if (symbol.members) result.members = cloneSymbolTable(symbol.members);
if (symbol.exports) result.exports = cloneSymbolTable(symbol.exports);
recordMergedSymbol(result, symbol);
return result;
}
function mergeSymbol(target: Symbol, source: Symbol) {
if (!(target.flags & getExcludedSymbolFlags(source.flags))) {
if (source.flags & SymbolFlags.ValueModule && target.flags & SymbolFlags.ValueModule && target.constEnumOnlyModule && !source.constEnumOnlyModule) {
// reset flag when merging instantiated module into value module that has only const enums
target.constEnumOnlyModule = false;
}
target.flags |= source.flags;
if (!target.valueDeclaration && source.valueDeclaration) target.valueDeclaration = source.valueDeclaration;
forEach(source.declarations, node => {
target.declarations.push(node);
});
if (source.members) {
if (!target.members) target.members = {};
mergeSymbolTable(target.members, source.members);
}
if (source.exports) {
if (!target.exports) target.exports = {};
mergeSymbolTable(target.exports, source.exports);
}
recordMergedSymbol(target, source);
}
else {
let 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(node.name ? node.name : node, message, symbolToString(source));
});
forEach(target.declarations, node => {
error(node.name ? node.name : node, message, symbolToString(source));
});
}
}
function cloneSymbolTable(symbolTable: SymbolTable): SymbolTable {
let result: SymbolTable = {};
for (let id in symbolTable) {
if (hasProperty(symbolTable, id)) {
result[id] = symbolTable[id];
}
}
return result;
}
function mergeSymbolTable(target: SymbolTable, source: SymbolTable) {
for (let id in source) {
if (hasProperty(source, id)) {
if (!hasProperty(target, id)) {
target[id] = source[id];
}
else {
let symbol = target[id];
if (!(symbol.flags & SymbolFlags.Merged)) {
target[id] = symbol = cloneSymbol(symbol);
}
mergeSymbol(symbol, source[id]);
}
}
}
}
function getSymbolLinks(symbol: Symbol): SymbolLinks {
if (symbol.flags & SymbolFlags.Transient) return symbol;
let id = getSymbolId(symbol);
return symbolLinks[id] || (symbolLinks[id] = {});
}
function getNodeLinks(node: Node): NodeLinks {
let nodeId = getNodeId(node);
return nodeLinks[nodeId] || (nodeLinks[nodeId] = {});
}
function getSourceFile(node: Node): SourceFile {
return getAncestor(node, SyntaxKind.SourceFile);
}
function isGlobalSourceFile(node: Node) {
return node.kind === SyntaxKind.SourceFile && !isExternalModule(node);
}
function getSymbol(symbols: SymbolTable, name: string, meaning: SymbolFlags): Symbol {
if (meaning && hasProperty(symbols, name)) {
let symbol = symbols[name];
Debug.assert((symbol.flags & SymbolFlags.Instantiated) === 0, "Should never get an instantiated symbol here.");
if (symbol.flags & meaning) {
return symbol;
}
if (symbol.flags & SymbolFlags.Alias) {
let 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.
}
/** Returns true if node1 is defined before node 2**/
function isDefinedBefore(node1: Node, node2: Node): boolean {
let file1 = getSourceFileOfNode(node1);
let file2 = getSourceFileOfNode(node2);
if (file1 === file2) {
return node1.pos <= node2.pos;
}
if (!compilerOptions.out) {
return true;
}
let sourceFiles = host.getSourceFiles();
return sourceFiles.indexOf(file1) <= sourceFiles.indexOf(file2);
}
// Resolve a given name for a given meaning at a given location. An error is reported if the name was not found and
// the nameNotFoundMessage argument is not undefined. Returns the resolved symbol, or undefined if no symbol with
// the given name can be found.
function resolveName(location: Node, name: string, meaning: SymbolFlags, nameNotFoundMessage: DiagnosticMessage, nameArg: string | Identifier): Symbol {
let result: Symbol;
let lastLocation: Node;
let propertyWithInvalidInitializer: Node;
let errorLocation = location;
let grandparent: Node;
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 = getSymbol(location.locals, name, meaning)) {
// 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.
if (!(meaning & SymbolFlags.Type) ||
!(result.flags & (SymbolFlags.Type & ~SymbolFlags.TypeParameter)) ||
!isFunctionLike(location) ||
lastLocation === (location).body) {
break loop;
}
result = undefined;
}
}
switch (location.kind) {
case SyntaxKind.SourceFile:
if (!isExternalModule(location)) break;
case SyntaxKind.ModuleDeclaration:
let moduleExports = getSymbolOfNode(location).exports;
if (location.kind === SyntaxKind.SourceFile ||
(location.kind === SyntaxKind.ModuleDeclaration && (location).name.kind === SyntaxKind.StringLiteral)) {
// It's an external module. 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.
if (hasProperty(moduleExports, name) &&
moduleExports[name].flags === SymbolFlags.Alias &&
getDeclarationOfKind(moduleExports[name], SyntaxKind.ExportSpecifier)) {
break;
}
result = moduleExports["default"];
let localSymbol = getLocalSymbolForExportDefault(result);
if (result && localSymbol && (result.flags & meaning) && localSymbol.name === name) {
break loop;
}
result = undefined;
}
if (result = getSymbol(moduleExports, name, meaning & SymbolFlags.ModuleMember)) {
break loop;
}
break;
case SyntaxKind.EnumDeclaration:
if (result = getSymbol(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) && !(location.flags & NodeFlags.Static)) {
let ctor = findConstructorDeclaration(location.parent);
if (ctor && ctor.locals) {
if (getSymbol(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 = getSymbol(getSymbolOfNode(location).members, name, meaning & SymbolFlags.Type)) {
if (lastLocation && lastLocation.flags & NodeFlags.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) {
let className = (location).name;
if (className && name === className.text) {
result = location.symbol;
break loop;
}
}
break;
// It is not legal to reference a class's own type parameters from a computed property name that
// belongs to the class. For example:
//
// function foo() { return '' }
// class C { // <-- Class's own type parameter T
// [foo()]() { } // <-- Reference to T from class's own computed property
// }
//
case SyntaxKind.ComputedPropertyName:
grandparent = location.parent.parent;
if (isClassLike(grandparent) || grandparent.kind === SyntaxKind.InterfaceDeclaration) {
// A reference to this grandparent's type parameters would be an error
if (result = getSymbol(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) {
let functionName = (location).name;
if (functionName && name === functionName.text) {
result = location.symbol;
break loop;
}
}
break;
case SyntaxKind.Decorator:
// Decorators are resolved at the class declaration. Resolving at the parameter
// or member would result in looking up locals in the method.
//
// function y() {}
// class C {
// method(@y x, y) {} // <-- decorator y should be resolved at the class declaration, not the parameter.
// }
//
if (location.parent && location.parent.kind === SyntaxKind.Parameter) {
location = location.parent;
}
//
// function y() {}
// class C {
// @y method(x, y) {} // <-- decorator y should be resolved at the class declaration, not the method.
// }
//
if (location.parent && isClassElement(location.parent)) {
location = location.parent;
}
break;
}
lastLocation = location;
location = location.parent;
}
if (!result) {
result = getSymbol(globals, name, meaning);
}
if (!result) {
if (nameNotFoundMessage) {
error(errorLocation, nameNotFoundMessage, typeof nameArg === "string" ? nameArg : declarationNameToString(nameArg));
}
return undefined;
}
// Perform extra checks only if error reporting was requested
if (nameNotFoundMessage) {
if (propertyWithInvalidInitializer) {
// We have a match, but the reference occurred within a property initializer and the identifier also binds
// to a local variable in the constructor where the code will be emitted.
let propertyName = (propertyWithInvalidInitializer).name;
error(errorLocation, Diagnostics.Initializer_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor,
declarationNameToString(propertyName), typeof nameArg === "string" ? nameArg : declarationNameToString(nameArg));
return undefined;
}
if (result.flags & SymbolFlags.BlockScopedVariable) {
checkResolvedBlockScopedVariable(result, errorLocation);
}
}
return result;
}
function checkResolvedBlockScopedVariable(result: Symbol, errorLocation: Node): void {
Debug.assert((result.flags & SymbolFlags.BlockScopedVariable) !== 0);
// Block-scoped variables cannot be used before their definition
let declaration = forEach(result.declarations, d => isBlockOrCatchScoped(d) ? d : undefined);
Debug.assert(declaration !== undefined, "Block-scoped variable declaration is undefined");
// first check if usage is lexically located after the declaration
let isUsedBeforeDeclaration = !isDefinedBefore(declaration, errorLocation);
if (!isUsedBeforeDeclaration) {
// lexical check succeeded however code still can be illegal.
// - block scoped variables cannot be used in its initializers
// let x = x; // illegal but usage is lexically after definition
// - in ForIn/ForOf statements variable cannot be contained in expression part
// for (let x in x)
// for (let x of x)
// climb up to the variable declaration skipping binding patterns
let variableDeclaration = getAncestor(declaration, SyntaxKind.VariableDeclaration);
let container = getEnclosingBlockScopeContainer(variableDeclaration);
if (variableDeclaration.parent.parent.kind === SyntaxKind.VariableStatement ||
variableDeclaration.parent.parent.kind === SyntaxKind.ForStatement) {
// variable statement/for statement case,
// use site should not be inside variable declaration (initializer of declaration or binding element)
isUsedBeforeDeclaration = isSameScopeDescendentOf(errorLocation, variableDeclaration, container);
}
else if (variableDeclaration.parent.parent.kind === SyntaxKind.ForOfStatement ||
variableDeclaration.parent.parent.kind === SyntaxKind.ForInStatement) {
// ForIn/ForOf case - use site should not be used in expression part
let expression = (variableDeclaration.parent.parent).expression;
isUsedBeforeDeclaration = isSameScopeDescendentOf(errorLocation, expression, container);
}
}
if (isUsedBeforeDeclaration) {
error(errorLocation, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, declarationNameToString(declaration.name));
}
}
/* 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 {
if (!parent) {
return false;
}
for (let current = initial; current && current !== stopAt && !isFunctionLike(current); current = current.parent) {
if (current === parent) {
return true;
}
}
return false;
}
function getAnyImportSyntax(node: Node): AnyImportSyntax {
if (isAliasSymbolDeclaration(node)) {
if (node.kind === SyntaxKind.ImportEqualsDeclaration) {
return node;
}
while (node && node.kind !== SyntaxKind.ImportDeclaration) {
node = node.parent;
}
return node;
}
}
function getDeclarationOfAliasSymbol(symbol: Symbol): Declaration {
return forEach(symbol.declarations, d => isAliasSymbolDeclaration(d) ? d : undefined);
}
function getTargetOfImportEqualsDeclaration(node: ImportEqualsDeclaration): Symbol {
if (node.moduleReference.kind === SyntaxKind.ExternalModuleReference) {
return resolveExternalModuleSymbol(resolveExternalModuleName(node, getExternalModuleImportEqualsDeclarationExpression(node)));
}
return getSymbolOfPartOfRightHandSideOfImportEquals(node.moduleReference, node);
}
function getTargetOfImportClause(node: ImportClause): Symbol {
let moduleSymbol = resolveExternalModuleName(node, (node.parent).moduleSpecifier);
if (moduleSymbol) {
let exportDefaultSymbol = resolveSymbol(moduleSymbol.exports["default"]);
if (!exportDefaultSymbol) {
error(node.name, Diagnostics.Module_0_has_no_default_export, symbolToString(moduleSymbol));
}
return exportDefaultSymbol;
}
}
function getTargetOfNamespaceImport(node: NamespaceImport): Symbol {
let moduleSpecifier = (node.parent.parent).moduleSpecifier;
return resolveESModuleSymbol(resolveExternalModuleName(node, moduleSpecifier), moduleSpecifier);
}
function getMemberOfModuleVariable(moduleSymbol: Symbol, name: string): Symbol {
if (moduleSymbol.flags & SymbolFlags.Variable) {
let typeAnnotation = (moduleSymbol.valueDeclaration).type;
if (typeAnnotation) {
return getPropertyOfType(getTypeFromTypeNode(typeAnnotation), name);
}
}
}
// This function creates a synthetic symbol that combines the value side of one symbol with the
// type/namespace side of another symbol. Consider this example:
//
// declare module graphics {
// interface Point {
// x: number;
// y: number;
// }
// }
// declare var graphics: {
// Point: new (x: number, y: number) => graphics.Point;
// }
// declare module "graphics" {
// export = graphics;
// }
//
// An 'import { Point } from "graphics"' needs to create a symbol that combines the value side 'Point'
// property with the type/namespace side interface 'Point'.
function combineValueAndTypeSymbols(valueSymbol: Symbol, typeSymbol: Symbol): Symbol {
if (valueSymbol.flags & (SymbolFlags.Type | SymbolFlags.Namespace)) {
return valueSymbol;
}
let result = createSymbol(valueSymbol.flags | typeSymbol.flags, valueSymbol.name);
result.declarations = concatenate(valueSymbol.declarations, typeSymbol.declarations);
result.parent = valueSymbol.parent || typeSymbol.parent;
if (valueSymbol.valueDeclaration) result.valueDeclaration = valueSymbol.valueDeclaration;
if (typeSymbol.members) result.members = typeSymbol.members;
if (valueSymbol.exports) result.exports = valueSymbol.exports;
return result;
}
function getExportOfModule(symbol: Symbol, name: string): Symbol {
if (symbol.flags & SymbolFlags.Module) {
let exports = getExportsOfSymbol(symbol);
if (hasProperty(exports, name)) {
return resolveSymbol(exports[name]);
}
}
}
function getPropertyOfVariable(symbol: Symbol, name: string): Symbol {
if (symbol.flags & SymbolFlags.Variable) {
let typeAnnotation = (symbol.valueDeclaration).type;
if (typeAnnotation) {
return resolveSymbol(getPropertyOfType(getTypeFromTypeNode(typeAnnotation), name));
}
}
}
function getExternalModuleMember(node: ImportDeclaration | ExportDeclaration, specifier: ImportOrExportSpecifier): Symbol {
let moduleSymbol = resolveExternalModuleName(node, node.moduleSpecifier);
let targetSymbol = resolveESModuleSymbol(moduleSymbol, node.moduleSpecifier);
if (targetSymbol) {
let name = specifier.propertyName || specifier.name;
if (name.text) {
let symbolFromModule = getExportOfModule(targetSymbol, name.text);
let symbolFromVariable = getPropertyOfVariable(targetSymbol, name.text);
let 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): Symbol {
return getExternalModuleMember(node.parent.parent.parent, node);
}
function getTargetOfExportSpecifier(node: ExportSpecifier): Symbol {
return (node.parent.parent).moduleSpecifier ?
getExternalModuleMember(node.parent.parent, node) :
resolveEntityName(node.propertyName || node.name, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace);
}
function getTargetOfExportAssignment(node: ExportAssignment): Symbol {
return resolveEntityName(node.expression, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace);
}
function getTargetOfAliasDeclaration(node: Declaration): Symbol {
switch (node.kind) {
case SyntaxKind.ImportEqualsDeclaration:
return getTargetOfImportEqualsDeclaration(node);
case SyntaxKind.ImportClause:
return getTargetOfImportClause(node);
case SyntaxKind.NamespaceImport:
return getTargetOfNamespaceImport(node);
case SyntaxKind.ImportSpecifier:
return getTargetOfImportSpecifier(node);
case SyntaxKind.ExportSpecifier:
return getTargetOfExportSpecifier(node);
case SyntaxKind.ExportAssignment:
return getTargetOfExportAssignment(node);
}
}
function resolveSymbol(symbol: Symbol): Symbol {
return symbol && symbol.flags & SymbolFlags.Alias && !(symbol.flags & (SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace)) ? resolveAlias(symbol) : symbol;
}
function resolveAlias(symbol: Symbol): Symbol {
Debug.assert((symbol.flags & SymbolFlags.Alias) !== 0, "Should only get Alias here.");
let links = getSymbolLinks(symbol);
if (!links.target) {
links.target = resolvingSymbol;
let node = getDeclarationOfAliasSymbol(symbol);
let 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) {
let symbol = getSymbolOfNode(node);
let target = resolveAlias(symbol);
if (target) {
let markAlias =
(target === unknownSymbol && compilerOptions.isolatedModules) ||
(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) {
let links = getSymbolLinks(symbol);
if (!links.referenced) {
links.referenced = true;
let node = getDeclarationOfAliasSymbol(symbol);
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, importDeclaration?: ImportEqualsDeclaration): Symbol {
if (!importDeclaration) {
importDeclaration = getAncestor(entityName, SyntaxKind.ImportEqualsDeclaration);
Debug.assert(importDeclaration !== undefined);
}
// There are three things we might try to look for. In the following examples,
// the search term is enclosed in |...|:
//
// import a = |b|; // Namespace
// import a = |b.c|; // Value, type, namespace
// import a = |b.c|.d; // Namespace
if (entityName.kind === SyntaxKind.Identifier && isRightSideOfQualifiedNameOrPropertyAccess(entityName)) {
entityName = entityName.parent;
}
// 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);
}
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);
}
}
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: EntityName | Expression, meaning: SymbolFlags): Symbol {
if (nodeIsMissing(name)) {
return undefined;
}
let symbol: Symbol;
if (name.kind === SyntaxKind.Identifier) {
let message = meaning === SymbolFlags.Namespace ? Diagnostics.Cannot_find_namespace_0 : Diagnostics.Cannot_find_name_0;
symbol = resolveName(name, (name).text, meaning, message, name);
if (!symbol) {
return undefined;
}
}
else if (name.kind === SyntaxKind.QualifiedName || name.kind === SyntaxKind.PropertyAccessExpression) {
let left = name.kind === SyntaxKind.QualifiedName ? (name).left : (name).expression;
let right = name.kind === SyntaxKind.QualifiedName ? (name).right : (name).name;
let namespace = resolveEntityName(left, SymbolFlags.Namespace);
if (!namespace || namespace === unknownSymbol || nodeIsMissing(right)) {
return undefined;
}
symbol = getSymbol(getExportsOfSymbol(namespace), right.text, meaning);
if (!symbol) {
error(right, Diagnostics.Module_0_has_no_exported_member_1, getFullyQualifiedName(namespace), declarationNameToString(right));
return undefined;
}
}
else {
Debug.fail("Unknown entity name kind.");
}
Debug.assert((symbol.flags & SymbolFlags.Instantiated) === 0, "Should never get an instantiated symbol here.");
return symbol.flags & meaning ? symbol : resolveAlias(symbol);
}
function isExternalModuleNameRelative(moduleName: string): boolean {
// TypeScript 1.0 spec (April 2014): 11.2.1
// An external module name is "relative" if the first term is "." or "..".
return moduleName.substr(0, 2) === "./" || moduleName.substr(0, 3) === "../" || moduleName.substr(0, 2) === ".\\" || moduleName.substr(0, 3) === "..\\";
}
function resolveExternalModuleName(location: Node, moduleReferenceExpression: Expression): Symbol {
if (moduleReferenceExpression.kind !== SyntaxKind.StringLiteral) {
return;
}
let moduleReferenceLiteral = moduleReferenceExpression;
let searchPath = getDirectoryPath(getSourceFile(location).fileName);
// Module names are escaped in our symbol table. However, string literal values aren't.
// Escape the name in the "require(...)" clause to ensure we find the right symbol.
let moduleName = escapeIdentifier(moduleReferenceLiteral.text);
if (!moduleName) return;
let isRelative = isExternalModuleNameRelative(moduleName);
if (!isRelative) {
let symbol = getSymbol(globals, '"' + moduleName + '"', SymbolFlags.ValueModule);
if (symbol) {
return symbol;
}
}
let fileName: string;
let sourceFile: SourceFile;
while (true) {
fileName = normalizePath(combinePaths(searchPath, moduleName));
sourceFile = forEach(supportedExtensions, extension => host.getSourceFile(fileName + extension));
if (sourceFile || isRelative) {
break;
}
let parentPath = getDirectoryPath(searchPath);
if (parentPath === searchPath) {
break;
}
searchPath = parentPath;
}
if (sourceFile) {
if (sourceFile.symbol) {
return sourceFile.symbol;
}
error(moduleReferenceLiteral, Diagnostics.File_0_is_not_a_module, sourceFile.fileName);
return;
}
error(moduleReferenceLiteral, Diagnostics.Cannot_find_module_0, moduleName);
}
// 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): Symbol {
return moduleSymbol && resolveSymbol(moduleSymbol.exports["export="]) || 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): Symbol {
let symbol = resolveExternalModuleSymbol(moduleSymbol);
if (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));
symbol = undefined;
}
return symbol;
}
function getExportAssignmentSymbol(moduleSymbol: Symbol): Symbol {
return moduleSymbol.exports["export="];
}
function getExportsOfModuleAsArray(moduleSymbol: Symbol): Symbol[] {
return symbolsToArray(getExportsOfModule(moduleSymbol));
}
function getExportsOfSymbol(symbol: Symbol): SymbolTable {
return symbol.flags & SymbolFlags.Module ? getExportsOfModule(symbol) : symbol.exports || emptySymbols;
}
function getExportsOfModule(moduleSymbol: Symbol): SymbolTable {
let links = getSymbolLinks(moduleSymbol);
return links.resolvedExports || (links.resolvedExports = getExportsForModule(moduleSymbol));
}
function extendExportSymbols(target: SymbolTable, source: SymbolTable) {
for (let id in source) {
if (id !== "default" && !hasProperty(target, id)) {
target[id] = source[id];
}
}
}
function getExportsForModule(moduleSymbol: Symbol): SymbolTable {
let result: SymbolTable;
let visitedSymbols: Symbol[] = [];
visit(moduleSymbol);
return result || moduleSymbol.exports;
// The ES6 spec permits export * declarations in a module to circularly reference the module itself. For example,
// module 'a' can 'export * from "b"' and 'b' can 'export * from "a"' without error.
function visit(symbol: Symbol) {
if (symbol && symbol.flags & SymbolFlags.HasExports && !contains(visitedSymbols, symbol)) {
visitedSymbols.push(symbol);
if (symbol !== moduleSymbol) {
if (!result) {
result = cloneSymbolTable(moduleSymbol.exports);
}
extendExportSymbols(result, symbol.exports);
}
// All export * declarations are collected in an __export symbol by the binder
let exportStars = symbol.exports["__export"];
if (exportStars) {
for (let node of exportStars.declarations) {
visit(resolveExternalModuleName(node, (node).moduleSpecifier));
}
}
}
}
}
function getMergedSymbol(symbol: Symbol): Symbol {
let merged: Symbol;
return symbol && symbol.mergeId && (merged = mergedSymbols[symbol.mergeId]) ? merged : symbol;
}
function getSymbolOfNode(node: Node): Symbol {
return getMergedSymbol(node.symbol);
}
function getParentOfSymbol(symbol: Symbol): Symbol {
return getMergedSymbol(symbol.parent);
}
function getExportSymbolOfValueSymbolIfExported(symbol: Symbol): Symbol {
return symbol && (symbol.flags & SymbolFlags.ExportValue) !== 0
? getMergedSymbol(symbol.exportSymbol)
: symbol;
}
function symbolIsValue(symbol: Symbol): boolean {
// If it is an instantiated symbol, then it is a value if the symbol it is an
// instantiation of is a value.
if (symbol.flags & SymbolFlags.Instantiated) {
return symbolIsValue(getSymbolLinks(symbol).target);
}
// If the symbol has the value flag, it is trivially a value.
if (symbol.flags & SymbolFlags.Value) {
return true;
}
// If it is an alias, then it is a value if the symbol it resolves to is a value.
if (symbol.flags & SymbolFlags.Alias) {
return (resolveAlias(symbol).flags & SymbolFlags.Value) !== 0;
}
return false;
}
function findConstructorDeclaration(node: ClassLikeDeclaration): ConstructorDeclaration {
let members = node.members;
for (let member of members) {
if (member.kind === SyntaxKind.Constructor && nodeIsPresent((member).body)) {
return member;
}
}
}
function createType(flags: TypeFlags): Type {
let result = new Type(checker, flags);
result.id = typeCount++;
return result;
}
function createIntrinsicType(kind: TypeFlags, intrinsicName: string): IntrinsicType {
let type = createType(kind);
type.intrinsicName = intrinsicName;
return type;
}
function createObjectType(kind: TypeFlags, symbol?: Symbol): ObjectType {
let type = createType(kind);
type.symbol = symbol;
return type;
}
// A reserved member name starts with two underscores, but the third character cannot be an underscore
// or the @ symbol. A third underscore indicates an escaped form of an identifer that started
// with at least two underscores. The @ character indicates that the name is denoted by a well known ES
// Symbol instance.
function isReservedMemberName(name: string) {
return name.charCodeAt(0) === CharacterCodes._ &&
name.charCodeAt(1) === CharacterCodes._ &&
name.charCodeAt(2) !== CharacterCodes._ &&
name.charCodeAt(2) !== CharacterCodes.at;
}
function getNamedMembers(members: SymbolTable): Symbol[] {
let result: Symbol[];
for (let id in members) {
if (hasProperty(members, id)) {
if (!isReservedMemberName(id)) {
if (!result) result = [];
let symbol = members[id];
if (symbolIsValue(symbol)) {
result.push(symbol);
}
}
}
}
return result || emptyArray;
}
function setObjectTypeMembers(type: ObjectType, members: SymbolTable, callSignatures: Signature[], constructSignatures: Signature[], stringIndexType: Type, numberIndexType: Type): ResolvedType {
(type).members = members;
(type).properties = getNamedMembers(members);
(type).callSignatures = callSignatures;
(type).constructSignatures = constructSignatures;
if (stringIndexType) (type).stringIndexType = stringIndexType;
if (numberIndexType) (type).numberIndexType = numberIndexType;
return type;
}
function createAnonymousType(symbol: Symbol, members: SymbolTable, callSignatures: Signature[], constructSignatures: Signature[], stringIndexType: Type, numberIndexType: Type): ResolvedType {
return setObjectTypeMembers(createObjectType(TypeFlags.Anonymous, symbol),
members, callSignatures, constructSignatures, stringIndexType, numberIndexType);
}
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 (!isExternalModule(location)) {
break;
}
case SyntaxKind.ModuleDeclaration:
if (result = callback(getSymbolOfNode(location).exports)) {
return result;
}
break;
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
if (result = callback(getSymbolOfNode(location).members)) {
return result;
}
break;
}
}
return callback(globals);
}
function getQualifiedLeftMeaning(rightMeaning: SymbolFlags) {
// If we are looking in value space, the parent meaning is value, other wise it is namespace
return rightMeaning === SymbolFlags.Value ? SymbolFlags.Value : SymbolFlags.Namespace;
}
function getAccessibleSymbolChain(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags, useOnlyExternalAliasing: boolean): Symbol[] {
function getAccessibleSymbolChainFromSymbolTable(symbols: SymbolTable): Symbol[] {
function canQualifySymbol(symbolFromSymbolTable: Symbol, meaning: SymbolFlags) {
// If the symbol is equivalent and doesn't need further qualification, this symbol is accessible
if (!needsQualification(symbolFromSymbolTable, enclosingDeclaration, meaning)) {
return true;
}
// If symbol needs qualification, make sure that parent is accessible, if it is then this symbol is accessible too
let accessibleParent = getAccessibleSymbolChain(symbolFromSymbolTable.parent, enclosingDeclaration, getQualifiedLeftMeaning(meaning), useOnlyExternalAliasing);
return !!accessibleParent;
}
function isAccessible(symbolFromSymbolTable: Symbol, resolvedAliasSymbol?: Symbol) {
if (symbol === (resolvedAliasSymbol || symbolFromSymbolTable)) {
// if the symbolFromSymbolTable is not external module (it could be if it was determined as ambient external module and would be in globals table)
// and if symbolfrom symbolTable or alias resolution matches the symbol,
// check the symbol can be qualified, it is only then this symbol is accessible
return !forEach(symbolFromSymbolTable.declarations, hasExternalModuleSymbol) &&
canQualifySymbol(symbolFromSymbolTable, meaning);
}
}
// If symbol is directly available by its name in the symbol table
if (isAccessible(lookUp(symbols, symbol.name))) {
return [symbol];
}
// Check if symbol is any of the alias
return forEachValue(symbols, symbolFromSymbolTable => {
if (symbolFromSymbolTable.flags & SymbolFlags.Alias
&& symbolFromSymbolTable.name !== "export="
&& !getDeclarationOfKind(symbolFromSymbolTable, SyntaxKind.ExportSpecifier)) {
if (!useOnlyExternalAliasing || // We can use any type of alias to get the name
// Is this external alias, then use it to name
ts.forEach(symbolFromSymbolTable.declarations, isExternalModuleImportEqualsDeclaration)) {
let resolvedImportedSymbol = resolveAlias(symbolFromSymbolTable);
if (isAccessible(symbolFromSymbolTable, resolveAlias(symbolFromSymbolTable))) {
return [symbolFromSymbolTable];
}
// Look in the exported members, if we can find accessibleSymbolChain, symbol is accessible using this chain
// but only if the symbolFromSymbolTable can be qualified
let accessibleSymbolsFromExports = resolvedImportedSymbol.exports ? getAccessibleSymbolChainFromSymbolTable(resolvedImportedSymbol.exports) : undefined;
if (accessibleSymbolsFromExports && canQualifySymbol(symbolFromSymbolTable, getQualifiedLeftMeaning(meaning))) {
return [symbolFromSymbolTable].concat(accessibleSymbolsFromExports);
}
}
}
});
}
if (symbol) {
return forEachSymbolTableInScope(enclosingDeclaration, getAccessibleSymbolChainFromSymbolTable);
}
}
function needsQualification(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags) {
let qualify = false;
forEachSymbolTableInScope(enclosingDeclaration, symbolTable => {
// If symbol of this name is not available in the symbol table we are ok
if (!hasProperty(symbolTable, symbol.name)) {
// Continue to the next symbol table
return false;
}
// If the symbol with this name is present it should refer to the symbol
let symbolFromSymbolTable = symbolTable[symbol.name];
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 isSymbolAccessible(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags): SymbolAccessiblityResult {
if (symbol && enclosingDeclaration && !(symbol.flags & SymbolFlags.TypeParameter)) {
let initialSymbol = symbol;
let meaningToLook = meaning;
while (symbol) {
// Symbol is accessible if it by itself is accessible
let accessibleSymbolChain = getAccessibleSymbolChain(symbol, enclosingDeclaration, meaningToLook, /*useOnlyExternalAliasing*/ false);
if (accessibleSymbolChain) {
let hasAccessibleDeclarations = hasVisibleDeclarations(accessibleSymbolChain[0]);
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 {
// }
// }
// let 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
let symbolExternalModule = forEach(initialSymbol.declarations, getExternalModuleContainer);
if (symbolExternalModule) {
let 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) {
for (; declaration; declaration = declaration.parent) {
if (hasExternalModuleSymbol(declaration)) {
return getSymbolOfNode(declaration);
}
}
}
}
function hasExternalModuleSymbol(declaration: Node) {
return (declaration.kind === SyntaxKind.ModuleDeclaration && (declaration).name.kind === SyntaxKind.StringLiteral) ||
(declaration.kind === SyntaxKind.SourceFile && isExternalModule(declaration));
}
function hasVisibleDeclarations(symbol: Symbol): 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
let anyImportSyntax = getAnyImportSyntax(declaration);
if (anyImportSyntax &&
!(anyImportSyntax.flags & NodeFlags.Export) && // import clause without export
isDeclarationVisible(anyImportSyntax.parent)) {
getNodeLinks(declaration).isVisible = true;
if (aliasesToMakeVisible) {
if (!contains(aliasesToMakeVisible, anyImportSyntax)) {
aliasesToMakeVisible.push(anyImportSyntax);
}
}
else {
aliasesToMakeVisible = [anyImportSyntax];
}
return true;
}
// Declaration is not visible
return false;
}
return true;
}
}
function isEntityNameVisible(entityName: EntityName | Expression, enclosingDeclaration: Node): SymbolVisibilityResult {
// get symbol of the first identifier of the entityName
let meaning: SymbolFlags;
if (entityName.parent.kind === SyntaxKind.TypeQuery) {
// 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;
}
let firstIdentifier = getFirstIdentifier(entityName);
let symbol = resolveName(enclosingDeclaration, (firstIdentifier).text, meaning, /*nodeNotFoundErrorMessage*/ undefined, /*nameArg*/ undefined);
// Verify if the symbol is accessible
return (symbol && hasVisibleDeclarations(symbol)) || {
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 {
let writer = getSingleLineStringWriter();
getSymbolDisplayBuilder().buildSymbolDisplay(symbol, writer, enclosingDeclaration, meaning);
let result = writer.string();
releaseStringWriter(writer);
return result;
}
function signatureToString(signature: Signature, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string {
let writer = getSingleLineStringWriter();
getSymbolDisplayBuilder().buildSignatureDisplay(signature, writer, enclosingDeclaration, flags);
let result = writer.string();
releaseStringWriter(writer);
return result;
}
function typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string {
let writer = getSingleLineStringWriter();
getSymbolDisplayBuilder().buildTypeDisplay(type, writer, enclosingDeclaration, flags);
let result = writer.string();
releaseStringWriter(writer);
let maxLength = compilerOptions.noErrorTruncation || flags & TypeFormatFlags.NoTruncation ? undefined : 100;
if (maxLength && result.length >= maxLength) {
result = result.substr(0, maxLength - "...".length) + "...";
}
return result;
}
function getTypeAliasForTypeLiteral(type: Type): Symbol {
if (type.symbol && type.symbol.flags & SymbolFlags.TypeLiteral) {
let node = type.symbol.declarations[0].parent;
while (node.kind === SyntaxKind.ParenthesizedType) {
node = node.parent;
}
if (node.kind === SyntaxKind.TypeAliasDeclaration) {
return getSymbolOfNode(node);
}
}
return undefined;
}
// This is for caching the result of getSymbolDisplayBuilder. Do not access directly.
let _displayBuilder: SymbolDisplayBuilder;
function getSymbolDisplayBuilder(): SymbolDisplayBuilder {
function getNameOfSymbol(symbol: Symbol): string {
if (symbol.declarations && symbol.declarations.length) {
let declaration = symbol.declarations[0];
if (declaration.name) {
return declarationNameToString(declaration.name);
}
switch (declaration.kind) {
case SyntaxKind.ClassExpression:
return "(Anonymous class)";
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
return "(Anonymous function)";
}
}
return symbol.name;
}
/**
* 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 inputted the name.
*/
function appendSymbolNameOnly(symbol: Symbol, writer: SymbolWriter): void {
writer.writeSymbol(getNameOfSymbol(symbol), 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 (symbol.flags & SymbolFlags.Instantiated) {
buildDisplayForTypeArgumentsAndDelimiters(getTypeParametersOfClassOrInterface(parentSymbol),
(symbol).mapper, writer, enclosingDeclaration);
}
else {
buildTypeParameterDisplayFromSymbol(parentSymbol, writer, enclosingDeclaration);
}
}
writePunctuation(writer, SyntaxKind.DotToken);
}
parentSymbol = symbol;
appendSymbolNameOnly(symbol, writer);
}
// 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);
function walkSymbol(symbol: Symbol, meaning: SymbolFlags): void {
if (symbol) {
let 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.
walkSymbol(
getParentOfSymbol(accessibleSymbolChain ? accessibleSymbolChain[0] : symbol),
getQualifiedLeftMeaning(meaning));
}
if (accessibleSymbolChain) {
for (let accessibleSymbol of accessibleSymbolChain) {
appendParentTypeArgumentsAndSymbolName(accessibleSymbol);
}
}
else {
// If we didn't find accessible symbol chain for this symbol, break if this is external module
if (!parentSymbol && ts.forEach(symbol.declarations, hasExternalModuleSymbol)) {
return;
}
// if this is anonymous type break
if (symbol.flags & SymbolFlags.TypeLiteral || symbol.flags & SymbolFlags.ObjectLiteral) {
return;
}
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
let isTypeParameter = symbol.flags & SymbolFlags.TypeParameter;
let typeFormatFlag = TypeFormatFlags.UseFullyQualifiedType & typeFlags;
if (!isTypeParameter && (enclosingDeclaration || typeFormatFlag)) {
walkSymbol(symbol, meaning);
return;
}
return appendParentTypeArgumentsAndSymbolName(symbol);
}
function buildTypeDisplay(type: Type, writer: SymbolWriter, enclosingDeclaration?: Node, globalFlags?: TypeFormatFlags, symbolStack?: Symbol[]) {
let globalFlagsToPass = globalFlags & TypeFormatFlags.WriteOwnNameForAnyLike;
return writeType(type, globalFlags);
function writeType(type: Type, flags: TypeFormatFlags) {
// 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.Reference) {
writeTypeReference(type, flags);
}
else if (type.flags & (TypeFlags.Class | TypeFlags.Interface | TypeFlags.Enum | TypeFlags.TypeParameter)) {
// The specified symbol flags need to be reinterpreted as type flags
buildSymbolDisplay(type.symbol, writer, enclosingDeclaration, SymbolFlags.Type, SymbolFormatFlags.None, flags);
}
else if (type.flags & TypeFlags.Tuple) {
writeTupleType(type);
}
else if (type.flags & TypeFlags.UnionOrIntersection) {
writeUnionOrIntersectionType(type, flags);
}
else if (type.flags & TypeFlags.Anonymous) {
writeAnonymousType(type, flags);
}
else if (type.flags & TypeFlags.StringLiteral) {
writer.writeStringLiteral((type).text);
}
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) {
// Unnamed function expressions, arrow functions, and unnamed class expressions have reserved names that
// we don't want to display
if (!isReservedMemberName(symbol.name)) {
buildSymbolDisplay(symbol, writer, enclosingDeclaration, SymbolFlags.Type);
}
if (pos < end) {
writePunctuation(writer, SyntaxKind.LessThanToken);
writeType(typeArguments[pos++], TypeFormatFlags.None);
while (pos < end) {
writePunctuation(writer, SyntaxKind.CommaToken);
writeSpace(writer);
writeType(typeArguments[pos++], TypeFormatFlags.None);
}
writePunctuation(writer, SyntaxKind.GreaterThanToken);
}
}
function writeTypeReference(type: TypeReference, flags: TypeFormatFlags) {
let typeArguments = type.typeArguments;
if (type.target === globalArrayType && !(flags & TypeFormatFlags.WriteArrayAsGenericType)) {
writeType(typeArguments[0], TypeFormatFlags.InElementType);
writePunctuation(writer, SyntaxKind.OpenBracketToken);
writePunctuation(writer, SyntaxKind.CloseBracketToken);
}
else {
// Write the type reference in the format f.g.C where A and B are type arguments
// for outer type parameters, and f and g are the respective declaring containers of those
// type parameters.
let outerTypeParameters = type.target.outerTypeParameters;
let i = 0;
if (outerTypeParameters) {
let length = outerTypeParameters.length;
while (i < length) {
// Find group of type arguments for type parameters with the same declaring container.
let start = i;
let 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);
writePunctuation(writer, SyntaxKind.DotToken);
}
}
}
writeSymbolTypeReference(type.symbol, typeArguments, i, typeArguments.length);
}
}
function writeTupleType(type: TupleType) {
writePunctuation(writer, SyntaxKind.OpenBracketToken);
writeTypeList(type.elementTypes, SyntaxKind.CommaToken);
writePunctuation(writer, SyntaxKind.CloseBracketToken);
}
function writeUnionOrIntersectionType(type: UnionOrIntersectionType, flags: TypeFormatFlags) {
if (flags & TypeFormatFlags.InElementType) {
writePunctuation(writer, SyntaxKind.OpenParenToken);
}
writeTypeList(type.types, type.flags & TypeFlags.Union ? SyntaxKind.BarToken : SyntaxKind.AmpersandToken);
if (flags & TypeFormatFlags.InElementType) {
writePunctuation(writer, SyntaxKind.CloseParenToken);
}
}
function writeAnonymousType(type: ObjectType, flags: TypeFormatFlags) {
let symbol = type.symbol;
if (symbol) {
// Always use 'typeof T' for type of class, enum, and module objects
if (symbol.flags & (SymbolFlags.Class | SymbolFlags.Enum | SymbolFlags.ValueModule)) {
writeTypeofSymbol(type, flags);
}
else if (shouldWriteTypeOfFunctionSymbol()) {
writeTypeofSymbol(type, flags);
}
else if (contains(symbolStack, symbol)) {
// If type is an anonymous type literal in a type alias declaration, use type alias name
let typeAlias = getTypeAliasForTypeLiteral(type);
if (typeAlias) {
// The specified symbol flags need to be reinterpreted as type flags
buildSymbolDisplay(typeAlias, writer, enclosingDeclaration, SymbolFlags.Type, SymbolFormatFlags.None, flags);
}
else {
// Recursive usage, use any
writeKeyword(writer, SyntaxKind.AnyKeyword);
}
}
else {
// Since instantiations of the same anonymous type have the same symbol, tracking symbols instead
// of types allows us to catch circular references to instantiations of the same anonymous type
if (!symbolStack) {
symbolStack = [];
}
symbolStack.push(symbol);
writeLiteralType(type, flags);
symbolStack.pop();
}
}
else {
// Anonymous types with no symbol are never circular
writeLiteralType(type, flags);
}
function shouldWriteTypeOfFunctionSymbol() {
let isStaticMethodSymbol = !!(symbol.flags & SymbolFlags.Method && // typeof static method
forEach(symbol.declarations, declaration => declaration.flags & NodeFlags.Static));
let isNonLocalFunctionSymbol = !!(symbol.flags & SymbolFlags.Function) &&
(symbol.parent || // is exported function symbol
forEach(symbol.declarations, declaration =>
declaration.parent.kind === SyntaxKind.SourceFile || declaration.parent.kind === SyntaxKind.ModuleBlock));
if (isStaticMethodSymbol || isNonLocalFunctionSymbol) {
// typeof is allowed only for static/non local functions
return !!(flags & TypeFormatFlags.UseTypeOfFunction) || // use typeof if format flags specify it
(contains(symbolStack, symbol)); // it is type of the symbol uses itself recursively
}
}
}
function writeTypeofSymbol(type: ObjectType, typeFormatFlags?: TypeFormatFlags) {
writeKeyword(writer, SyntaxKind.TypeOfKeyword);
writeSpace(writer);
buildSymbolDisplay(type.symbol, writer, enclosingDeclaration, SymbolFlags.Value, SymbolFormatFlags.None, typeFormatFlags);
}
function getIndexerParameterName(type: ObjectType, indexKind: IndexKind, fallbackName: string): string {
let declaration = getIndexDeclarationOfSymbol(type.symbol, indexKind);
if (!declaration) {
// declaration might not be found if indexer was added from the contextual type.
// in this case use fallback name
return fallbackName;
}
Debug.assert(declaration.parameters.length !== 0);
return declarationNameToString(declaration.parameters[0].name);
}
function writeLiteralType(type: ObjectType, flags: TypeFormatFlags) {
let resolved = resolveStructuredTypeMembers(type);
if (!resolved.properties.length && !resolved.stringIndexType && !resolved.numberIndexType) {
if (!resolved.callSignatures.length && !resolved.constructSignatures.length) {
writePunctuation(writer, SyntaxKind.OpenBraceToken);
writePunctuation(writer, SyntaxKind.CloseBraceToken);
return;
}
if (resolved.callSignatures.length === 1 && !resolved.constructSignatures.length) {
if (flags & TypeFormatFlags.InElementType) {
writePunctuation(writer, SyntaxKind.OpenParenToken);
}
buildSignatureDisplay(resolved.callSignatures[0], writer, enclosingDeclaration, globalFlagsToPass | TypeFormatFlags.WriteArrowStyleSignature, symbolStack);
if (flags & TypeFormatFlags.InElementType) {
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, symbolStack);
if (flags & TypeFormatFlags.InElementType) {
writePunctuation(writer, SyntaxKind.CloseParenToken);
}
return;
}
}
writePunctuation(writer, SyntaxKind.OpenBraceToken);
writer.writeLine();
writer.increaseIndent();
for (let signature of resolved.callSignatures) {
buildSignatureDisplay(signature, writer, enclosingDeclaration, globalFlagsToPass, symbolStack);
writePunctuation(writer, SyntaxKind.SemicolonToken);
writer.writeLine();
}
for (let signature of resolved.constructSignatures) {
writeKeyword(writer, SyntaxKind.NewKeyword);
writeSpace(writer);
buildSignatureDisplay(signature, writer, enclosingDeclaration, globalFlagsToPass, symbolStack);
writePunctuation(writer, SyntaxKind.SemicolonToken);
writer.writeLine();
}
if (resolved.stringIndexType) {
// [x: string]:
writePunctuation(writer, SyntaxKind.OpenBracketToken);
writer.writeParameter(getIndexerParameterName(resolved, IndexKind.String, /*fallbackName*/"x"));
writePunctuation(writer, SyntaxKind.ColonToken);
writeSpace(writer);
writeKeyword(writer, SyntaxKind.StringKeyword);
writePunctuation(writer, SyntaxKind.CloseBracketToken);
writePunctuation(writer, SyntaxKind.ColonToken);
writeSpace(writer);
writeType(resolved.stringIndexType, TypeFormatFlags.None);
writePunctuation(writer, SyntaxKind.SemicolonToken);
writer.writeLine();
}
if (resolved.numberIndexType) {
// [x: number]:
writePunctuation(writer, SyntaxKind.OpenBracketToken);
writer.writeParameter(getIndexerParameterName(resolved, IndexKind.Number, /*fallbackName*/"x"));
writePunctuation(writer, SyntaxKind.ColonToken);
writeSpace(writer);
writeKeyword(writer, SyntaxKind.NumberKeyword);
writePunctuation(writer, SyntaxKind.CloseBracketToken);
writePunctuation(writer, SyntaxKind.ColonToken);
writeSpace(writer);
writeType(resolved.numberIndexType, TypeFormatFlags.None);
writePunctuation(writer, SyntaxKind.SemicolonToken);
writer.writeLine();
}
for (let p of resolved.properties) {
let t = getTypeOfSymbol(p);
if (p.flags & (SymbolFlags.Function | SymbolFlags.Method) && !getPropertiesOfObjectType(t).length) {
let signatures = getSignaturesOfType(t, SignatureKind.Call);
for (let signature of signatures) {
buildSymbolDisplay(p, writer);
if (p.flags & SymbolFlags.Optional) {
writePunctuation(writer, SyntaxKind.QuestionToken);
}
buildSignatureDisplay(signature, writer, enclosingDeclaration, globalFlagsToPass, symbolStack);
writePunctuation(writer, SyntaxKind.SemicolonToken);
writer.writeLine();
}
}
else {
buildSymbolDisplay(p, writer);
if (p.flags & SymbolFlags.Optional) {
writePunctuation(writer, SyntaxKind.QuestionToken);
}
writePunctuation(writer, SyntaxKind.ColonToken);
writeSpace(writer);
writeType(t, TypeFormatFlags.None);
writePunctuation(writer, SyntaxKind.SemicolonToken);
writer.writeLine();
}
}
writer.decreaseIndent();
writePunctuation(writer, SyntaxKind.CloseBraceToken);
}
}
function buildTypeParameterDisplayFromSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaraiton?: Node, flags?: TypeFormatFlags) {
let targetSymbol = getTargetSymbol(symbol);
if (targetSymbol.flags & SymbolFlags.Class || targetSymbol.flags & SymbolFlags.Interface) {
buildDisplayForTypeParametersAndDelimiters(getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol), writer, enclosingDeclaraiton, flags);
}
}
function buildTypeParameterDisplay(tp: TypeParameter, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) {
appendSymbolNameOnly(tp.symbol, writer);
let constraint = getConstraintOfTypeParameter(tp);
if (constraint) {
writeSpace(writer);
writeKeyword(writer, SyntaxKind.ExtendsKeyword);
writeSpace(writer);
buildTypeDisplay(constraint, writer, enclosingDeclaration, flags, symbolStack);
}
}
function buildParameterDisplay(p: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) {
let parameterNode = p.valueDeclaration;
if (isRestParameter(parameterNode)) {
writePunctuation(writer, SyntaxKind.DotDotDotToken);
}
appendSymbolNameOnly(p, writer);
if (isOptionalParameter(parameterNode)) {
writePunctuation(writer, SyntaxKind.QuestionToken);
}
writePunctuation(writer, SyntaxKind.ColonToken);
writeSpace(writer);
buildTypeDisplay(getTypeOfSymbol(p), writer, enclosingDeclaration, flags, symbolStack);
}
function buildDisplayForTypeParametersAndDelimiters(typeParameters: TypeParameter[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) {
if (typeParameters && typeParameters.length) {
writePunctuation(writer, SyntaxKind.LessThanToken);
for (let i = 0; i < typeParameters.length; i++) {
if (i > 0) {
writePunctuation(writer, SyntaxKind.CommaToken);
writeSpace(writer);
}
buildTypeParameterDisplay(typeParameters[i], writer, enclosingDeclaration, flags, symbolStack);
}
writePunctuation(writer, SyntaxKind.GreaterThanToken);
}
}
function buildDisplayForTypeArgumentsAndDelimiters(typeParameters: TypeParameter[], mapper: TypeMapper, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) {
if (typeParameters && typeParameters.length) {
writePunctuation(writer, SyntaxKind.LessThanToken);
for (let i = 0; i < typeParameters.length; i++) {
if (i > 0) {
writePunctuation(writer, SyntaxKind.CommaToken);
writeSpace(writer);
}
buildTypeDisplay(mapper(typeParameters[i]), writer, enclosingDeclaration, TypeFormatFlags.None);
}
writePunctuation(writer, SyntaxKind.GreaterThanToken);
}
}
function buildDisplayForParametersAndDelimiters(parameters: Symbol[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) {
writePunctuation(writer, SyntaxKind.OpenParenToken);
for (let i = 0; i < parameters.length; i++) {
if (i > 0) {
writePunctuation(writer, SyntaxKind.CommaToken);
writeSpace(writer);
}
buildParameterDisplay(parameters[i], writer, enclosingDeclaration, flags, symbolStack);
}
writePunctuation(writer, SyntaxKind.CloseParenToken);
}
function buildReturnTypeDisplay(signature: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) {
if (flags & TypeFormatFlags.WriteArrowStyleSignature) {
writeSpace(writer);
writePunctuation(writer, SyntaxKind.EqualsGreaterThanToken);
}
else {
writePunctuation(writer, SyntaxKind.ColonToken);
}
writeSpace(writer);
buildTypeDisplay(getReturnTypeOfSignature(signature), writer, enclosingDeclaration, flags, symbolStack);
}
function buildSignatureDisplay(signature: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) {
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.parameters, writer, enclosingDeclaration, flags, symbolStack);
buildReturnTypeDisplay(signature, writer, enclosingDeclaration, flags, symbolStack);
}
return _displayBuilder || (_displayBuilder = {
symbolToString: symbolToString,
typeToString: typeToString,
buildSymbolDisplay: buildSymbolDisplay,
buildTypeDisplay: buildTypeDisplay,
buildTypeParameterDisplay: buildTypeParameterDisplay,
buildParameterDisplay: buildParameterDisplay,
buildDisplayForParametersAndDelimiters: buildDisplayForParametersAndDelimiters,
buildDisplayForTypeParametersAndDelimiters: buildDisplayForTypeParametersAndDelimiters,
buildDisplayForTypeArgumentsAndDelimiters: buildDisplayForTypeArgumentsAndDelimiters,
buildTypeParameterDisplayFromSymbol: buildTypeParameterDisplayFromSymbol,
buildSignatureDisplay: buildSignatureDisplay,
buildReturnTypeDisplay: buildReturnTypeDisplay
});
}
function isDeclarationVisible(node: Declaration): boolean {
function getContainingExternalModule(node: Node) {
for (; node; node = node.parent) {
if (node.kind === SyntaxKind.ModuleDeclaration) {
if ((node).name.kind === SyntaxKind.StringLiteral) {
return node;
}
}
else if (node.kind === SyntaxKind.SourceFile) {
return isExternalModule(node) ? node : undefined;
}
}
Debug.fail("getContainingModule cant reach here");
}
function isUsedInExportAssignment(node: Node) {
// Get source File and see if it is external module and has export assigned symbol
let externalModule = getContainingExternalModule(node);
let exportAssignmentSymbol: Symbol;
let resolvedExportSymbol: Symbol;
if (externalModule) {
// This is export assigned symbol node
let externalModuleSymbol = getSymbolOfNode(externalModule);
exportAssignmentSymbol = getExportAssignmentSymbol(externalModuleSymbol);
let symbolOfNode = getSymbolOfNode(node);
if (isSymbolUsedInExportAssignment(symbolOfNode)) {
return true;
}
// if symbolOfNode is alias declaration, resolve the symbol declaration and check
if (symbolOfNode.flags & SymbolFlags.Alias) {
return isSymbolUsedInExportAssignment(resolveAlias(symbolOfNode));
}
}
// Check if the symbol is used in export assignment
function isSymbolUsedInExportAssignment(symbol: Symbol) {
if (exportAssignmentSymbol === symbol) {
return true;
}
if (exportAssignmentSymbol && !!(exportAssignmentSymbol.flags & SymbolFlags.Alias)) {
// if export assigned symbol is alias declaration, resolve the alias
resolvedExportSymbol = resolvedExportSymbol || resolveAlias(exportAssignmentSymbol);
if (resolvedExportSymbol === symbol) {
return true;
}
// Container of resolvedExportSymbol is visible
return forEach(resolvedExportSymbol.declarations, (current: Node) => {
while (current) {
if (current === node) {
return true;
}
current = current.parent;
}
});
}
}
}
function determineIfDeclarationIsVisible() {
switch (node.kind) {
case SyntaxKind.BindingElement:
return isDeclarationVisible(node.parent.parent);
case SyntaxKind.VariableDeclaration:
if (isBindingPattern(node.name) &&
!(node.name).elements.length) {
// If the binding pattern is empty, this variable declaration is not visible
return false;
}
// Otherwise fall through
case SyntaxKind.ModuleDeclaration:
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.TypeAliasDeclaration:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.EnumDeclaration:
case SyntaxKind.ImportEqualsDeclaration:
let parent = getDeclarationContainer(node);
// If the node is not exported or it is not ambient module element (except import declaration)
if (!(getCombinedNodeFlags(node) & NodeFlags.Export) &&
!(node.kind !== SyntaxKind.ImportEqualsDeclaration && parent.kind !== SyntaxKind.SourceFile && isInAmbientContext(parent))) {
return isGlobalSourceFile(parent);
}
// Exported members/ambient module elements (exception import declaration) are visible if parent is visible
return isDeclarationVisible(parent);
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.PropertySignature:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
if (node.flags & (NodeFlags.Private | NodeFlags.Protected)) {
// Private/protected properties/methods are not visible
return false;
}
// Public properties/methods are visible if its parents are visible, so let it fall into next case statement
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 is always visible
case SyntaxKind.SourceFile:
return true;
// Export assignements do not create name bindings outside the module
case SyntaxKind.ExportAssignment:
return false;
default:
Debug.fail("isDeclarationVisible unknown: SyntaxKind: " + node.kind);
}
}
if (node) {
let links = getNodeLinks(node);
if (links.isVisible === undefined) {
links.isVisible = !!determineIfDeclarationIsVisible();
}
return links.isVisible;
}
}
function collectLinkedAliases(node: Identifier): Node[] {
let exportSymbol: Symbol;
if (node.parent && node.parent.kind === SyntaxKind.ExportAssignment) {
exportSymbol = resolveName(node.parent, node.text, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace, Diagnostics.Cannot_find_name_0, node);
}
else if (node.parent.kind === SyntaxKind.ExportSpecifier) {
exportSymbol = getTargetOfExportSpecifier(node.parent);
}
let result: Node[] = [];
if (exportSymbol) {
buildVisibleNodeList(exportSymbol.declarations);
}
return result;
function buildVisibleNodeList(declarations: Declaration[]) {
forEach(declarations, declaration => {
getNodeLinks(declaration).isVisible = true;
let resultNode = getAnyImportSyntax(declaration) || declaration;
if (!contains(result, resultNode)) {
result.push(resultNode);
}
if (isInternalModuleImportEqualsDeclaration(declaration)) {
// Add the referenced top container visible
let internalModuleReference = (declaration).moduleReference;
let firstIdentifier = getFirstIdentifier(internalModuleReference);
let importSymbol = resolveName(declaration, firstIdentifier.text, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace,
Diagnostics.Cannot_find_name_0, firstIdentifier);
buildVisibleNodeList(importSymbol.declarations);
}
});
}
}
// Push an entry on the type resolution stack. If an entry with the given target is not already on the stack,
// a new entry with that target and an associated result value of true is pushed on the stack, and the value
// true is returned. Otherwise, a circularity has occurred and the result values of the existing entry and
// all entries pushed after it are changed to false, and the value false is returned. The target object provides
// a unique identity for a particular type resolution result: Symbol instances are used to track resolution of
// SymbolLinks.type, SymbolLinks instances are used to track resolution of SymbolLinks.declaredType, and
// Signature instances are used to track resolution of Signature.resolvedReturnType.
function pushTypeResolution(target: Object): boolean {
let i = 0;
let count = resolutionTargets.length;
while (i < count && resolutionTargets[i] !== target) {
i++;
}
if (i < count) {
do {
resolutionResults[i++] = false;
}
while (i < count);
return false;
}
resolutionTargets.push(target);
resolutionResults.push(true);
return true;
}
// 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();
return resolutionResults.pop();
}
function getDeclarationContainer(node: Node): Node {
node = getRootDeclaration(node);
// Parent chain:
// VaribleDeclaration -> VariableDeclarationList -> VariableStatement -> 'Declaration Container'
return node.kind === SyntaxKind.VariableDeclaration ? node.parent.parent.parent : 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'.
let classType = getDeclaredTypeOfSymbol(prototype.parent);
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 {
let prop = getPropertyOfType(type, name);
return prop ? getTypeOfSymbol(prop) : undefined;
}
function isTypeAny(type: Type) {
return type && (type.flags & TypeFlags.Any) !== 0;
}
// Return the inferred type for a binding element
function getTypeForBindingElement(declaration: BindingElement): Type {
let pattern = declaration.parent;
let parentType = getTypeForVariableLikeDeclaration(pattern.parent);
// If parent has the unknown (error) type, then so does this binding element
if (parentType === unknownType) {
return unknownType;
}
// If no type was specified or inferred for parent, or if the specified or inferred type is any,
// infer from the initializer of the binding element if one is present. Otherwise, go with the
// undefined or any type of the parent.
if (!parentType || isTypeAny(parentType)) {
if (declaration.initializer) {
return checkExpressionCached(declaration.initializer);
}
return parentType;
}
let type: Type;
if (pattern.kind === SyntaxKind.ObjectBindingPattern) {
// Use explicitly specified property name ({ p: xxx } form), or otherwise the implied name ({ p } form)
let name = declaration.propertyName || declaration.name;
// 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.
type = getTypeOfPropertyOfType(parentType, name.text) ||
isNumericLiteralName(name.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).
let elementType = checkIteratedTypeOrElementType(parentType, pattern, /*allowStringInput*/ false);
if (!declaration.dotDotDotToken) {
if (isTypeAny(elementType)) {
return elementType;
}
// Use specific property type when parent is a tuple or numeric index type when parent is an array
let propName = "" + indexOf(pattern.elements, declaration);
type = isTupleLikeType(parentType)
? getTypeOfPropertyOfType(parentType, propName)
: elementType;
if (!type) {
if (isTupleType(parentType)) {
error(declaration, Diagnostics.Tuple_type_0_with_length_1_cannot_be_assigned_to_tuple_with_length_2, typeToString(parentType), (parentType).elementTypes.length, pattern.elements.length);
}
else {
error(declaration, Diagnostics.Type_0_has_no_property_1, typeToString(parentType), propName);
}
return unknownType;
}
}
else {
// Rest element has an array type with the same element type as the parent type
type = createArrayType(elementType);
}
}
return type;
}
// Return the inferred type for a variable, parameter, or property declaration
function getTypeForVariableLikeDeclaration(declaration: VariableLikeDeclaration): Type {
// A variable declared in a for..in statement is always of type any
if (declaration.parent.parent.kind === SyntaxKind.ForInStatement) {
return anyType;
}
if (declaration.parent.parent.kind === SyntaxKind.ForOfStatement) {
// checkRightHandSideOfForOf will return undefined if the for-of expression type was
// missing properties/signatures required to get its iteratedType (like
// [Symbol.iterator] or next). This may be because we accessed properties from anyType,
// or it may have led to an error inside getElementTypeOfIterable.
return checkRightHandSideOfForOf((declaration.parent.parent).expression) || anyType;
}
if (isBindingPattern(declaration.parent)) {
return getTypeForBindingElement(declaration);
}
// Use type from type annotation if one is present
if (declaration.type) {
return getTypeFromTypeNode(declaration.type);
}
if (declaration.kind === SyntaxKind.Parameter) {
let func = declaration.parent;
// For a parameter of a set accessor, use the type of the get accessor if one is present
if (func.kind === SyntaxKind.SetAccessor && !hasDynamicName(func)) {
let getter = getDeclarationOfKind(declaration.parent.symbol, SyntaxKind.GetAccessor);
if (getter) {
return getReturnTypeOfSignature(getSignatureFromDeclaration(getter));
}
}
// Use contextual parameter type if one is available
let type = getContextuallyTypedParameterType(declaration);
if (type) {
return type;
}
}
// Use the type of the initializer expression if one is present
if (declaration.initializer) {
return checkExpressionCached(declaration.initializer);
}
// If it is a short-hand property assignment, use the type of the identifier
if (declaration.kind === SyntaxKind.ShorthandPropertyAssignment) {
return checkIdentifier(declaration.name);
}
// No type specified and nothing can be inferred
return undefined;
}
// 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): Type {
if (element.initializer) {
return getWidenedType(checkExpressionCached(element.initializer));
}
if (isBindingPattern(element.name)) {
return getTypeFromBindingPattern(element.name);
}
return anyType;
}
// Return the type implied by an object binding pattern
function getTypeFromObjectBindingPattern(pattern: BindingPattern): Type {
let members: SymbolTable = {};
forEach(pattern.elements, e => {
let flags = SymbolFlags.Property | SymbolFlags.Transient | (e.initializer ? SymbolFlags.Optional : 0);
let name = e.propertyName || e.name;
let symbol = createSymbol(flags, name.text);
symbol.type = getTypeFromBindingElement(e);
members[symbol.name] = symbol;
});
return createAnonymousType(undefined, members, emptyArray, emptyArray, undefined, undefined);
}
// Return the type implied by an array binding pattern
function getTypeFromArrayBindingPattern(pattern: BindingPattern): Type {
let hasSpreadElement: boolean = false;
let elementTypes: Type[] = [];
forEach(pattern.elements, e => {
elementTypes.push(e.kind === SyntaxKind.OmittedExpression || e.dotDotDotToken ? anyType : getTypeFromBindingElement(e));
if (e.dotDotDotToken) {
hasSpreadElement = true;
}
});
if (!elementTypes.length) {
return languageVersion >= ScriptTarget.ES6 ? createIterableType(anyType) : anyArrayType;
}
else if (hasSpreadElement) {
let unionOfElements = getUnionType(elementTypes);
return languageVersion >= ScriptTarget.ES6 ? createIterableType(unionOfElements) : createArrayType(unionOfElements);
}
// If the pattern has at least one element, and no rest element, then it should imply a tuple type.
return createTupleType(elementTypes);
}
// 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): Type {
return pattern.kind === SyntaxKind.ObjectBindingPattern
? getTypeFromObjectBindingPattern(pattern)
: getTypeFromArrayBindingPattern(pattern);
}
// Return the type associated with a variable, parameter, or property declaration. In the simple case this is the type
// specified in a type annotation or inferred from an initializer. However, in the case of a destructuring declaration it
// is a bit more involved. For example:
//
// var [x, s = ""] = [1, "one"];
//
// Here, the array literal [1, "one"] is contextually typed by the type [any, string], which is the implied type of the
// binding pattern [x, s = ""]. Because the contextual type is a tuple type, the resulting type of [1, "one"] is the
// tuple type [number, string]. Thus, the type inferred for 'x' is number and the type inferred for 's' is string.
function getWidenedTypeForVariableLikeDeclaration(declaration: VariableLikeDeclaration, reportErrors?: boolean): Type {
let type = getTypeForVariableLikeDeclaration(declaration);
if (type) {
if (reportErrors) {
reportErrorsFromWidening(declaration, type);
}
// During a normal type check we'll never get to here with a property assignment (the check of the containing
// object literal uses a different path). We exclude widening only so that language services and type verification
// tools see the actual type.
return declaration.kind !== SyntaxKind.PropertyAssignment ? getWidenedType(type) : type;
}
// If no type was specified and nothing could be inferred, and if the declaration specifies a binding pattern, use
// the type implied by the binding pattern
if (isBindingPattern(declaration.name)) {
return getTypeFromBindingPattern(declaration.name);
}
// Rest parameters default to type any[], other parameters default to type any
type = declaration.dotDotDotToken ? anyArrayType : anyType;
// Report implicit any errors unless this is a private property within an ambient declaration
if (reportErrors && compilerOptions.noImplicitAny) {
let root = getRootDeclaration(declaration);
if (!isPrivateWithinAmbient(root) && !(root.kind === SyntaxKind.Parameter && isPrivateWithinAmbient(root.parent))) {
reportImplicitAnyError(declaration, type);
}
}
return type;
}
function getTypeOfVariableOrParameterOrProperty(symbol: Symbol): Type {
let links = getSymbolLinks(symbol);
if (!links.type) {
// Handle prototype property
if (symbol.flags & SymbolFlags.Prototype) {
return links.type = getTypeOfPrototypeProperty(symbol);
}
// Handle catch clause variables
let declaration = symbol.valueDeclaration;
if (declaration.parent.kind === SyntaxKind.CatchClause) {
return links.type = anyType;
}
// Handle export default expressions
if (declaration.kind === SyntaxKind.ExportAssignment) {
return links.type = checkExpression((declaration).expression);
}
// Handle variable, parameter or property
if (!pushTypeResolution(symbol)) {
return unknownType;
}
let type = getWidenedTypeForVariableLikeDeclaration(declaration, /*reportErrors*/ true);
if (!popTypeResolution()) {
if ((symbol.valueDeclaration).type) {
// Variable has type annotation that circularly references the variable itself
type = unknownType;
error(symbol.valueDeclaration, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_type_annotation,
symbolToString(symbol));
}
else {
// Variable has initializer that circularly references the variable itself
type = anyType;
if (compilerOptions.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));
}
}
}
links.type = type;
}
return links.type;
}
function getSetAccessorTypeAnnotationNode(accessor: AccessorDeclaration): TypeNode {
return accessor && accessor.parameters.length > 0 && accessor.parameters[0].type;
}
function getAnnotatedAccessorType(accessor: AccessorDeclaration): Type {
if (accessor) {
if (accessor.kind === SyntaxKind.GetAccessor) {
return accessor.type && getTypeFromTypeNode(accessor.type);
}
else {
let setterTypeAnnotation = getSetAccessorTypeAnnotationNode(accessor);
return setterTypeAnnotation && getTypeFromTypeNode(setterTypeAnnotation);
}
}
return undefined;
}
function getTypeOfAccessors(symbol: Symbol): Type {
let links = getSymbolLinks(symbol);
if (!links.type) {
if (!pushTypeResolution(symbol)) {
return unknownType;
}
let getter = getDeclarationOfKind(symbol, SyntaxKind.GetAccessor);
let setter = getDeclarationOfKind(symbol, SyntaxKind.SetAccessor);
let type: Type;
// First try to see if the user specified a return type on the get-accessor.
let 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.
let 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 (compilerOptions.noImplicitAny) {
error(setter, Diagnostics.Property_0_implicitly_has_type_any_because_its_set_accessor_lacks_a_type_annotation, symbolToString(symbol));
}
type = anyType;
}
}
}
if (!popTypeResolution()) {
type = anyType;
if (compilerOptions.noImplicitAny) {
let 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 getTypeOfFuncClassEnumModule(symbol: Symbol): Type {
let links = getSymbolLinks(symbol);
if (!links.type) {
links.type = createObjectType(TypeFlags.Anonymous, symbol);
}
return links.type;
}
function getTypeOfEnumMember(symbol: Symbol): Type {
let links = getSymbolLinks(symbol);
if (!links.type) {
links.type = getDeclaredTypeOfEnum(getParentOfSymbol(symbol));
}
return links.type;
}
function getTypeOfAlias(symbol: Symbol): Type {
let links = getSymbolLinks(symbol);
if (!links.type) {
let 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 {
let links = getSymbolLinks(symbol);
if (!links.type) {
links.type = instantiateType(getTypeOfSymbol(links.target), links.mapper);
}
return links.type;
}
function getTypeOfSymbol(symbol: Symbol): Type {
if (symbol.flags & SymbolFlags.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 getTargetType(type: ObjectType): Type {
return type.flags & TypeFlags.Reference ? (type).target : type;
}
function hasBaseType(type: InterfaceType, checkBase: InterfaceType) {
return check(type);
function check(type: InterfaceType): boolean {
let target = getTargetType(type);
return target === checkBase || forEach(getBaseTypes(target), check);
}
}
// Appends the type parameters given by a list of declarations to a set of type parameters and returns the resulting set.
// The function allocates a new array if the input type parameter set is undefined, but otherwise it modifies the set
// in-place and returns the same array.
function appendTypeParameters(typeParameters: TypeParameter[], declarations: TypeParameterDeclaration[]): TypeParameter[] {
for (let declaration of declarations) {
let tp = getDeclaredTypeOfTypeParameter(getSymbolOfNode(declaration));
if (!typeParameters) {
typeParameters = [tp];
}
else if (!contains(typeParameters, tp)) {
typeParameters.push(tp);
}
}
return typeParameters;
}
// Appends the outer type parameters of a node to a set of type parameters and returns the resulting set. The function
// allocates a new array if the input type parameter set is undefined, but otherwise it modifies the set in-place and
// returns the same array.
function appendOuterTypeParameters(typeParameters: TypeParameter[], node: Node): TypeParameter[] {
while (true) {
node = node.parent;
if (!node) {
return typeParameters;
}
if (node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.ClassExpression ||
node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.FunctionExpression ||
node.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.ArrowFunction) {
let declarations = (node).typeParameters;
if (declarations) {
return appendTypeParameters(appendOuterTypeParameters(typeParameters, node), declarations);
}
}
}
}
// The outer type parameters are those defined by enclosing generic classes, methods, or functions.
function getOuterTypeParametersOfClassOrInterface(symbol: Symbol): TypeParameter[] {
let declaration = symbol.flags & SymbolFlags.Class ? symbol.valueDeclaration : getDeclarationOfKind(symbol, SyntaxKind.InterfaceDeclaration);
return appendOuterTypeParameters(undefined, declaration);
}
// The local type parameters are the combined set of type parameters from all declarations of the class,
// interface, or type alias.
function getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol: Symbol): TypeParameter[] {
let result: TypeParameter[];
for (let node of symbol.declarations) {
if (node.kind === SyntaxKind.InterfaceDeclaration || node.kind === SyntaxKind.ClassDeclaration ||
node.kind === SyntaxKind.ClassExpression || node.kind === SyntaxKind.TypeAliasDeclaration) {
let 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));
}
function isConstructorType(type: Type): boolean {
return type.flags & TypeFlags.ObjectType && getSignaturesOfType(type, SignatureKind.Construct).length > 0;
}
function getBaseTypeNodeOfClass(type: InterfaceType): ExpressionWithTypeArguments {
return getClassExtendsHeritageClauseElement(type.symbol.valueDeclaration);
}
function getConstructorsForTypeArguments(type: ObjectType, typeArgumentNodes: TypeNode[]): Signature[] {
let typeArgCount = typeArgumentNodes ? typeArgumentNodes.length : 0;
return filter(getSignaturesOfType(type, SignatureKind.Construct),
sig => (sig.typeParameters ? sig.typeParameters.length : 0) === typeArgCount);
}
function getInstantiatedConstructorsForTypeArguments(type: ObjectType, typeArgumentNodes: TypeNode[]): Signature[] {
let signatures = getConstructorsForTypeArguments(type, typeArgumentNodes);
if (typeArgumentNodes) {
let typeArguments = map(typeArgumentNodes, getTypeFromTypeNode);
signatures = map(signatures, sig => getSignatureInstantiation(sig, typeArguments));
}
return signatures;
}
// The base constructor of a class can resolve to
// undefinedType if the class has no extends clause,
// unknownType if an error occurred during resolution of the extends expression,
// nullType if the extends expression is the null value, or
// an object type with at least one construct signature.
function getBaseConstructorTypeOfClass(type: InterfaceType): ObjectType {
if (!type.resolvedBaseConstructorType) {
let baseTypeNode = getBaseTypeNodeOfClass(type);
if (!baseTypeNode) {
return type.resolvedBaseConstructorType = undefinedType;
}
if (!pushTypeResolution(type)) {
return unknownType;
}
let baseConstructorType = checkExpression(baseTypeNode.expression);
if (baseConstructorType.flags & TypeFlags.ObjectType) {
// 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 !== unknownType && baseConstructorType !== nullType && !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): ObjectType[] {
if (!type.resolvedBaseTypes) {
if (type.symbol.flags & SymbolFlags.Class) {
resolveBaseTypesOfClass(type);
}
else if (type.symbol.flags & SymbolFlags.Interface) {
resolveBaseTypesOfInterface(type);
}
else {
Debug.fail("type must be class or interface");
}
}
return type.resolvedBaseTypes;
}
function resolveBaseTypesOfClass(type: InterfaceType): void {
type.resolvedBaseTypes = emptyArray;
let baseContructorType = getBaseConstructorTypeOfClass(type);
if (!(baseContructorType.flags & TypeFlags.ObjectType)) {
return;
}
let baseTypeNode = getBaseTypeNodeOfClass(type);
let baseType: Type;
if (baseContructorType.symbol && baseContructorType.symbol.flags & SymbolFlags.Class) {
// When base constructor type is a class 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, baseContructorType.symbol);
}
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.
let constructors = getInstantiatedConstructorsForTypeArguments(baseContructorType, baseTypeNode.typeArguments);
if (!constructors.length) {
error(baseTypeNode.expression, Diagnostics.No_base_constructor_has_the_specified_number_of_type_arguments);
return;
}
baseType = getReturnTypeOfSignature(constructors[0]);
}
if (baseType === unknownType) {
return;
}
if (!(getTargetType(baseType).flags & (TypeFlags.Class | TypeFlags.Interface))) {
error(baseTypeNode.expression, Diagnostics.Base_constructor_return_type_0_is_not_a_class_or_interface_type, typeToString(baseType));
return;
}
if (type === baseType || hasBaseType(baseType, type)) {
error(type.symbol.valueDeclaration, Diagnostics.Type_0_recursively_references_itself_as_a_base_type,
typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType));
return;
}
type.resolvedBaseTypes = [baseType];
}
function resolveBaseTypesOfInterface(type: InterfaceType): void {
type.resolvedBaseTypes = [];
for (let declaration of type.symbol.declarations) {
if (declaration.kind === SyntaxKind.InterfaceDeclaration && getInterfaceBaseTypeNodes(declaration)) {
for (let node of getInterfaceBaseTypeNodes(declaration)) {
let baseType = getTypeFromTypeNode(node);
if (baseType !== unknownType) {
if (getTargetType(baseType).flags & (TypeFlags.Class | TypeFlags.Interface)) {
if (type !== baseType && !hasBaseType(baseType, type)) {
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);
}
}
}
}
}
}
function getDeclaredTypeOfClassOrInterface(symbol: Symbol): InterfaceType {
let links = getSymbolLinks(symbol);
if (!links.declaredType) {
let kind = symbol.flags & SymbolFlags.Class ? TypeFlags.Class : TypeFlags.Interface;
let type = links.declaredType = createObjectType(kind, symbol);
let outerTypeParameters = getOuterTypeParametersOfClassOrInterface(symbol);
let localTypeParameters = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol);
if (outerTypeParameters || localTypeParameters) {
type.flags |= TypeFlags.Reference;
type.typeParameters = concatenate(outerTypeParameters, localTypeParameters);
type.outerTypeParameters = outerTypeParameters;
type.localTypeParameters = localTypeParameters;
(type).instantiations = {};
(type).instantiations[getTypeListId(type.typeParameters)] = type;
(type).target = type;
(type).typeArguments = type.typeParameters;
}
}
return links.declaredType;
}
function getDeclaredTypeOfTypeAlias(symbol: Symbol): Type {
let 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(links)) {
return unknownType;
}
let declaration = getDeclarationOfKind(symbol, SyntaxKind.TypeAliasDeclaration);
let type = getTypeFromTypeNode(declaration.type);
if (popTypeResolution()) {
links.typeParameters = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol);
if (links.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.instantiations = {};
links.instantiations[getTypeListId(links.typeParameters)] = type;
}
}
else {
type = unknownType;
error(declaration.name, Diagnostics.Type_alias_0_circularly_references_itself, symbolToString(symbol));
}
links.declaredType = type;
}
return links.declaredType;
}
function getDeclaredTypeOfEnum(symbol: Symbol): Type {
let links = getSymbolLinks(symbol);
if (!links.declaredType) {
let type = createType(TypeFlags.Enum);
type.symbol = symbol;
links.declaredType = type;
}
return links.declaredType;
}
function getDeclaredTypeOfTypeParameter(symbol: Symbol): TypeParameter {
let links = getSymbolLinks(symbol);
if (!links.declaredType) {
let type = createType(TypeFlags.TypeParameter);
type.symbol = symbol;
if (!(getDeclarationOfKind(symbol, SyntaxKind.TypeParameter)).constraint) {
type.constraint = noConstraintType;
}
links.declaredType = type;
}
return links.declaredType;
}
function getDeclaredTypeOfAlias(symbol: Symbol): Type {
let links = getSymbolLinks(symbol);
if (!links.declaredType) {
links.declaredType = getDeclaredTypeOfSymbol(resolveAlias(symbol));
}
return links.declaredType;
}
function getDeclaredTypeOfSymbol(symbol: Symbol): Type {
Debug.assert((symbol.flags & SymbolFlags.Instantiated) === 0);
if (symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) {
return getDeclaredTypeOfClassOrInterface(symbol);
}
if (symbol.flags & SymbolFlags.TypeAlias) {
return getDeclaredTypeOfTypeAlias(symbol);
}
if (symbol.flags & SymbolFlags.Enum) {
return getDeclaredTypeOfEnum(symbol);
}
if (symbol.flags & SymbolFlags.TypeParameter) {
return getDeclaredTypeOfTypeParameter(symbol);
}
if (symbol.flags & SymbolFlags.Alias) {
return getDeclaredTypeOfAlias(symbol);
}
return unknownType;
}
function createSymbolTable(symbols: Symbol[]): SymbolTable {
let result: SymbolTable = {};
for (let symbol of symbols) {
result[symbol.name] = symbol;
}
return result;
}
function createInstantiatedSymbolTable(symbols: Symbol[], mapper: TypeMapper): SymbolTable {
let result: SymbolTable = {};
for (let symbol of symbols) {
result[symbol.name] = instantiateSymbol(symbol, mapper);
}
return result;
}
function addInheritedMembers(symbols: SymbolTable, baseSymbols: Symbol[]) {
for (let s of baseSymbols) {
if (!hasProperty(symbols, s.name)) {
symbols[s.name] = s;
}
}
}
function addInheritedSignatures(signatures: Signature[], baseSignatures: Signature[]) {
if (baseSignatures) {
for (let signature of baseSignatures) {
signatures.push(signature);
}
}
}
function resolveDeclaredMembers(type: InterfaceType): InterfaceTypeWithDeclaredMembers {
if (!(type).declaredProperties) {
let symbol = type.symbol;
(type).declaredProperties = getNamedMembers(symbol.members);
(type).declaredCallSignatures = getSignaturesOfSymbol(symbol.members["__call"]);
(type).declaredConstructSignatures = getSignaturesOfSymbol(symbol.members["__new"]);
(type).declaredStringIndexType = getIndexTypeOfSymbol(symbol, IndexKind.String);
(type).declaredNumberIndexType = getIndexTypeOfSymbol(symbol, IndexKind.Number);
}
return type;
}
function resolveClassOrInterfaceMembers(type: InterfaceType): void {
let target = resolveDeclaredMembers(type);
let members = target.symbol.members;
let callSignatures = target.declaredCallSignatures;
let constructSignatures = target.declaredConstructSignatures;
let stringIndexType = target.declaredStringIndexType;
let numberIndexType = target.declaredNumberIndexType;
let baseTypes = getBaseTypes(target);
if (baseTypes.length) {
members = createSymbolTable(target.declaredProperties);
for (let baseType of baseTypes) {
addInheritedMembers(members, getPropertiesOfObjectType(baseType));
callSignatures = concatenate(callSignatures, getSignaturesOfType(baseType, SignatureKind.Call));
constructSignatures = concatenate(constructSignatures, getSignaturesOfType(baseType, SignatureKind.Construct));
stringIndexType = stringIndexType || getIndexTypeOfType(baseType, IndexKind.String);
numberIndexType = numberIndexType || getIndexTypeOfType(baseType, IndexKind.Number);
}
}
setObjectTypeMembers(type, members, callSignatures, constructSignatures, stringIndexType, numberIndexType);
}
function resolveTypeReferenceMembers(type: TypeReference): void {
let target = resolveDeclaredMembers(type.target);
let mapper = createTypeMapper(target.typeParameters, type.typeArguments);
let members = createInstantiatedSymbolTable(target.declaredProperties, mapper);
let callSignatures = instantiateList(target.declaredCallSignatures, mapper, instantiateSignature);
let constructSignatures = instantiateList(target.declaredConstructSignatures, mapper, instantiateSignature);
let stringIndexType = target.declaredStringIndexType ? instantiateType(target.declaredStringIndexType, mapper) : undefined;
let numberIndexType = target.declaredNumberIndexType ? instantiateType(target.declaredNumberIndexType, mapper) : undefined;
forEach(getBaseTypes(target), baseType => {
let instantiatedBaseType = instantiateType(baseType, mapper);
addInheritedMembers(members, getPropertiesOfObjectType(instantiatedBaseType));
callSignatures = concatenate(callSignatures, getSignaturesOfType(instantiatedBaseType, SignatureKind.Call));
constructSignatures = concatenate(constructSignatures, getSignaturesOfType(instantiatedBaseType, SignatureKind.Construct));
stringIndexType = stringIndexType || getIndexTypeOfType(instantiatedBaseType, IndexKind.String);
numberIndexType = numberIndexType || getIndexTypeOfType(instantiatedBaseType, IndexKind.Number);
});
setObjectTypeMembers(type, members, callSignatures, constructSignatures, stringIndexType, numberIndexType);
}
function createSignature(declaration: SignatureDeclaration, typeParameters: TypeParameter[], parameters: Symbol[],
resolvedReturnType: Type, typePredicate: TypePredicate, minArgumentCount: number, hasRestParameter: boolean, hasStringLiterals: boolean): Signature {
let sig = new Signature(checker);
sig.declaration = declaration;
sig.typeParameters = typeParameters;
sig.parameters = parameters;
sig.resolvedReturnType = resolvedReturnType;
sig.typePredicate = typePredicate;
sig.minArgumentCount = minArgumentCount;
sig.hasRestParameter = hasRestParameter;
sig.hasStringLiterals = hasStringLiterals;
return sig;
}
function cloneSignature(sig: Signature): Signature {
return createSignature(sig.declaration, sig.typeParameters, sig.parameters, sig.resolvedReturnType, sig.typePredicate,
sig.minArgumentCount, sig.hasRestParameter, sig.hasStringLiterals);
}
function getDefaultConstructSignatures(classType: InterfaceType): Signature[] {
if (!getBaseTypes(classType).length) {
return [createSignature(undefined, classType.localTypeParameters, emptyArray, classType, undefined, 0, false, false)];
}
let baseConstructorType = getBaseConstructorTypeOfClass(classType);
let baseSignatures = getSignaturesOfType(baseConstructorType, SignatureKind.Construct);
let baseTypeNode = getBaseTypeNodeOfClass(classType);
let typeArguments = map(baseTypeNode.typeArguments, getTypeFromTypeNode);
let typeArgCount = typeArguments ? typeArguments.length : 0;
let result: Signature[] = [];
for (let baseSig of baseSignatures) {
let typeParamCount = baseSig.typeParameters ? baseSig.typeParameters.length : 0;
if (typeParamCount === typeArgCount) {
let sig = typeParamCount ? getSignatureInstantiation(baseSig, typeArguments) : cloneSignature(baseSig);
sig.typeParameters = classType.localTypeParameters;
sig.resolvedReturnType = classType;
result.push(sig);
}
}
return result;
}
function createTupleTypeMemberSymbols(memberTypes: Type[]): SymbolTable {
let members: SymbolTable = {};
for (let i = 0; i < memberTypes.length; i++) {
let symbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "" + i);
symbol.type = memberTypes[i];
members[i] = symbol;
}
return members;
}
function resolveTupleTypeMembers(type: TupleType) {
let arrayType = resolveStructuredTypeMembers(createArrayType(getUnionType(type.elementTypes)));
let members = createTupleTypeMemberSymbols(type.elementTypes);
addInheritedMembers(members, arrayType.properties);
setObjectTypeMembers(type, members, arrayType.callSignatures, arrayType.constructSignatures, arrayType.stringIndexType, arrayType.numberIndexType);
}
function signatureListsIdentical(s: Signature[], t: Signature[]): boolean {
if (s.length !== t.length) {
return false;
}
for (let i = 0; i < s.length; i++) {
if (!compareSignatures(s[i], t[i], /*compareReturnTypes*/ false, compareTypes)) {
return false;
}
}
return true;
}
// If the lists of call or construct signatures in the given types are all identical except for return types,
// and if none of the signatures are generic, return a list of signatures that has substitutes a union of the
// return types of the corresponding signatures in each resulting signature.
function getUnionSignatures(types: Type[], kind: SignatureKind): Signature[] {
let signatureLists = map(types, t => getSignaturesOfType(t, kind));
let signatures = signatureLists[0];
for (let signature of signatures) {
if (signature.typeParameters) {
return emptyArray;
}
}
for (let i = 1; i < signatureLists.length; i++) {
if (!signatureListsIdentical(signatures, signatureLists[i])) {
return emptyArray;
}
}
let result = map(signatures, cloneSignature);
for (var i = 0; i < result.length; i++) {
let s = result[i];
// Clear resolved return type we possibly got from cloneSignature
s.resolvedReturnType = undefined;
s.unionSignatures = map(signatureLists, signatures => signatures[i]);
}
return result;
}
function getUnionIndexType(types: Type[], kind: IndexKind): Type {
let indexTypes: Type[] = [];
for (let type of types) {
let indexType = getIndexTypeOfType(type, kind);
if (!indexType) {
return undefined;
}
indexTypes.push(indexType);
}
return getUnionType(indexTypes);
}
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).
let callSignatures = getUnionSignatures(type.types, SignatureKind.Call);
let constructSignatures = getUnionSignatures(type.types, SignatureKind.Construct);
let stringIndexType = getUnionIndexType(type.types, IndexKind.String);
let numberIndexType = getUnionIndexType(type.types, IndexKind.Number);
setObjectTypeMembers(type, emptySymbols, callSignatures, constructSignatures, stringIndexType, numberIndexType);
}
function intersectTypes(type1: Type, type2: Type): Type {
return !type1 ? type2 : !type2 ? type1 : getIntersectionType([type1, type2]);
}
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 stringIndexType: Type = undefined;
let numberIndexType: Type = undefined;
for (let t of type.types) {
callSignatures = concatenate(callSignatures, getSignaturesOfType(t, SignatureKind.Call));
constructSignatures = concatenate(constructSignatures, getSignaturesOfType(t, SignatureKind.Construct));
stringIndexType = intersectTypes(stringIndexType, getIndexTypeOfType(t, IndexKind.String));
numberIndexType = intersectTypes(numberIndexType, getIndexTypeOfType(t, IndexKind.Number));
}
setObjectTypeMembers(type, emptySymbols, callSignatures, constructSignatures, stringIndexType, numberIndexType);
}
function resolveAnonymousTypeMembers(type: ObjectType) {
let symbol = type.symbol;
let members: SymbolTable;
let callSignatures: Signature[];
let constructSignatures: Signature[];
let stringIndexType: Type;
let numberIndexType: Type;
if (symbol.flags & SymbolFlags.TypeLiteral) {
members = symbol.members;
callSignatures = getSignaturesOfSymbol(members["__call"]);
constructSignatures = getSignaturesOfSymbol(members["__new"]);
stringIndexType = getIndexTypeOfSymbol(symbol, IndexKind.String);
numberIndexType = getIndexTypeOfSymbol(symbol, IndexKind.Number);
}
else {
// Combinations of function, class, enum and module
members = emptySymbols;
callSignatures = emptyArray;
constructSignatures = emptyArray;
if (symbol.flags & SymbolFlags.HasExports) {
members = getExportsOfSymbol(symbol);
}
if (symbol.flags & (SymbolFlags.Function | SymbolFlags.Method)) {
callSignatures = getSignaturesOfSymbol(symbol);
}
if (symbol.flags & SymbolFlags.Class) {
let classType = getDeclaredTypeOfClassOrInterface(symbol);
constructSignatures = getSignaturesOfSymbol(symbol.members["__constructor"]);
if (!constructSignatures.length) {
constructSignatures = getDefaultConstructSignatures(classType);
}
let baseConstructorType = getBaseConstructorTypeOfClass(classType);
if (baseConstructorType.flags & TypeFlags.ObjectType) {
members = createSymbolTable(getNamedMembers(members));
addInheritedMembers(members, getPropertiesOfObjectType(baseConstructorType));
}
}
stringIndexType = undefined;
numberIndexType = (symbol.flags & SymbolFlags.Enum) ? stringType : undefined;
}
setObjectTypeMembers(type, members, callSignatures, constructSignatures, stringIndexType, numberIndexType);
}
function resolveStructuredTypeMembers(type: ObjectType): ResolvedType {
if (!(type).members) {
if (type.flags & (TypeFlags.Class | TypeFlags.Interface)) {
resolveClassOrInterfaceMembers(type);
}
else if (type.flags & TypeFlags.Anonymous) {
resolveAnonymousTypeMembers(type);
}
else if (type.flags & TypeFlags.Tuple) {
resolveTupleTypeMembers(type);
}
else if (type.flags & TypeFlags.Union) {
resolveUnionTypeMembers(type);
}
else if (type.flags & TypeFlags.Intersection) {
resolveIntersectionTypeMembers(type);
}
else {
resolveTypeReferenceMembers(type);
}
}
return type;
}
// Return properties of an object type or an empty array for other types
function getPropertiesOfObjectType(type: Type): Symbol[] {
if (type.flags & TypeFlags.ObjectType) {
return resolveStructuredTypeMembers(type).properties;
}
return emptyArray;
}
// If the given type is an object type and that type has a property by the given name,
// return the symbol for that property.Otherwise return undefined.
function getPropertyOfObjectType(type: Type, name: string): Symbol {
if (type.flags & TypeFlags.ObjectType) {
let resolved = resolveStructuredTypeMembers(type);
if (hasProperty(resolved.members, name)) {
let symbol = resolved.members[name];
if (symbolIsValue(symbol)) {
return symbol;
}
}
}
}
function getPropertiesOfUnionOrIntersectionType(type: UnionOrIntersectionType): Symbol[] {
for (let current of type.types) {
for (let prop of getPropertiesOfType(current)) {
getPropertyOfUnionOrIntersectionType(type, prop.name);
}
// The properties of a union type are those that are present in all constituent types, so
// we only need to check the properties of the first type
if (type.flags & TypeFlags.Union) {
break;
}
}
return type.resolvedProperties ? symbolsToArray(type.resolvedProperties) : emptyArray;
}
function getPropertiesOfType(type: Type): Symbol[] {
type = getApparentType(type);
return type.flags & TypeFlags.UnionOrIntersection ? getPropertiesOfUnionOrIntersectionType(type) : getPropertiesOfObjectType(type);
}
/**
* For a type parameter, return the base constraint of the type parameter. For the string, number,
* boolean, and symbol primitive types, return the corresponding object types. Otherwise return the
* type itself. Note that the apparent type of a union type is the union type itself.
*/
function getApparentType(type: Type): Type {
if (type.flags & TypeFlags.Union) {
type = getReducedTypeOfUnionType(type);
}
if (type.flags & TypeFlags.TypeParameter) {
do {
type = getConstraintOfTypeParameter(type);
} while (type && type.flags & TypeFlags.TypeParameter);
if (!type) {
type = emptyObjectType;
}
}
if (type.flags & TypeFlags.StringLike) {
type = globalStringType;
}
else if (type.flags & TypeFlags.NumberLike) {
type = globalNumberType;
}
else if (type.flags & TypeFlags.Boolean) {
type = globalBooleanType;
}
else if (type.flags & TypeFlags.ESSymbol) {
type = globalESSymbolType;
}
return type;
}
function createUnionOrIntersectionProperty(containingType: UnionOrIntersectionType, name: string): Symbol {
let types = containingType.types;
let props: Symbol[];
for (let current of types) {
let type = getApparentType(current);
if (type !== unknownType) {
let prop = getPropertyOfType(type, name);
if (prop && !(getDeclarationFlagsFromSymbol(prop) & (NodeFlags.Private | NodeFlags.Protected))) {
if (!props) {
props = [prop];
}
else if (!contains(props, prop)) {
props.push(prop);
}
}
else if (containingType.flags & TypeFlags.Union) {
// A union type requires the property to be present in all constituent types
return undefined;
}
}
}
if (!props) {
return undefined;
}
if (props.length === 1) {
return props[0];
}
let propTypes: Type[] = [];
let declarations: Declaration[] = [];
for (let prop of props) {
if (prop.declarations) {
declarations.push.apply(declarations, prop.declarations);
}
propTypes.push(getTypeOfSymbol(prop));
}
let result = createSymbol(SymbolFlags.Property | SymbolFlags.Transient | SymbolFlags.SyntheticProperty, name);
result.containingType = containingType;
result.declarations = declarations;
result.type = containingType.flags & TypeFlags.Union ? getUnionType(propTypes) : getIntersectionType(propTypes);
return result;
}
function getPropertyOfUnionOrIntersectionType(type: UnionOrIntersectionType, name: string): Symbol {
let properties = type.resolvedProperties || (type.resolvedProperties = {});
if (hasProperty(properties, name)) {
return properties[name];
}
let property = createUnionOrIntersectionProperty(type, name);
if (property) {
properties[name] = property;
}
return property;
}
// Return the symbol for the property with the given name in the given type. Creates synthetic union properties when
// necessary, maps primitive types and type parameters are to their apparent types, and augments with properties from
// Object and Function as appropriate.
function getPropertyOfType(type: Type, name: string): Symbol {
type = getApparentType(type);
if (type.flags & TypeFlags.ObjectType) {
let resolved = resolveStructuredTypeMembers(type);
if (hasProperty(resolved.members, name)) {
let symbol = resolved.members[name];
if (symbolIsValue(symbol)) {
return symbol;
}
}
if (resolved === anyFunctionType || resolved.callSignatures.length || resolved.constructSignatures.length) {
let symbol = getPropertyOfObjectType(globalFunctionType, name);
if (symbol) {
return symbol;
}
}
return getPropertyOfObjectType(globalObjectType, name);
}
if (type.flags & TypeFlags.UnionOrIntersection) {
return getPropertyOfUnionOrIntersectionType(type, name);
}
return undefined;
}
function getSignaturesOfStructuredType(type: Type, kind: SignatureKind): Signature[] {
if (type.flags & TypeFlags.StructuredType) {
let resolved = resolveStructuredTypeMembers(type);
return kind === SignatureKind.Call ? resolved.callSignatures : resolved.constructSignatures;
}
return emptyArray;
}
// Return the signatures of the given kind in the given type. Creates synthetic union signatures when necessary and
// maps primitive types and type parameters are to their apparent types.
function getSignaturesOfType(type: Type, kind: SignatureKind): Signature[] {
return getSignaturesOfStructuredType(getApparentType(type), kind);
}
function typeHasCallOrConstructSignatures(type: Type): boolean {
let apparentType = getApparentType(type);
if (apparentType.flags & TypeFlags.StructuredType) {
let resolved = resolveStructuredTypeMembers(type);
return resolved.callSignatures.length > 0 || resolved.constructSignatures.length > 0;
}
return false;
}
function getIndexTypeOfStructuredType(type: Type, kind: IndexKind): Type {
if (type.flags & TypeFlags.StructuredType) {
let resolved = resolveStructuredTypeMembers(type);
return kind === IndexKind.String ? resolved.stringIndexType : resolved.numberIndexType;
}
}
// Return the index type of the given kind in the given type. Creates synthetic union index types when necessary and
// maps primitive types and type parameters are to their apparent types.
function getIndexTypeOfType(type: Type, kind: IndexKind): Type {
return getIndexTypeOfStructuredType(getApparentType(type), kind);
}
// Return list of type parameters with duplicates removed (duplicate identifier errors are generated in the actual
// type checking functions).
function getTypeParametersFromDeclaration(typeParameterDeclarations: TypeParameterDeclaration[]): TypeParameter[] {
let result: TypeParameter[] = [];
forEach(typeParameterDeclarations, node => {
let tp = getDeclaredTypeOfTypeParameter(node.symbol);
if (!contains(result, tp)) {
result.push(tp);
}
});
return result;
}
function symbolsToArray(symbols: SymbolTable): Symbol[] {
let result: Symbol[] = [];
for (let id in symbols) {
if (!isReservedMemberName(id)) {
result.push(symbols[id]);
}
}
return result;
}
function isOptionalParameter(node: ParameterDeclaration) {
return hasQuestionToken(node) || !!node.initializer;
}
function getSignatureFromDeclaration(declaration: SignatureDeclaration): Signature {
let links = getNodeLinks(declaration);
if (!links.resolvedSignature) {
let classType = declaration.kind === SyntaxKind.Constructor ? getDeclaredTypeOfClassOrInterface((declaration.parent).symbol) : undefined;
let typeParameters = classType ? classType.localTypeParameters :
declaration.typeParameters ? getTypeParametersFromDeclaration(declaration.typeParameters) : undefined;
let parameters: Symbol[] = [];
let hasStringLiterals = false;
let minArgumentCount = -1;
for (let i = 0, n = declaration.parameters.length; i < n; i++) {
let param = declaration.parameters[i];
parameters.push(param.symbol);
if (param.type && param.type.kind === SyntaxKind.StringLiteral) {
hasStringLiterals = true;
}
if (minArgumentCount < 0) {
if (param.initializer || param.questionToken || param.dotDotDotToken) {
minArgumentCount = i;
}
}
}
if (minArgumentCount < 0) {
minArgumentCount = declaration.parameters.length;
}
let returnType: Type;
let typePredicate: TypePredicate;
if (classType) {
returnType = classType;
}
else if (declaration.type) {
returnType = getTypeFromTypeNode(declaration.type);
if (declaration.type.kind === SyntaxKind.TypePredicate) {
let typePredicateNode = declaration.type;
typePredicate = {
parameterName: typePredicateNode.parameterName ? typePredicateNode.parameterName.text : undefined,
parameterIndex: typePredicateNode.parameterName ? getTypePredicateParameterIndex(declaration.parameters, typePredicateNode.parameterName) : undefined,
type: getTypeFromTypeNode(typePredicateNode.type)
};
}
}
else {
// TypeScript 1.0 spec (April 2014):
// If only one accessor includes a type annotation, the other behaves as if it had the same type annotation.
if (declaration.kind === SyntaxKind.GetAccessor && !hasDynamicName(declaration)) {
let setter = getDeclarationOfKind(declaration.symbol, SyntaxKind.SetAccessor);
returnType = getAnnotatedAccessorType(setter);
}
if (!returnType && nodeIsMissing((declaration).body)) {
returnType = anyType;
}
}
links.resolvedSignature = createSignature(declaration, typeParameters, parameters, returnType, typePredicate,
minArgumentCount, hasRestParameter(declaration), hasStringLiterals);
}
return links.resolvedSignature;
}
function getSignaturesOfSymbol(symbol: Symbol): Signature[] {
if (!symbol) return emptyArray;
let result: Signature[] = [];
for (let i = 0, len = symbol.declarations.length; i < len; i++) {
let node = symbol.declarations[i];
switch (node.kind) {
case SyntaxKind.FunctionType:
case SyntaxKind.ConstructorType:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
case SyntaxKind.Constructor:
case SyntaxKind.CallSignature:
case SyntaxKind.ConstructSignature:
case SyntaxKind.IndexSignature:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
// Don't include signature if node is the implementation of an overloaded function. A node is considered
// an implementation node if it has a body and the previous node is of the same kind and immediately
// precedes the implementation node (i.e. has the same parent and ends where the implementation starts).
if (i > 0 && (node).body) {
let previous = symbol.declarations[i - 1];
if (node.parent === previous.parent && node.kind === previous.kind && node.pos === previous.end) {
break;
}
}
result.push(getSignatureFromDeclaration(node));
}
}
return result;
}
function getReturnTypeOfSignature(signature: Signature): Type {
if (!signature.resolvedReturnType) {
if (!pushTypeResolution(signature)) {
return unknownType;
}
let type: Type;
if (signature.target) {
type = instantiateType(getReturnTypeOfSignature(signature.target), signature.mapper);
}
else if (signature.unionSignatures) {
type = getUnionType(map(signature.unionSignatures, getReturnTypeOfSignature));
}
else {
type = getReturnTypeFromBody(signature.declaration);
}
if (!popTypeResolution()) {
type = anyType;
if (compilerOptions.noImplicitAny) {
let declaration = signature.declaration;
if (declaration.name) {
error(declaration.name, Diagnostics._0_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions, declarationNameToString(declaration.name));
}
else {
error(declaration, Diagnostics.Function_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions);
}
}
}
signature.resolvedReturnType = type;
}
return signature.resolvedReturnType;
}
function getRestTypeOfSignature(signature: Signature): Type {
if (signature.hasRestParameter) {
let type = getTypeOfSymbol(lastOrUndefined(signature.parameters));
if (type.flags & TypeFlags.Reference && (type).target === globalArrayType) {
return (type).typeArguments[0];
}
}
return anyType;
}
function getSignatureInstantiation(signature: Signature, typeArguments: Type[]): Signature {
return instantiateSignature(signature, createTypeMapper(signature.typeParameters, typeArguments), true);
}
function getErasedSignature(signature: Signature): Signature {
if (!signature.typeParameters) return signature;
if (!signature.erasedSignatureCache) {
if (signature.target) {
signature.erasedSignatureCache = instantiateSignature(getErasedSignature(signature.target), signature.mapper);
}
else {
signature.erasedSignatureCache = instantiateSignature(signature, createTypeEraser(signature.typeParameters), true);
}
}
return signature.erasedSignatureCache;
}
function getOrCreateTypeFromSignature(signature: Signature): ObjectType {
// There are two ways to declare a construct signature, one is by declaring a class constructor
// using the constructor keyword, and the other is declaring a bare construct signature in an
// object type literal or interface (using the new keyword). Each way of declaring a constructor
// will result in a different declaration kind.
if (!signature.isolatedSignatureType) {
let isConstructor = signature.declaration.kind === SyntaxKind.Constructor || signature.declaration.kind === SyntaxKind.ConstructSignature;
let type = createObjectType(TypeFlags.Anonymous | TypeFlags.FromSignature);
type.members = emptySymbols;
type.properties = emptyArray;
type.callSignatures = !isConstructor ? [signature] : emptyArray;
type.constructSignatures = isConstructor ? [signature] : emptyArray;
signature.isolatedSignatureType = type;
}
return signature.isolatedSignatureType;
}
function getIndexSymbol(symbol: Symbol): Symbol {
return symbol.members["__index"];
}
function getIndexDeclarationOfSymbol(symbol: Symbol, kind: IndexKind): SignatureDeclaration {
let syntaxKind = kind === IndexKind.Number ? SyntaxKind.NumberKeyword : SyntaxKind.StringKeyword;
let indexSymbol = getIndexSymbol(symbol);
if (indexSymbol) {
for (let decl of indexSymbol.declarations) {
let node = decl;
if (node.parameters.length === 1) {
let parameter = node.parameters[0];
if (parameter && parameter.type && parameter.type.kind === syntaxKind) {
return node;
}
}
}
}
return undefined;
}
function getIndexTypeOfSymbol(symbol: Symbol, kind: IndexKind): Type {
let declaration = getIndexDeclarationOfSymbol(symbol, kind);
return declaration
? declaration.type ? getTypeFromTypeNode(declaration.type) : anyType
: undefined;
}
function getConstraintOfTypeParameter(type: TypeParameter): Type {
if (!type.constraint) {
if (type.target) {
let targetConstraint = getConstraintOfTypeParameter(type.target);
type.constraint = targetConstraint ? instantiateType(targetConstraint, type.mapper) : noConstraintType;
}
else {
type.constraint = getTypeFromTypeNode((getDeclarationOfKind(type.symbol, SyntaxKind.TypeParameter)).constraint);
}
}
return type.constraint === noConstraintType ? undefined : type.constraint;
}
function getParentSymbolOfTypeParameter(typeParameter: TypeParameter): Symbol {
return getSymbolOfNode(getDeclarationOfKind(typeParameter.symbol, SyntaxKind.TypeParameter).parent);
}
function getTypeListId(types: Type[]) {
switch (types.length) {
case 1:
return "" + types[0].id;
case 2:
return types[0].id + "," + types[1].id;
default:
let result = "";
for (let i = 0; i < types.length; i++) {
if (i > 0) {
result += ",";
}
result += types[i].id;
}
return result;
}
}
// This function is used to propagate widening flags when creating new object types references and union types.
// It is only necessary to do so if a constituent type might be the undefined type, the null type, or the type
// of an object literal (since those types have widening related information we need to track).
function getWideningFlagsOfTypes(types: Type[]): TypeFlags {
let result: TypeFlags = 0;
for (let type of types) {
result |= type.flags;
}
return result & TypeFlags.RequiresWidening;
}
function createTypeReference(target: GenericType, typeArguments: Type[]): TypeReference {
let id = getTypeListId(typeArguments);
let type = target.instantiations[id];
if (!type) {
let flags = TypeFlags.Reference | getWideningFlagsOfTypes(typeArguments);
type = target.instantiations[id] = createObjectType(flags, target.symbol);
type.target = target;
type.typeArguments = typeArguments;
}
return type;
}
function isTypeParameterReferenceIllegalInConstraint(typeReferenceNode: TypeReferenceNode | ExpressionWithTypeArguments, typeParameterSymbol: Symbol): boolean {
let links = getNodeLinks(typeReferenceNode);
if (links.isIllegalTypeReferenceInConstraint !== undefined) {
return links.isIllegalTypeReferenceInConstraint;
}
// bubble up to the declaration
let currentNode: Node = typeReferenceNode;
// forEach === exists
while (!forEach(typeParameterSymbol.declarations, d => d.parent === currentNode.parent)) {
currentNode = currentNode.parent;
}
// if last step was made from the type parameter this means that path has started somewhere in constraint which is illegal
links.isIllegalTypeReferenceInConstraint = currentNode.kind === SyntaxKind.TypeParameter;
return links.isIllegalTypeReferenceInConstraint;
}
function checkTypeParameterHasIllegalReferencesInConstraint(typeParameter: TypeParameterDeclaration): void {
let typeParameterSymbol: Symbol;
function check(n: Node): void {
if (n.kind === SyntaxKind.TypeReference && (n).typeName.kind === SyntaxKind.Identifier) {
let links = getNodeLinks(n);
if (links.isIllegalTypeReferenceInConstraint === undefined) {
let symbol = resolveName(typeParameter, ((n).typeName).text, SymbolFlags.Type, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined);
if (symbol && (symbol.flags & SymbolFlags.TypeParameter)) {
// TypeScript 1.0 spec (April 2014): 3.4.1
// Type parameters declared in a particular type parameter list
// may not be referenced in constraints in that type parameter list
// symbol.declaration.parent === typeParameter.parent
// -> typeParameter and symbol.declaration originate from the same type parameter list
// -> illegal for all declarations in symbol
// forEach === exists
links.isIllegalTypeReferenceInConstraint = forEach(symbol.declarations, d => d.parent === typeParameter.parent);
}
}
if (links.isIllegalTypeReferenceInConstraint) {
error(typeParameter, Diagnostics.Constraint_of_a_type_parameter_cannot_reference_any_type_parameter_from_the_same_type_parameter_list);
}
}
forEachChild(n, check);
}
if (typeParameter.constraint) {
typeParameterSymbol = getSymbolOfNode(typeParameter);
check(typeParameter.constraint);
}
}
// Get type from reference to class or interface
function getTypeFromClassOrInterfaceReference(node: TypeReferenceNode | ExpressionWithTypeArguments, symbol: Symbol): Type {
let type = getDeclaredTypeOfSymbol(symbol);
let typeParameters = (type).localTypeParameters;
if (typeParameters) {
if (!node.typeArguments || node.typeArguments.length !== typeParameters.length) {
error(node, Diagnostics.Generic_type_0_requires_1_type_argument_s, typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType), typeParameters.length);
return unknownType;
}
// In a type reference, the outer type parameters of the referenced class or interface are automatically
// supplied as type arguments and the type reference only specifies arguments for the local type parameters
// of the class or interface.
return createTypeReference(type, concatenate((type).outerTypeParameters,
map(node.typeArguments, getTypeFromTypeNode)));
}
if (node.typeArguments) {
error(node, Diagnostics.Type_0_is_not_generic, typeToString(type));
return unknownType;
}
return type;
}
// Get type from reference to type alias. When a type alias is generic, the declared type of the type alias may include
// references to the type parameters of the alias. We replace those with the actual type arguments by instantiating the
// declared type. Instantiations are cached using the type identities of the type arguments as the key.
function getTypeFromTypeAliasReference(node: TypeReferenceNode | ExpressionWithTypeArguments, symbol: Symbol): Type {
let type = getDeclaredTypeOfSymbol(symbol);
let links = getSymbolLinks(symbol);
let typeParameters = links.typeParameters;
if (typeParameters) {
if (!node.typeArguments || node.typeArguments.length !== typeParameters.length) {
error(node, Diagnostics.Generic_type_0_requires_1_type_argument_s, symbolToString(symbol), typeParameters.length);
return unknownType;
}
let typeArguments = map(node.typeArguments, getTypeFromTypeNode);
let id = getTypeListId(typeArguments);
return links.instantiations[id] || (links.instantiations[id] = instantiateType(type, createTypeMapper(typeParameters, typeArguments)));
}
if (node.typeArguments) {
error(node, Diagnostics.Type_0_is_not_generic, symbolToString(symbol));
return unknownType;
}
return type;
}
// Get type from reference to named type that cannot be generic (enum or type parameter)
function getTypeFromNonGenericTypeReference(node: TypeReferenceNode | ExpressionWithTypeArguments, symbol: Symbol): Type {
if (symbol.flags & SymbolFlags.TypeParameter && isTypeParameterReferenceIllegalInConstraint(node, symbol)) {
// TypeScript 1.0 spec (April 2014): 3.4.1
// Type parameters declared in a particular type parameter list
// may not be referenced in constraints in that type parameter list
// Implementation: such type references are resolved to 'unknown' type that usually denotes error
return unknownType;
}
if (node.typeArguments) {
error(node, Diagnostics.Type_0_is_not_generic, symbolToString(symbol));
return unknownType;
}
return getDeclaredTypeOfSymbol(symbol);
}
function getTypeFromTypeReference(node: TypeReferenceNode | ExpressionWithTypeArguments): Type {
let links = getNodeLinks(node);
if (!links.resolvedType) {
// We only support expressions that are simple qualified names. For other expressions this produces undefined.
let typeNameOrExpression = node.kind === SyntaxKind.TypeReference ? (node).typeName :
isSupportedExpressionWithTypeArguments(node) ? (node).expression :
undefined;
let symbol = typeNameOrExpression && resolveEntityName(typeNameOrExpression, SymbolFlags.Type) || unknownSymbol;
let type = symbol === unknownSymbol ? unknownType :
symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface) ? getTypeFromClassOrInterfaceReference(node, symbol) :
symbol.flags & SymbolFlags.TypeAlias ? getTypeFromTypeAliasReference(node, symbol) :
getTypeFromNonGenericTypeReference(node, symbol);
// Cache both the resolved symbol and the resolved type. The resolved symbol is needed in when we check the
// type reference in checkTypeReferenceOrExpressionWithTypeArguments.
links.resolvedSymbol = symbol;
links.resolvedType = type;
}
return links.resolvedType;
}
function getTypeFromTypeQueryNode(node: TypeQueryNode): Type {
let links = getNodeLinks(node);
if (!links.resolvedType) {
// TypeScript 1.0 spec (April 2014): 3.6.3
// The expression is processed as an identifier expression (section 4.3)
// or property access expression(section 4.10),
// the widened type(section 3.9) of which becomes the result.
links.resolvedType = getWidenedType(checkExpression(node.exprName));
}
return links.resolvedType;
}
function getTypeOfGlobalSymbol(symbol: Symbol, arity: number): ObjectType {
function getTypeDeclaration(symbol: Symbol): Declaration {
let declarations = symbol.declarations;
for (let declaration of declarations) {
switch (declaration.kind) {
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.EnumDeclaration:
return declaration;
}
}
}
if (!symbol) {
return arity ? emptyGenericType : emptyObjectType;
}
let type = getDeclaredTypeOfSymbol(symbol);
if (!(type.flags & TypeFlags.ObjectType)) {
error(getTypeDeclaration(symbol), Diagnostics.Global_type_0_must_be_a_class_or_interface_type, symbol.name);
return arity ? emptyGenericType : emptyObjectType;
}
if (((type).typeParameters ? (type).typeParameters.length : 0) !== arity) {
error(getTypeDeclaration(symbol), Diagnostics.Global_type_0_must_have_1_type_parameter_s, symbol.name, arity);
return arity ? emptyGenericType : emptyObjectType;
}
return type;
}
function getGlobalValueSymbol(name: string): Symbol {
return getGlobalSymbol(name, SymbolFlags.Value, Diagnostics.Cannot_find_global_value_0);
}
function getGlobalTypeSymbol(name: string): Symbol {
return getGlobalSymbol(name, SymbolFlags.Type, Diagnostics.Cannot_find_global_type_0);
}
function getGlobalSymbol(name: string, meaning: SymbolFlags, diagnostic: DiagnosticMessage): Symbol {
return resolveName(undefined, name, meaning, diagnostic, name);
}
function getGlobalType(name: string, arity = 0): ObjectType {
return getTypeOfGlobalSymbol(getGlobalTypeSymbol(name), arity);
}
function tryGetGlobalType(name: string, arity = 0): ObjectType {
return getTypeOfGlobalSymbol(getGlobalSymbol(name, SymbolFlags.Type, /*diagnostic*/ undefined), arity);
}
/**
* Returns a type that is inside a namespace at the global scope, e.g.
* getExportedTypeFromNamespace('JSX', 'Element') returns the JSX.Element type
*/
function getExportedTypeFromNamespace(namespace: string, name: string): Type {
let namespaceSymbol = getGlobalSymbol(namespace, SymbolFlags.Namespace, /*diagnosticMessage*/ undefined);
let typeSymbol = namespaceSymbol && getSymbol(namespaceSymbol.exports, name, SymbolFlags.Type);
return typeSymbol && getDeclaredTypeOfSymbol(typeSymbol);
}
function getGlobalESSymbolConstructorSymbol() {
return globalESSymbolConstructorSymbol || (globalESSymbolConstructorSymbol = getGlobalValueSymbol("Symbol"));
}
/**
* Creates a TypeReference for a generic `TypedPropertyDescriptor`.
*/
function createTypedPropertyDescriptorType(propertyType: Type): Type {
let globalTypedPropertyDescriptorType = getGlobalTypedPropertyDescriptorType();
return globalTypedPropertyDescriptorType !== emptyObjectType
? createTypeReference(globalTypedPropertyDescriptorType, [propertyType])
: emptyObjectType;
}
/**
* Instantiates a global type that is generic with some element type, and returns that instantiation.
*/
function createTypeFromGenericGlobalType(genericGlobalType: GenericType, elementType: Type): Type {
return genericGlobalType !== emptyGenericType ? createTypeReference(genericGlobalType, [elementType]) : emptyObjectType;
}
function createIterableType(elementType: Type): Type {
return createTypeFromGenericGlobalType(globalIterableType, elementType);
}
function createIterableIteratorType(elementType: Type): Type {
return createTypeFromGenericGlobalType(globalIterableIteratorType, elementType);
}
function createArrayType(elementType: Type): Type {
return createTypeFromGenericGlobalType(globalArrayType, elementType);
}
function getTypeFromArrayTypeNode(node: ArrayTypeNode): Type {
let links = getNodeLinks(node);
if (!links.resolvedType) {
links.resolvedType = createArrayType(getTypeFromTypeNode(node.elementType));
}
return links.resolvedType;
}
function createTupleType(elementTypes: Type[]) {
let id = getTypeListId(elementTypes);
let type = tupleTypes[id];
if (!type) {
type = tupleTypes[id] = createObjectType(TypeFlags.Tuple);
type.elementTypes = elementTypes;
}
return type;
}
function getTypeFromTupleTypeNode(node: TupleTypeNode): Type {
let links = getNodeLinks(node);
if (!links.resolvedType) {
links.resolvedType = createTupleType(map(node.elementTypes, getTypeFromTypeNode));
}
return links.resolvedType;
}
function addTypeToSet(typeSet: Type[], type: Type, typeSetKind: TypeFlags) {
if (type.flags & typeSetKind) {
addTypesToSet(typeSet, (type).types, typeSetKind);
}
else if (!contains(typeSet, type)) {
typeSet.push(type);
}
}
// Add the given types to the given type set. Order is preserved, duplicates are removed,
// and nested types of the given kind are flattened into the set.
function addTypesToSet(typeSet: Type[], types: Type[], typeSetKind: TypeFlags) {
for (let type of types) {
addTypeToSet(typeSet, type, typeSetKind);
}
}
function isSubtypeOfAny(candidate: Type, types: Type[]): boolean {
for (let type of types) {
if (candidate !== type && isTypeSubtypeOf(candidate, type)) {
return true;
}
}
return false;
}
function removeSubtypes(types: Type[]) {
let i = types.length;
while (i > 0) {
i--;
if (isSubtypeOfAny(types[i], types)) {
types.splice(i, 1);
}
}
}
function containsTypeAny(types: Type[]) {
for (let type of types) {
if (isTypeAny(type)) {
return true;
}
}
return false;
}
function removeAllButLast(types: Type[], typeToRemove: Type) {
let i = types.length;
while (i > 0 && types.length > 1) {
i--;
if (types[i] === typeToRemove) {
types.splice(i, 1);
}
}
}
function compareTypeIds(type1: Type, type2: Type): number {
return type1.id - type2.id;
}
// The noSubtypeReduction flag is there because it isn't possible to always do subtype reduction. The flag
// is true when creating a union type from a type node and when instantiating a union type. In both of those
// cases subtype reduction has to be deferred to properly support recursive union types. For example, a
// type alias of the form "type Item = string | (() => Item)" cannot be reduced during its declaration.
function getUnionType(types: Type[], noSubtypeReduction?: boolean): Type {
if (types.length === 0) {
return emptyObjectType;
}
let typeSet: Type[] = [];
addTypesToSet(typeSet, types, TypeFlags.Union);
typeSet.sort(compareTypeIds);
if (noSubtypeReduction) {
if (containsTypeAny(typeSet)) {
return anyType;
}
removeAllButLast(typeSet, undefinedType);
removeAllButLast(typeSet, nullType);
}
else {
removeSubtypes(typeSet);
}
if (typeSet.length === 1) {
return typeSet[0];
}
let id = getTypeListId(typeSet);
let type = unionTypes[id];
if (!type) {
type = unionTypes[id] = createObjectType(TypeFlags.Union | getWideningFlagsOfTypes(typeSet));
type.types = typeSet;
type.reducedType = noSubtypeReduction ? undefined : type;
}
return type;
}
// Subtype reduction is basically an optimization we do to avoid excessively large union types, which take longer
// to process and look strange in quick info and error messages. Semantically there is no difference between the
// reduced type and the type itself. So, when we detect a circularity we simply say that the reduced type is the
// type itself.
function getReducedTypeOfUnionType(type: UnionType): Type {
if (!type.reducedType) {
type.reducedType = circularType;
let reducedType = getUnionType(type.types, /*noSubtypeReduction*/ false);
if (type.reducedType === circularType) {
type.reducedType = reducedType;
}
}
else if (type.reducedType === circularType) {
type.reducedType = type;
}
return type.reducedType;
}
function getTypeFromUnionTypeNode(node: UnionTypeNode): Type {
let links = getNodeLinks(node);
if (!links.resolvedType) {
links.resolvedType = getUnionType(map(node.types, getTypeFromTypeNode), /*noSubtypeReduction*/ true);
}
return links.resolvedType;
}
// We do not perform supertype reduction on intersection types. Intersection types are created only by the &
// type operator and we can't reduce those because we want to support recursive intersection types. For example,
// a type alias of the form "type List = T & { next: List }" cannot be reduced during its declaration.
// Also, unlike union types, the order of the constituent types is preserved in order that overload resolution
// for intersections of types with signatures can be deterministic.
function getIntersectionType(types: Type[]): Type {
if (types.length === 0) {
return emptyObjectType;
}
let typeSet: Type[] = [];
addTypesToSet(typeSet, types, TypeFlags.Intersection);
if (containsTypeAny(typeSet)) {
return anyType;
}
if (typeSet.length === 1) {
return typeSet[0];
}
let id = getTypeListId(typeSet);
let type = intersectionTypes[id];
if (!type) {
type = intersectionTypes[id] = createObjectType(TypeFlags.Intersection | getWideningFlagsOfTypes(typeSet));
type.types = typeSet;
}
return type;
}
function getTypeFromIntersectionTypeNode(node: IntersectionTypeNode): Type {
let links = getNodeLinks(node);
if (!links.resolvedType) {
links.resolvedType = getIntersectionType(map(node.types, getTypeFromTypeNode));
}
return links.resolvedType;
}
function getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node: Node): Type {
let links = getNodeLinks(node);
if (!links.resolvedType) {
// Deferred resolution of members is handled by resolveObjectTypeMembers
links.resolvedType = createObjectType(TypeFlags.Anonymous, node.symbol);
}
return links.resolvedType;
}
function getStringLiteralType(node: StringLiteral): StringLiteralType {
if (hasProperty(stringLiteralTypes, node.text)) {
return stringLiteralTypes[node.text];
}
let type = stringLiteralTypes[node.text] = createType(TypeFlags.StringLiteral);
type.text = getTextOfNode(node);
return type;
}
function getTypeFromStringLiteral(node: StringLiteral): Type {
let links = getNodeLinks(node);
if (!links.resolvedType) {
links.resolvedType = getStringLiteralType(node);
}
return links.resolvedType;
}
function getTypeFromTypeNode(node: TypeNode): Type {
switch (node.kind) {
case SyntaxKind.AnyKeyword:
return anyType;
case SyntaxKind.StringKeyword:
return stringType;
case SyntaxKind.NumberKeyword:
return numberType;
case SyntaxKind.BooleanKeyword:
return booleanType;
case SyntaxKind.SymbolKeyword:
return esSymbolType;
case SyntaxKind.VoidKeyword:
return voidType;
case SyntaxKind.StringLiteral:
return getTypeFromStringLiteral(node);
case SyntaxKind.TypeReference:
return getTypeFromTypeReference(node);
case SyntaxKind.TypePredicate:
return booleanType;
case SyntaxKind.ExpressionWithTypeArguments:
return getTypeFromTypeReference(node);
case SyntaxKind.TypeQuery:
return getTypeFromTypeQueryNode(node);
case SyntaxKind.ArrayType:
return getTypeFromArrayTypeNode(node);
case SyntaxKind.TupleType:
return getTypeFromTupleTypeNode(node);
case SyntaxKind.UnionType:
return getTypeFromUnionTypeNode(node);
case SyntaxKind.IntersectionType:
return getTypeFromIntersectionTypeNode(node);
case SyntaxKind.ParenthesizedType:
return getTypeFromTypeNode((node).type);
case SyntaxKind.FunctionType:
case SyntaxKind.ConstructorType:
case SyntaxKind.TypeLiteral:
return getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node);
// This function assumes that an identifier or qualified name is a type expression
// Callers should first ensure this by calling isTypeNode
case SyntaxKind.Identifier:
case SyntaxKind.QualifiedName:
let symbol = getSymbolInfo(node);
return symbol && getDeclaredTypeOfSymbol(symbol);
default:
return unknownType;
}
}
function instantiateList(items: T[], mapper: TypeMapper, instantiator: (item: T, mapper: TypeMapper) => T): T[] {
if (items && items.length) {
let result: T[] = [];
for (let v of items) {
result.push(instantiator(v, mapper));
}
return result;
}
return items;
}
function createUnaryTypeMapper(source: Type, target: Type): TypeMapper {
return t => t === source ? target : t;
}
function createBinaryTypeMapper(source1: Type, target1: Type, source2: Type, target2: Type): TypeMapper {
return t => t === source1 ? target1 : t === source2 ? target2 : t;
}
function createTypeMapper(sources: Type[], targets: Type[]): TypeMapper {
switch (sources.length) {
case 1: return createUnaryTypeMapper(sources[0], targets[0]);
case 2: return createBinaryTypeMapper(sources[0], targets[0], sources[1], targets[1]);
}
return t => {
for (let i = 0; i < sources.length; i++) {
if (t === sources[i]) {
return targets[i];
}
}
return t;
};
}
function createUnaryTypeEraser(source: Type): TypeMapper {
return t => t === source ? anyType : t;
}
function createBinaryTypeEraser(source1: Type, source2: Type): TypeMapper {
return t => t === source1 || t === source2 ? anyType : t;
}
function createTypeEraser(sources: Type[]): TypeMapper {
switch (sources.length) {
case 1: return createUnaryTypeEraser(sources[0]);
case 2: return createBinaryTypeEraser(sources[0], sources[1]);
}
return t => {
for (let source of sources) {
if (t === source) {
return anyType;
}
}
return t;
};
}
function createInferenceMapper(context: InferenceContext): TypeMapper {
return t => {
for (let i = 0; i < context.typeParameters.length; i++) {
if (t === context.typeParameters[i]) {
context.inferences[i].isFixed = true;
return getInferredType(context, i);
}
}
return t;
};
}
function identityMapper(type: Type): Type {
return type;
}
function combineTypeMappers(mapper1: TypeMapper, mapper2: TypeMapper): TypeMapper {
return t => instantiateType(mapper1(t), mapper2);
}
function instantiateTypeParameter(typeParameter: TypeParameter, mapper: TypeMapper): TypeParameter {
let result = createType(TypeFlags.TypeParameter);
result.symbol = typeParameter.symbol;
if (typeParameter.constraint) {
result.constraint = instantiateType(typeParameter.constraint, mapper);
}
else {
result.target = typeParameter;
result.mapper = mapper;
}
return result;
}
function instantiateSignature(signature: Signature, mapper: TypeMapper, eraseTypeParameters?: boolean): Signature {
let freshTypeParameters: TypeParameter[];
let freshTypePredicate: TypePredicate;
if (signature.typeParameters && !eraseTypeParameters) {
freshTypeParameters = instantiateList(signature.typeParameters, mapper, instantiateTypeParameter);
mapper = combineTypeMappers(createTypeMapper(signature.typeParameters, freshTypeParameters), mapper);
}
if (signature.typePredicate) {
freshTypePredicate = {
parameterName: signature.typePredicate.parameterName,
parameterIndex: signature.typePredicate.parameterIndex,
type: instantiateType(signature.typePredicate.type, mapper)
};
}
let result = createSignature(signature.declaration, freshTypeParameters,
instantiateList(signature.parameters, mapper, instantiateSymbol),
signature.resolvedReturnType ? instantiateType(signature.resolvedReturnType, mapper) : undefined,
freshTypePredicate,
signature.minArgumentCount, signature.hasRestParameter, signature.hasStringLiterals);
result.target = signature;
result.mapper = mapper;
return result;
}
function instantiateSymbol(symbol: Symbol, mapper: TypeMapper): Symbol {
if (symbol.flags & SymbolFlags.Instantiated) {
let links = getSymbolLinks(symbol);
// If symbol being instantiated is itself a instantiation, fetch the original target and combine the
// type mappers. This ensures that original type identities are properly preserved and that aliases
// always reference a non-aliases.
symbol = links.target;
mapper = combineTypeMappers(links.mapper, mapper);
}
// Keep the flags from the symbol we're instantiating. Mark that is instantiated, and
// also transient so that we can just store data on it directly.
let result = createSymbol(SymbolFlags.Instantiated | SymbolFlags.Transient | symbol.flags, symbol.name);
result.declarations = symbol.declarations;
result.parent = symbol.parent;
result.target = symbol;
result.mapper = mapper;
if (symbol.valueDeclaration) {
result.valueDeclaration = symbol.valueDeclaration;
}
return result;
}
function instantiateAnonymousType(type: ObjectType, mapper: TypeMapper): ObjectType {
// Mark the anonymous type as instantiated such that our infinite instantiation detection logic can recognize it
let result = createObjectType(TypeFlags.Anonymous | TypeFlags.Instantiated, type.symbol);
result.properties = instantiateList(getPropertiesOfObjectType(type), mapper, instantiateSymbol);
result.members = createSymbolTable(result.properties);
result.callSignatures = instantiateList(getSignaturesOfType(type, SignatureKind.Call), mapper, instantiateSignature);
result.constructSignatures = instantiateList(getSignaturesOfType(type, SignatureKind.Construct), mapper, instantiateSignature);
let stringIndexType = getIndexTypeOfType(type, IndexKind.String);
let numberIndexType = getIndexTypeOfType(type, IndexKind.Number);
if (stringIndexType) result.stringIndexType = instantiateType(stringIndexType, mapper);
if (numberIndexType) result.numberIndexType = instantiateType(numberIndexType, mapper);
return result;
}
function instantiateType(type: Type, mapper: TypeMapper): Type {
if (mapper !== identityMapper) {
if (type.flags & TypeFlags.TypeParameter) {
return mapper(type);
}
if (type.flags & TypeFlags.Anonymous) {
return type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) ?
instantiateAnonymousType(type, mapper) : type;
}
if (type.flags & TypeFlags.Reference) {
return createTypeReference((type).target, instantiateList((type).typeArguments, mapper, instantiateType));
}
if (type.flags & TypeFlags.Tuple) {
return createTupleType(instantiateList((type).elementTypes, mapper, instantiateType));
}
if (type.flags & TypeFlags.Union) {
return getUnionType(instantiateList((type).types, mapper, instantiateType), /*noSubtypeReduction*/ true);
}
if (type.flags & TypeFlags.Intersection) {
return getIntersectionType(instantiateList((type).types, mapper, instantiateType));
}
}
return type;
}
// Returns true if the given expression contains (at any level of nesting) a function or arrow expression
// that is subject to contextual typing.
function isContextSensitive(node: Expression | MethodDeclaration | ObjectLiteralElement): boolean {
Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isObjectLiteralMethod(node));
switch (node.kind) {
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
return isContextSensitiveFunctionLikeDeclaration(node);
case SyntaxKind.ObjectLiteralExpression:
return forEach((node).properties, isContextSensitive);
case SyntaxKind.ArrayLiteralExpression:
return forEach((node).elements, isContextSensitive);
case SyntaxKind.ConditionalExpression:
return isContextSensitive((node).whenTrue) ||
isContextSensitive((node).whenFalse);
case SyntaxKind.BinaryExpression:
return (node).operatorToken.kind === SyntaxKind.BarBarToken &&
(isContextSensitive((node).left) || isContextSensitive((node).right));
case SyntaxKind.PropertyAssignment:
return isContextSensitive((node).initializer);
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
return isContextSensitiveFunctionLikeDeclaration(node);
case SyntaxKind.ParenthesizedExpression:
return isContextSensitive((node).expression);
}
return false;
}
function isContextSensitiveFunctionLikeDeclaration(node: FunctionLikeDeclaration) {
return !node.typeParameters && node.parameters.length && !forEach(node.parameters, p => p.type);
}
function getTypeWithoutSignatures(type: Type): Type {
if (type.flags & TypeFlags.ObjectType) {
let resolved = resolveStructuredTypeMembers(type);
if (resolved.constructSignatures.length) {
let result = createObjectType(TypeFlags.Anonymous, type.symbol);
result.members = resolved.members;
result.properties = resolved.properties;
result.callSignatures = emptyArray;
result.constructSignatures = emptyArray;
type = result;
}
}
return type;
}
// TYPE CHECKING
function isTypeIdenticalTo(source: Type, target: Type): boolean {
return checkTypeRelatedTo(source, target, identityRelation, /*errorNode*/ undefined);
}
function compareTypes(source: Type, target: Type): Ternary {
return checkTypeRelatedTo(source, target, identityRelation, /*errorNode*/ undefined) ? Ternary.True : Ternary.False;
}
function isTypeSubtypeOf(source: Type, target: Type): boolean {
return checkTypeSubtypeOf(source, target, /*errorNode*/ undefined);
}
function isTypeAssignableTo(source: Type, target: Type): boolean {
return checkTypeAssignableTo(source, target, /*errorNode*/ undefined);
}
function checkTypeSubtypeOf(source: Type, target: Type, errorNode: Node, headMessage?: DiagnosticMessage, containingMessageChain?: DiagnosticMessageChain): boolean {
return checkTypeRelatedTo(source, target, subtypeRelation, errorNode, headMessage, containingMessageChain);
}
function checkTypeAssignableTo(source: Type, target: Type, errorNode: Node, headMessage?: DiagnosticMessage, containingMessageChain?: DiagnosticMessageChain): boolean {
return checkTypeRelatedTo(source, target, assignableRelation, errorNode, headMessage, containingMessageChain);
}
function isSignatureAssignableTo(source: Signature, target: Signature): boolean {
let sourceType = getOrCreateTypeFromSignature(source);
let targetType = getOrCreateTypeFromSignature(target);
return checkTypeRelatedTo(sourceType, targetType, assignableRelation, /*errorNode*/ undefined);
}
/**
* Checks if 'source' is related to 'target' (e.g.: is a assignable to).
* @param source The left-hand-side of the relation.
* @param target The right-hand-side of the relation.
* @param relation The relation considered. One of 'identityRelation', 'assignableRelation', or 'subTypeRelation'.
* Used as both to determine which checks are performed and as a cache of previously computed results.
* @param errorNode The node upon which all errors will be reported, if defined.
* @param headMessage If the error chain should be prepended by a head message, then headMessage will be used.
* @param containingMessageChain A chain of errors to prepend any new errors found.
*/
function checkTypeRelatedTo(
source: Type,
target: Type,
relation: Map,
errorNode: Node,
headMessage?: DiagnosticMessage,
containingMessageChain?: DiagnosticMessageChain): boolean {
let errorInfo: DiagnosticMessageChain;
let sourceStack: ObjectType[];
let targetStack: ObjectType[];
let maybeStack: Map[];
let expandingFlags: number;
let depth = 0;
let overflow = false;
let elaborateErrors = false;
Debug.assert(relation !== identityRelation || !errorNode, "no error reporting in identity checking");
let result = isRelatedTo(source, target, errorNode !== undefined, headMessage);
if (overflow) {
error(errorNode, Diagnostics.Excessive_stack_depth_comparing_types_0_and_1, typeToString(source), typeToString(target));
}
else if (errorInfo) {
// If we already computed this relation, but in a context where we didn't want to report errors (e.g. overload resolution),
// then we'll only have a top-level error (e.g. 'Class X does not implement interface Y') without any details. If this happened,
// request a recompuation to get a complete error message. This will be skipped if we've already done this computation in a context
// where errors were being reported.
if (errorInfo.next === undefined) {
errorInfo = undefined;
elaborateErrors = true;
isRelatedTo(source, target, errorNode !== undefined, headMessage);
}
if (containingMessageChain) {
errorInfo = concatenateDiagnosticMessageChains(containingMessageChain, errorInfo);
}
diagnostics.add(createDiagnosticForNodeFromMessageChain(errorNode, errorInfo));
}
return result !== Ternary.False;
function reportError(message: DiagnosticMessage, arg0?: string, arg1?: string, arg2?: string): void {
errorInfo = chainDiagnosticMessages(errorInfo, message, arg0, arg1, arg2);
}
// Compare two types and return
// Ternary.True if they are related with no assumptions,
// Ternary.Maybe if they are related with assumptions of other relationships, or
// Ternary.False if they are not related.
function isRelatedTo(source: Type, target: Type, reportErrors?: boolean, headMessage?: DiagnosticMessage): Ternary {
let result: Ternary;
// both types are the same - covers 'they are the same primitive type or both are Any' or the same type parameter cases
if (source === target) return Ternary.True;
if (relation !== identityRelation) {
if (isTypeAny(target)) return Ternary.True;
if (source === undefinedType) return Ternary.True;
if (source === nullType && target !== undefinedType) return Ternary.True;
if (source.flags & TypeFlags.Enum && target === numberType) return Ternary.True;
if (source.flags & TypeFlags.StringLiteral && target === stringType) return Ternary.True;
if (relation === assignableRelation) {
if (isTypeAny(source)) return Ternary.True;
if (source === numberType && target.flags & TypeFlags.Enum) return Ternary.True;
}
}
let saveErrorInfo = errorInfo;
if (source.flags & TypeFlags.Reference && target.flags & TypeFlags.Reference && (source).target === (target).target) {
// We have type references to same target type, see if relationship holds for all type arguments
if (result = typesRelatedTo((source).typeArguments, (target).typeArguments, reportErrors)) {
return result;
}
}
else if (source.flags & TypeFlags.TypeParameter && target.flags & TypeFlags.TypeParameter) {
if (result = typeParameterRelatedTo(source, target, reportErrors)) {
return result;
}
}
else if (relation !== identityRelation) {
// Note that the "each" checks must precede the "some" checks to produce the correct results
if (source.flags & TypeFlags.Union) {
if (result = eachTypeRelatedToType(source, target, reportErrors)) {
return result;
}
}
else if (target.flags & TypeFlags.Intersection) {
if (result = typeRelatedToEachType(source, target, reportErrors)) {
return result;
}
}
else {
// It is necessary to try "each" checks on both sides because there may be nested "some" checks
// on either side that need to be prioritized. For example, A | B = (A | B) & (C | D) or
// A & B = (A & B) | (C & D).
if (source.flags & TypeFlags.Intersection) {
// If target is a union type the following check will report errors so we suppress them here
if (result = someTypeRelatedToType(source, target, reportErrors && !(target.flags & TypeFlags.Union))) {
return result;
}
}
if (target.flags & TypeFlags.Union) {
if (result = typeRelatedToSomeType(source, target, reportErrors)) {
return result;
}
}
}
}
else {
if (source.flags & TypeFlags.Union && target.flags & TypeFlags.Union ||
source.flags & TypeFlags.Intersection && target.flags & TypeFlags.Intersection) {
if (result = eachTypeRelatedToSomeType(source, target)) {
if (result &= eachTypeRelatedToSomeType(target, source)) {
return result;
}
}
}
}
// Even if relationship doesn't hold for unions, type parameters, or generic type references,
// it may hold in a structural comparison.
// Report structural errors only if we haven't reported any errors yet
let reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo;
// Identity relation does not use apparent type
let sourceOrApparentType = relation === identityRelation ? source : getApparentType(source);
// In a check of the form X = A & B, we will have previously checked if A relates to X or B relates
// to X. Failing both of those we want to check if the aggregation of A and B's members structurally
// relates to X. Thus, we include intersection types on the source side here.
if (sourceOrApparentType.flags & (TypeFlags.ObjectType | TypeFlags.Intersection) && target.flags & TypeFlags.ObjectType) {
if (result = objectTypeRelatedTo(sourceOrApparentType,