///
module ts {
var nextSymbolId = 1;
var nextNodeId = 1;
var nextMergeId = 1;
/* @internal */ export var checkTime = 0;
export function createTypeChecker(host: TypeCheckerHost, produceDiagnostics: boolean): TypeChecker {
var Symbol = objectAllocator.getSymbolConstructor();
var Type = objectAllocator.getTypeConstructor();
var Signature = objectAllocator.getSignatureConstructor();
var typeCount = 0;
var emptyArray: any[] = [];
var emptySymbols: SymbolTable = {};
var compilerOptions = host.getCompilerOptions();
var languageVersion = compilerOptions.target || ScriptTarget.ES3;
var emitResolver = createResolver();
var 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: resolveImport,
getEmitResolver,
};
var undefinedSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "undefined");
var argumentsSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "arguments");
var unknownSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "unknown");
var resolvingSymbol = createSymbol(SymbolFlags.Transient, "__resolving__");
var anyType = createIntrinsicType(TypeFlags.Any, "any");
var stringType = createIntrinsicType(TypeFlags.String, "string");
var numberType = createIntrinsicType(TypeFlags.Number, "number");
var booleanType = createIntrinsicType(TypeFlags.Boolean, "boolean");
var esSymbolType = createIntrinsicType(TypeFlags.ESSymbol, "symbol");
var voidType = createIntrinsicType(TypeFlags.Void, "void");
var undefinedType = createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsUndefinedOrNull, "undefined");
var nullType = createIntrinsicType(TypeFlags.Null | TypeFlags.ContainsUndefinedOrNull, "null");
var unknownType = createIntrinsicType(TypeFlags.Any, "unknown");
var resolvingType = createIntrinsicType(TypeFlags.Any, "__resolving__");
var emptyObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
var anyFunctionType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
var noConstraintType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
var inferenceFailureType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
var anySignature = createSignature(undefined, undefined, emptyArray, anyType, 0, false, false);
var unknownSignature = createSignature(undefined, undefined, emptyArray, unknownType, 0, false, false);
var globals: SymbolTable = {};
var globalArraySymbol: Symbol;
var globalESSymbolConstructorSymbol: Symbol;
var globalObjectType: ObjectType;
var globalFunctionType: ObjectType;
var globalArrayType: ObjectType;
var globalStringType: ObjectType;
var globalNumberType: ObjectType;
var globalBooleanType: ObjectType;
var globalRegExpType: ObjectType;
var globalTemplateStringsArrayType: ObjectType;
var globalESSymbolType: ObjectType;
var anyArrayType: Type;
var tupleTypes: Map = {};
var unionTypes: Map = {};
var stringLiteralTypes: Map = {};
var emitExtends = false;
var mergedSymbols: Symbol[] = [];
var symbolLinks: SymbolLinks[] = [];
var nodeLinks: NodeLinks[] = [];
var potentialThisCollisions: Node[] = [];
var diagnostics = createDiagnosticCollection();
var 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
}
};
function getEmitResolver(sourceFile?: SourceFile) {
// 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);
return emitResolver;
}
function error(location: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): void {
var 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 {
var 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.Import) result |= SymbolFlags.ImportExcludes;
return result;
}
function recordMergedSymbol(target: Symbol, source: Symbol) {
if (!source.mergeId) source.mergeId = nextMergeId++;
mergedSymbols[source.mergeId] = target;
}
function cloneSymbol(symbol: Symbol): Symbol {
var 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 extendSymbol(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 = {};
extendSymbolTable(target.members, source.members);
}
if (source.exports) {
if (!target.exports) target.exports = {};
extendSymbolTable(target.exports, source.exports);
}
recordMergedSymbol(target, source);
}
else {
var 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 {
var result: SymbolTable = {};
for (var id in symbolTable) {
if (hasProperty(symbolTable, id)) {
result[id] = symbolTable[id];
}
}
return result;
}
function extendSymbolTable(target: SymbolTable, source: SymbolTable) {
for (var id in source) {
if (hasProperty(source, id)) {
if (!hasProperty(target, id)) {
target[id] = source[id];
}
else {
var symbol = target[id];
if (!(symbol.flags & SymbolFlags.Merged)) {
target[id] = symbol = cloneSymbol(symbol);
}
extendSymbol(symbol, source[id]);
}
}
}
}
function getSymbolLinks(symbol: Symbol): SymbolLinks {
if (symbol.flags & SymbolFlags.Transient) return symbol;
if (!symbol.id) symbol.id = nextSymbolId++;
return symbolLinks[symbol.id] || (symbolLinks[symbol.id] = {});
}
function getNodeLinks(node: Node): NodeLinks {
if (!node.id) node.id = nextNodeId++;
return nodeLinks[node.id] || (nodeLinks[node.id] = {});
}
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)) {
var 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.Import) {
var target = resolveImport(symbol);
// unknown symbol will mean that there were reported error during import 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 {
var file1 = getSourceFileOfNode(node1);
var file2 = getSourceFileOfNode(node2);
if (file1 === file2) {
return node1.pos <= node2.pos;
}
if (!compilerOptions.out) {
return true;
}
var 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 {
var result: Symbol;
var lastLocation: Node;
var propertyWithInvalidInitializer: Node;
var errorLocation = location;
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)) {
break loop;
}
}
switch (location.kind) {
case SyntaxKind.SourceFile:
if (!isExternalModule(location)) break;
case SyntaxKind.ModuleDeclaration:
if (result = getSymbol(getSymbolOfNode(location).exports, 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 (location.parent.kind === SyntaxKind.ClassDeclaration && !(location.flags & NodeFlags.Static)) {
var 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.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;
}
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:
var grandparent = location.parent.parent;
if (grandparent.kind === SyntaxKind.ClassDeclaration || 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 (name === "arguments") {
result = argumentsSymbol;
break loop;
}
break;
case SyntaxKind.FunctionExpression:
if (name === "arguments") {
result = argumentsSymbol;
break loop;
}
var id = (location).name;
if (id && name === id.text) {
result = location.symbol;
break loop;
}
break;
case SyntaxKind.CatchClause:
var id = (location).name;
if (name === id.text) {
result = location.symbol;
break loop;
}
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.
var 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) {
// Block-scoped variables cannot be used before their definition
var declaration = forEach(result.declarations, d => getCombinedNodeFlags(d) & NodeFlags.BlockScoped ? d : undefined);
Debug.assert(declaration !== undefined, "Block-scoped variable declaration is undefined");
if (!isDefinedBefore(declaration, errorLocation)) {
error(errorLocation, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, declarationNameToString(declaration.name));
}
}
}
return result;
}
function resolveImport(symbol: Symbol): Symbol {
Debug.assert((symbol.flags & SymbolFlags.Import) !== 0, "Should only get Imports here.");
var links = getSymbolLinks(symbol);
if (!links.target) {
links.target = resolvingSymbol;
var node = getDeclarationOfKind(symbol, SyntaxKind.ImportDeclaration);
// Grammar checking
if (node.moduleReference.kind === SyntaxKind.ExternalModuleReference) {
if ((node.moduleReference).expression.kind !== SyntaxKind.StringLiteral) {
grammarErrorOnNode((node.moduleReference).expression, Diagnostics.String_literal_expected);
}
}
var target = node.moduleReference.kind === SyntaxKind.ExternalModuleReference
? resolveExternalModuleName(node, getExternalModuleImportDeclarationExpression(node))
: getSymbolOfPartOfRightHandSideOfImport(node.moduleReference, 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;
}
// This function is only for imports with entity names
function getSymbolOfPartOfRightHandSideOfImport(entityName: EntityName, importDeclaration?: ImportDeclaration): Symbol {
if (!importDeclaration) {
importDeclaration = getAncestor(entityName, SyntaxKind.ImportDeclaration);
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(importDeclaration, 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.ImportDeclaration);
return resolveEntityName(importDeclaration, 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 import aliases
function resolveEntityName(location: Node, name: EntityName, meaning: SymbolFlags): Symbol {
if (getFullWidth(name) === 0) {
return undefined;
}
if (name.kind === SyntaxKind.Identifier) {
var symbol = resolveName(location,(name).text, meaning, Diagnostics.Cannot_find_name_0, name);
if (!symbol) {
return;
}
}
else if (name.kind === SyntaxKind.QualifiedName) {
var namespace = resolveEntityName(location,(name).left, SymbolFlags.Namespace);
if (!namespace || namespace === unknownSymbol || getFullWidth((name).right) === 0) return;
var symbol = getSymbol(namespace.exports,(name).right.text, meaning);
if (!symbol) {
error(location, Diagnostics.Module_0_has_no_exported_member_1, getFullyQualifiedName(namespace),
declarationNameToString((name).right));
return;
}
}
Debug.assert((symbol.flags & SymbolFlags.Instantiated) === 0, "Should never get an instantiated symbol here.");
return symbol.flags & meaning ? symbol : resolveImport(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;
}
var moduleReferenceLiteral = moduleReferenceExpression;
var 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.
var moduleName = escapeIdentifier(moduleReferenceLiteral.text);
if (!moduleName) return;
var isRelative = isExternalModuleNameRelative(moduleName);
if (!isRelative) {
var symbol = getSymbol(globals, '"' + moduleName + '"', SymbolFlags.ValueModule);
if (symbol) {
return getResolvedExportSymbol(symbol);
}
}
while (true) {
var fileName = normalizePath(combinePaths(searchPath, moduleName));
var sourceFile = host.getSourceFile(fileName + ".ts") || host.getSourceFile(fileName + ".d.ts");
if (sourceFile || isRelative) break;
var parentPath = getDirectoryPath(searchPath);
if (parentPath === searchPath) break;
searchPath = parentPath;
}
if (sourceFile) {
if (sourceFile.symbol) {
return getResolvedExportSymbol(sourceFile.symbol);
}
error(moduleReferenceLiteral, Diagnostics.File_0_is_not_an_external_module, sourceFile.fileName);
return;
}
error(moduleReferenceLiteral, Diagnostics.Cannot_find_external_module_0, moduleName);
}
function getResolvedExportSymbol(moduleSymbol: Symbol): Symbol {
var symbol = getExportAssignmentSymbol(moduleSymbol);
if (symbol) {
if (symbol.flags & (SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace)) {
return symbol;
}
if (symbol.flags & SymbolFlags.Import) {
return resolveImport(symbol);
}
}
return moduleSymbol;
}
function getExportAssignmentSymbol(symbol: Symbol): Symbol {
checkTypeOfExportAssignmentSymbol(symbol);
var symbolLinks = getSymbolLinks(symbol);
return symbolLinks.exportAssignSymbol === unknownSymbol ? undefined : symbolLinks.exportAssignSymbol;
}
function checkTypeOfExportAssignmentSymbol(containerSymbol: Symbol): void {
var symbolLinks = getSymbolLinks(containerSymbol);
if (!symbolLinks.exportAssignSymbol) {
var exportInformation = collectExportInformationForSourceFileOrModule(containerSymbol);
if (exportInformation.exportAssignments.length) {
if (exportInformation.exportAssignments.length > 1) {
// TypeScript 1.0 spec (April 2014): 11.2.4
// It is an error for an external module to contain more than one export assignment.
forEach(exportInformation.exportAssignments, node => error(node, Diagnostics.A_module_cannot_have_more_than_one_export_assignment));
}
var node = exportInformation.exportAssignments[0];
if (exportInformation.hasExportedMember) {
// TypeScript 1.0 spec (April 2014): 11.2.3
// If an external module contains an export assignment it is an error
// for the external module to also contain export declarations.
// The two types of exports are mutually exclusive.
error(node, Diagnostics.An_export_assignment_cannot_be_used_in_a_module_with_other_exported_elements);
}
if (node.exportName.text) {
var meaning = SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace;
var exportSymbol = resolveName(node, node.exportName.text, meaning, Diagnostics.Cannot_find_name_0, node.exportName);
}
}
symbolLinks.exportAssignSymbol = exportSymbol || unknownSymbol;
}
}
function collectExportInformationForSourceFileOrModule(symbol: Symbol) {
var seenExportedMember = false;
var result: ExportAssignment[] = [];
forEach(symbol.declarations, declaration => {
var block = (declaration.kind === SyntaxKind.SourceFile ? declaration : (declaration).body);
forEach(block.statements, node => {
if (node.kind === SyntaxKind.ExportAssignment) {
result.push(node);
}
else {
seenExportedMember = seenExportedMember || (node.flags & NodeFlags.Export) !== 0;
}
});
});
return {
hasExportedMember: seenExportedMember,
exportAssignments: result
};
}
function getMergedSymbol(symbol: Symbol): Symbol {
var 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 import, then it is a value if the symbol it resolves to is a value.
if (symbol.flags & SymbolFlags.Import) {
return (resolveImport(symbol).flags & SymbolFlags.Value) !== 0;
}
return false;
}
function findConstructorDeclaration(node: ClassDeclaration): ConstructorDeclaration {
var members = node.members;
for (var i = 0; i < members.length; i++) {
var member = members[i];
if (member.kind === SyntaxKind.Constructor && nodeIsPresent((member).body)) {
return member;
}
}
}
function createType(flags: TypeFlags): Type {
var result = new Type(checker, flags);
result.id = typeCount++;
return result;
}
function createIntrinsicType(kind: TypeFlags, intrinsicName: string): IntrinsicType {
var type = createType(kind);
type.intrinsicName = intrinsicName;
return type;
}
function createObjectType(kind: TypeFlags, symbol?: Symbol): ObjectType {
var 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[] {
var result: Symbol[];
for (var id in members) {
if (hasProperty(members, id)) {
if (!isReservedMemberName(id)) {
if (!result) result = [];
var 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 {
var result: T;
for (var 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
var 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.Import) {
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, isExternalModuleImportDeclaration)) {
var resolvedImportedSymbol = resolveImport(symbolFromSymbolTable);
if (isAccessible(symbolFromSymbolTable, resolveImport(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
var 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) {
var 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
var 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.Import) ? resolveImport(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)) {
var initialSymbol = symbol;
var meaningToLook = meaning;
while (symbol) {
// Symbol is accessible if it by itself is accessible
var accessibleSymbolChain = getAccessibleSymbolChain(symbol, enclosingDeclaration, meaningToLook, /*useOnlyExternalAliasing*/ false);
if (accessibleSymbolChain) {
var 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 {
// }
// }
// var 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
var symbolExternalModule = forEach(initialSymbol.declarations, getExternalModuleContainer);
if (symbolExternalModule) {
var 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 {
var aliasesToMakeVisible: ImportDeclaration[];
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
if (declaration.kind === SyntaxKind.ImportDeclaration &&
!(declaration.flags & NodeFlags.Export) &&
isDeclarationVisible(declaration.parent)) {
getNodeLinks(declaration).isVisible = true;
if (aliasesToMakeVisible) {
if (!contains(aliasesToMakeVisible, declaration)) {
aliasesToMakeVisible.push(declaration);
}
}
else {
aliasesToMakeVisible = [declaration];
}
return true;
}
// Declaration is not visible
return false;
}
return true;
}
}
function isEntityNameVisible(entityName: EntityName, enclosingDeclaration: Node): SymbolVisibilityResult {
// get symbol of the first identifier of the entityName
var meaning: SymbolFlags;
if (entityName.parent.kind === SyntaxKind.TypeQuery) {
// Typeof value
meaning = SymbolFlags.Value | SymbolFlags.ExportValue;
}
else if (entityName.kind === SyntaxKind.QualifiedName ||
entityName.parent.kind === SyntaxKind.ImportDeclaration) {
// 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;
}
var firstIdentifier = getFirstIdentifier(entityName);
var 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 {
var writer = getSingleLineStringWriter();
getSymbolDisplayBuilder().buildSymbolDisplay(symbol, writer, enclosingDeclaration, meaning);
var result = writer.string();
releaseStringWriter(writer);
return result;
}
function typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string {
var writer = getSingleLineStringWriter();
getSymbolDisplayBuilder().buildTypeDisplay(type, writer, enclosingDeclaration, flags);
var result = writer.string();
releaseStringWriter(writer);
var 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) {
var 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.
var _displayBuilder: SymbolDisplayBuilder;
function getSymbolDisplayBuilder(): SymbolDisplayBuilder {
/**
* Writes only the name of the symbol out to the writer. Uses the original source text
* for the name of the symbol if it is available to match how the user inputted the name.
*/
function appendSymbolNameOnly(symbol: Symbol, writer: SymbolWriter): void {
if (symbol.declarations && symbol.declarations.length > 0) {
var declaration = symbol.declarations[0];
if (declaration.name) {
writer.writeSymbol(declarationNameToString(declaration.name), symbol);
return;
}
}
writer.writeSymbol(symbol.name, 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 {
var 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) {
var 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 (var i = 0, n = accessibleSymbolChain.length; i < n; i++) {
appendParentTypeArgumentsAndSymbolName(accessibleSymbolChain[i]);
}
}
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
var isTypeParameter = symbol.flags & SymbolFlags.TypeParameter;
var 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, typeStack?: Type[]) {
var 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) &&
(type.flags & TypeFlags.Any) ? "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.Union) {
writeUnionType(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[], union: boolean) {
for (var i = 0; i < types.length; i++) {
if (i > 0) {
if (union) {
writeSpace(writer);
}
writePunctuation(writer, union ? SyntaxKind.BarToken : SyntaxKind.CommaToken);
writeSpace(writer);
}
writeType(types[i], union ? TypeFormatFlags.InElementType : TypeFormatFlags.None);
}
}
function writeTypeReference(type: TypeReference, flags: TypeFormatFlags) {
if (type.target === globalArrayType && !(flags & TypeFormatFlags.WriteArrayAsGenericType)) {
writeType(type.typeArguments[0], TypeFormatFlags.InElementType);
writePunctuation(writer, SyntaxKind.OpenBracketToken);
writePunctuation(writer, SyntaxKind.CloseBracketToken);
}
else {
buildSymbolDisplay(type.target.symbol, writer, enclosingDeclaration, SymbolFlags.Type);
writePunctuation(writer, SyntaxKind.LessThanToken);
writeTypeList(type.typeArguments, /*union*/ false);
writePunctuation(writer, SyntaxKind.GreaterThanToken);
}
}
function writeTupleType(type: TupleType) {
writePunctuation(writer, SyntaxKind.OpenBracketToken);
writeTypeList(type.elementTypes, /*union*/ false);
writePunctuation(writer, SyntaxKind.CloseBracketToken);
}
function writeUnionType(type: UnionType, flags: TypeFormatFlags) {
if (flags & TypeFormatFlags.InElementType) {
writePunctuation(writer, SyntaxKind.OpenParenToken);
}
writeTypeList(type.types, /*union*/ true);
if (flags & TypeFormatFlags.InElementType) {
writePunctuation(writer, SyntaxKind.CloseParenToken);
}
}
function writeAnonymousType(type: ObjectType, flags: TypeFormatFlags) {
// Always use 'typeof T' for type of class, enum, and module objects
if (type.symbol && type.symbol.flags & (SymbolFlags.Class | SymbolFlags.Enum | SymbolFlags.ValueModule)) {
writeTypeofSymbol(type, flags);
}
// Use 'typeof T' for types of functions and methods that circularly reference themselves
else if (shouldWriteTypeOfFunctionSymbol()) {
writeTypeofSymbol(type, flags);
}
else if (typeStack && contains(typeStack, type)) {
// If type is an anonymous type literal in a type alias declaration, use type alias name
var 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 {
if (!typeStack) {
typeStack = [];
}
typeStack.push(type);
writeLiteralType(type, flags);
typeStack.pop();
}
function shouldWriteTypeOfFunctionSymbol() {
if (type.symbol) {
var isStaticMethodSymbol = !!(type.symbol.flags & SymbolFlags.Method && // typeof static method
ts.forEach(type.symbol.declarations, declaration => declaration.flags & NodeFlags.Static));
var isNonLocalFunctionSymbol = !!(type.symbol.flags & SymbolFlags.Function) &&
(type.symbol.parent || // is exported function symbol
ts.forEach(type.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
(typeStack && contains(typeStack, type)); // 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 {
var 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) {
var resolved = resolveObjectOrUnionTypeMembers(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, typeStack);
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, typeStack);
if (flags & TypeFormatFlags.InElementType) {
writePunctuation(writer, SyntaxKind.CloseParenToken);
}
return;
}
}
writePunctuation(writer, SyntaxKind.OpenBraceToken);
writer.writeLine();
writer.increaseIndent();
for (var i = 0; i < resolved.callSignatures.length; i++) {
buildSignatureDisplay(resolved.callSignatures[i], writer, enclosingDeclaration, globalFlagsToPass, typeStack);
writePunctuation(writer, SyntaxKind.SemicolonToken);
writer.writeLine();
}
for (var i = 0; i < resolved.constructSignatures.length; i++) {
writeKeyword(writer, SyntaxKind.NewKeyword);
writeSpace(writer);
buildSignatureDisplay(resolved.constructSignatures[i], writer, enclosingDeclaration, globalFlagsToPass, typeStack);
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 (var i = 0; i < resolved.properties.length; i++) {
var p = resolved.properties[i];
var t = getTypeOfSymbol(p);
if (p.flags & (SymbolFlags.Function | SymbolFlags.Method) && !getPropertiesOfObjectType(t).length) {
var signatures = getSignaturesOfType(t, SignatureKind.Call);
for (var j = 0; j < signatures.length; j++) {
buildSymbolDisplay(p, writer);
if (p.flags & SymbolFlags.Optional) {
writePunctuation(writer, SyntaxKind.QuestionToken);
}
buildSignatureDisplay(signatures[j], writer, enclosingDeclaration, globalFlagsToPass, typeStack);
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) {
var targetSymbol = getTargetSymbol(symbol);
if (targetSymbol.flags & SymbolFlags.Class || targetSymbol.flags & SymbolFlags.Interface) {
buildDisplayForTypeParametersAndDelimiters(getTypeParametersOfClassOrInterface(symbol), writer, enclosingDeclaraiton, flags);
}
}
function buildTypeParameterDisplay(tp: TypeParameter, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, typeStack?: Type[]) {
appendSymbolNameOnly(tp.symbol, writer);
var constraint = getConstraintOfTypeParameter(tp);
if (constraint) {
writeSpace(writer);
writeKeyword(writer, SyntaxKind.ExtendsKeyword);
writeSpace(writer);
buildTypeDisplay(constraint, writer, enclosingDeclaration, flags, typeStack);
}
}
function buildParameterDisplay(p: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, typeStack?: Type[]) {
if (hasDotDotDotToken(p.valueDeclaration)) {
writePunctuation(writer, SyntaxKind.DotDotDotToken);
}
appendSymbolNameOnly(p, writer);
if (hasQuestionToken(p.valueDeclaration) || (p.valueDeclaration).initializer) {
writePunctuation(writer, SyntaxKind.QuestionToken);
}
writePunctuation(writer, SyntaxKind.ColonToken);
writeSpace(writer);
buildTypeDisplay(getTypeOfSymbol(p), writer, enclosingDeclaration, flags, typeStack);
}
function buildDisplayForTypeParametersAndDelimiters(typeParameters: TypeParameter[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, typeStack?: Type[]) {
if (typeParameters && typeParameters.length) {
writePunctuation(writer, SyntaxKind.LessThanToken);
for (var i = 0; i < typeParameters.length; i++) {
if (i > 0) {
writePunctuation(writer, SyntaxKind.CommaToken);
writeSpace(writer);
}
buildTypeParameterDisplay(typeParameters[i], writer, enclosingDeclaration, flags, typeStack);
}
writePunctuation(writer, SyntaxKind.GreaterThanToken);
}
}
function buildDisplayForTypeArgumentsAndDelimiters(typeParameters: TypeParameter[], mapper: TypeMapper, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, typeStack?: Type[]) {
if (typeParameters && typeParameters.length) {
writePunctuation(writer, SyntaxKind.LessThanToken);
for (var 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, typeStack?: Type[]) {
writePunctuation(writer, SyntaxKind.OpenParenToken);
for (var i = 0; i < parameters.length; i++) {
if (i > 0) {
writePunctuation(writer, SyntaxKind.CommaToken);
writeSpace(writer);
}
buildParameterDisplay(parameters[i], writer, enclosingDeclaration, flags, typeStack);
}
writePunctuation(writer, SyntaxKind.CloseParenToken);
}
function buildReturnTypeDisplay(signature: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, typeStack?: Type[]) {
if (flags & TypeFormatFlags.WriteArrowStyleSignature) {
writeSpace(writer);
writePunctuation(writer, SyntaxKind.EqualsGreaterThanToken);
}
else {
writePunctuation(writer, SyntaxKind.ColonToken);
}
writeSpace(writer);
buildTypeDisplay(getReturnTypeOfSignature(signature), writer, enclosingDeclaration, flags, typeStack);
}
function buildSignatureDisplay(signature: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, typeStack?: Type[]) {
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, typeStack);
}
buildDisplayForParametersAndDelimiters(signature.parameters, writer, enclosingDeclaration, flags, typeStack);
buildReturnTypeDisplay(signature, writer, enclosingDeclaration, flags, typeStack);
}
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
var externalModule = getContainingExternalModule(node);
if (externalModule) {
// This is export assigned symbol node
var externalModuleSymbol = getSymbolOfNode(externalModule);
var exportAssignmentSymbol = getExportAssignmentSymbol(externalModuleSymbol);
var resolvedExportSymbol: Symbol;
var symbolOfNode = getSymbolOfNode(node);
if (isSymbolUsedInExportAssignment(symbolOfNode)) {
return true;
}
// if symbolOfNode is import declaration, resolve the symbol declaration and check
if (symbolOfNode.flags & SymbolFlags.Import) {
return isSymbolUsedInExportAssignment(resolveImport(symbolOfNode));
}
}
// Check if the symbol is used in export assignment
function isSymbolUsedInExportAssignment(symbol: Symbol) {
if (exportAssignmentSymbol === symbol) {
return true;
}
if (exportAssignmentSymbol && !!(exportAssignmentSymbol.flags & SymbolFlags.Import)) {
// if export assigned symbol is import declaration, resolve the import
resolvedExportSymbol = resolvedExportSymbol || resolveImport(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.VariableDeclaration:
case SyntaxKind.BindingElement:
case SyntaxKind.ModuleDeclaration:
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.TypeAliasDeclaration:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.EnumDeclaration:
case SyntaxKind.ImportDeclaration:
var 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.ImportDeclaration && parent.kind !== SyntaxKind.SourceFile && isInAmbientContext(parent))) {
return isGlobalSourceFile(parent) || isUsedInExportAssignment(node);
}
// 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.ParenthesizedType:
return isDeclarationVisible(node.parent);
// Type parameters are always visible
case SyntaxKind.TypeParameter:
// Source file is always visible
case SyntaxKind.SourceFile:
return true;
default:
Debug.fail("isDeclarationVisible unknown: SyntaxKind: " + node.kind);
}
}
if (node) {
var links = getNodeLinks(node);
if (links.isVisible === undefined) {
links.isVisible = !!determineIfDeclarationIsVisible();
}
return links.isVisible;
}
}
function getRootDeclaration(node: Node): Node {
while (node.kind === SyntaxKind.BindingElement) {
node = node.parent.parent;
}
return node;
}
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'.
var 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 {
var prop = getPropertyOfType(type, name);
return prop ? getTypeOfSymbol(prop) : undefined;
}
// Return the inferred type for a binding element
function getTypeForBindingElement(declaration: BindingElement): Type {
var pattern = declaration.parent;
var 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 || parentType === anyType) {
if (declaration.initializer) {
return checkExpressionCached(declaration.initializer);
}
return parentType;
}
if (pattern.kind === SyntaxKind.ObjectBindingPattern) {
// Use explicitly specified property name ({ p: xxx } form), or otherwise the implied name ({ p } form)
var 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.
var 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 {
// For an array binding element the specified or inferred type of the parent must be assignable to any[]
if (!isTypeAssignableTo(parentType, anyArrayType)) {
error(pattern, Diagnostics.Type_0_is_not_an_array_type, typeToString(parentType));
return unknownType;
}
if (!declaration.dotDotDotToken) {
// Use specific property type when parent is a tuple or numeric index type when parent is an array
var propName = "" + indexOf(pattern.elements, declaration);
var type = isTupleLikeType(parentType) ? getTypeOfPropertyOfType(parentType, propName) : getIndexTypeOfType(parentType, IndexKind.Number);
if (!type) {
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
var type = createArrayType(getIndexTypeOfType(parentType, IndexKind.Number));
}
}
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 (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) {
var 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)) {
var getter = getDeclarationOfKind(declaration.parent.symbol, SyntaxKind.GetAccessor);
if (getter) {
return getReturnTypeOfSignature(getSignatureFromDeclaration(getter));
}
}
// Use contextual parameter type if one is available
var 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 {
var members: SymbolTable = {};
forEach(pattern.elements, e => {
var flags = SymbolFlags.Property | SymbolFlags.Transient | (e.initializer ? SymbolFlags.Optional : 0);
var name = e.propertyName || e.name;
var 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 {
var hasSpreadElement: boolean = false;
var elementTypes: Type[] = [];
forEach(pattern.elements, e => {
elementTypes.push(e.kind === SyntaxKind.OmittedExpression || e.dotDotDotToken ? anyType : getTypeFromBindingElement(e));
if (e.dotDotDotToken) {
hasSpreadElement = true;
}
});
return !elementTypes.length ? anyArrayType : hasSpreadElement ? createArrayType(getUnionType(elementTypes)) : 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 {
var 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) {
var root = getRootDeclaration(declaration);
if (!isPrivateWithinAmbient(root) && !(root.kind === SyntaxKind.Parameter && isPrivateWithinAmbient(root.parent))) {
reportImplicitAnyError(declaration, type);
}
}
return type;
}
function getTypeOfVariableOrParameterOrProperty(symbol: Symbol): Type {
var links = getSymbolLinks(symbol);
if (!links.type) {
// Handle prototype property
if (symbol.flags & SymbolFlags.Prototype) {
return links.type = getTypeOfPrototypeProperty(symbol);
}
// Handle catch clause variables
var declaration = symbol.valueDeclaration;
if (declaration.kind === SyntaxKind.CatchClause) {
return links.type = anyType;
}
// Handle variable, parameter or property
links.type = resolvingType;
var type = getWidenedTypeForVariableLikeDeclaration(declaration, /*reportErrors*/ true);
if (links.type === resolvingType) {
links.type = type;
}
}
else if (links.type === resolvingType) {
links.type = anyType;
if (compilerOptions.noImplicitAny) {
var diagnostic = (symbol.valueDeclaration).type ?
Diagnostics._0_implicitly_has_type_any_because_it_is_referenced_directly_or_indirectly_in_its_own_type_annotation :
Diagnostics._0_implicitly_has_type_any_because_it_is_does_not_have_a_type_annotation_and_is_referenced_directly_or_indirectly_in_its_own_initializer;
error(symbol.valueDeclaration, diagnostic, symbolToString(symbol));
}
}
return links.type;
}
function getSetAccessorTypeAnnotationNode(accessor: AccessorDeclaration): TypeNode | LiteralExpression {
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 {
var setterTypeAnnotation = getSetAccessorTypeAnnotationNode(accessor);
return setterTypeAnnotation && getTypeFromTypeNode(setterTypeAnnotation);
}
}
return undefined;
}
function getTypeOfAccessors(symbol: Symbol): Type {
var links = getSymbolLinks(symbol);
checkAndStoreTypeOfAccessors(symbol, links);
return links.type;
}
function checkAndStoreTypeOfAccessors(symbol: Symbol, links?: SymbolLinks) {
links = links || getSymbolLinks(symbol);
if (!links.type) {
links.type = resolvingType;
var getter = getDeclarationOfKind(symbol, SyntaxKind.GetAccessor);
var setter = getDeclarationOfKind(symbol, SyntaxKind.SetAccessor);
var type: Type;
// First try to see if the user specified a return type on the get-accessor.
var 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.
var 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 (links.type === resolvingType) {
links.type = type;
}
}
else if (links.type === resolvingType) {
links.type = anyType;
if (compilerOptions.noImplicitAny) {
var 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));
}
}
}
function getTypeOfFuncClassEnumModule(symbol: Symbol): Type {
var links = getSymbolLinks(symbol);
if (!links.type) {
links.type = createObjectType(TypeFlags.Anonymous, symbol);
}
return links.type;
}
function getTypeOfEnumMember(symbol: Symbol): Type {
var links = getSymbolLinks(symbol);
if (!links.type) {
links.type = getDeclaredTypeOfEnum(getParentOfSymbol(symbol));
}
return links.type;
}
function getTypeOfImport(symbol: Symbol): Type {
var links = getSymbolLinks(symbol);
if (!links.type) {
links.type = getTypeOfSymbol(resolveImport(symbol));
}
return links.type;
}
function getTypeOfInstantiatedSymbol(symbol: Symbol): Type {
var 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.Import) {
return getTypeOfImport(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 {
var target = getTargetType(type);
return target === checkBase || forEach(target.baseTypes, check);
}
}
// Return combined list of type parameters from all declarations of a class or interface. Elsewhere we check they're all
// the same, but even if they're not we still need the complete list to ensure instantiations supply type arguments
// for all type parameters.
function getTypeParametersOfClassOrInterface(symbol: Symbol): TypeParameter[] {
var result: TypeParameter[];
forEach(symbol.declarations, node => {
if (node.kind === SyntaxKind.InterfaceDeclaration || node.kind === SyntaxKind.ClassDeclaration) {
var declaration = node;
if (declaration.typeParameters && declaration.typeParameters.length) {
forEach(declaration.typeParameters, node => {
var tp = getDeclaredTypeOfTypeParameter(getSymbolOfNode(node));
if (!result) {
result = [tp];
}
else if (!contains(result, tp)) {
result.push(tp);
}
});
}
}
});
return result;
}
function getDeclaredTypeOfClass(symbol: Symbol): InterfaceType {
var links = getSymbolLinks(symbol);
if (!links.declaredType) {
var type = links.declaredType = createObjectType(TypeFlags.Class, symbol);
var typeParameters = getTypeParametersOfClassOrInterface(symbol);
if (typeParameters) {
type.flags |= TypeFlags.Reference;
type.typeParameters = typeParameters;
(type).instantiations = {};
(type).instantiations[getTypeListId(type.typeParameters)] = type;
(type).target = type;
(type).typeArguments = type.typeParameters;
}
type.baseTypes = [];
var declaration = getDeclarationOfKind(symbol, SyntaxKind.ClassDeclaration);
var baseTypeNode = getClassBaseTypeNode(declaration);
if (baseTypeNode) {
var baseType = getTypeFromTypeReferenceNode(baseTypeNode);
if (baseType !== unknownType) {
if (getTargetType(baseType).flags & TypeFlags.Class) {
if (type !== baseType && !hasBaseType(baseType, type)) {
type.baseTypes.push(baseType);
}
else {
error(declaration, Diagnostics.Type_0_recursively_references_itself_as_a_base_type, typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType));
}
}
else {
error(baseTypeNode, Diagnostics.A_class_may_only_extend_another_class);
}
}
}
type.declaredProperties = getNamedMembers(symbol.members);
type.declaredCallSignatures = emptyArray;
type.declaredConstructSignatures = emptyArray;
type.declaredStringIndexType = getIndexTypeOfSymbol(symbol, IndexKind.String);
type.declaredNumberIndexType = getIndexTypeOfSymbol(symbol, IndexKind.Number);
}
return links.declaredType;
}
function getDeclaredTypeOfInterface(symbol: Symbol): InterfaceType {
var links = getSymbolLinks(symbol);
if (!links.declaredType) {
var type = links.declaredType = createObjectType(TypeFlags.Interface, symbol);
var typeParameters = getTypeParametersOfClassOrInterface(symbol);
if (typeParameters) {
type.flags |= TypeFlags.Reference;
type.typeParameters = typeParameters;
(type).instantiations = {};
(type).instantiations[getTypeListId(type.typeParameters)] = type;
(type).target = type;
(type).typeArguments = type.typeParameters;
}
type.baseTypes = [];
forEach(symbol.declarations, declaration => {
if (declaration.kind === SyntaxKind.InterfaceDeclaration && getInterfaceBaseTypeNodes(declaration)) {
forEach(getInterfaceBaseTypeNodes(declaration), node => {
var baseType = getTypeFromTypeReferenceNode(node);
if (baseType !== unknownType) {
if (getTargetType(baseType).flags & (TypeFlags.Class | TypeFlags.Interface)) {
if (type !== baseType && !hasBaseType(baseType, type)) {
type.baseTypes.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);
}
}
});
}
});
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 links.declaredType;
}
function getDeclaredTypeOfTypeAlias(symbol: Symbol): Type {
var links = getSymbolLinks(symbol);
if (!links.declaredType) {
links.declaredType = resolvingType;
var declaration = getDeclarationOfKind(symbol, SyntaxKind.TypeAliasDeclaration);
var type = getTypeFromTypeNode(declaration.type);
if (links.declaredType === resolvingType) {
links.declaredType = type;
}
}
else if (links.declaredType === resolvingType) {
links.declaredType = unknownType;
var declaration = getDeclarationOfKind(symbol, SyntaxKind.TypeAliasDeclaration);
error(declaration.name, Diagnostics.Type_alias_0_circularly_references_itself, symbolToString(symbol));
}
return links.declaredType;
}
function getDeclaredTypeOfEnum(symbol: Symbol): Type {
var links = getSymbolLinks(symbol);
if (!links.declaredType) {
var type = createType(TypeFlags.Enum);
type.symbol = symbol;
links.declaredType = type;
}
return links.declaredType;
}
function getDeclaredTypeOfTypeParameter(symbol: Symbol): TypeParameter {
var links = getSymbolLinks(symbol);
if (!links.declaredType) {
var type = createType(TypeFlags.TypeParameter);
type.symbol = symbol;
if (!(getDeclarationOfKind(symbol, SyntaxKind.TypeParameter)).constraint) {
type.constraint = noConstraintType;
}
links.declaredType = type;
}
return links.declaredType;
}
function getDeclaredTypeOfImport(symbol: Symbol): Type {
var links = getSymbolLinks(symbol);
if (!links.declaredType) {
links.declaredType = getDeclaredTypeOfSymbol(resolveImport(symbol));
}
return links.declaredType;
}
function getDeclaredTypeOfSymbol(symbol: Symbol): Type {
Debug.assert((symbol.flags & SymbolFlags.Instantiated) === 0);
if (symbol.flags & SymbolFlags.Class) {
return getDeclaredTypeOfClass(symbol);
}
if (symbol.flags & SymbolFlags.Interface) {
return getDeclaredTypeOfInterface(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.Import) {
return getDeclaredTypeOfImport(symbol);
}
return unknownType;
}
function createSymbolTable(symbols: Symbol[]): SymbolTable {
var result: SymbolTable = {};
for (var i = 0; i < symbols.length; i++) {
var symbol = symbols[i];
result[symbol.name] = symbol;
}
return result;
}
function createInstantiatedSymbolTable(symbols: Symbol[], mapper: TypeMapper): SymbolTable {
var result: SymbolTable = {};
for (var i = 0; i < symbols.length; i++) {
var symbol = symbols[i];
result[symbol.name] = instantiateSymbol(symbol, mapper);
}
return result;
}
function addInheritedMembers(symbols: SymbolTable, baseSymbols: Symbol[]) {
for (var i = 0; i < baseSymbols.length; i++) {
var s = baseSymbols[i];
if (!hasProperty(symbols, s.name)) {
symbols[s.name] = s;
}
}
}
function addInheritedSignatures(signatures: Signature[], baseSignatures: Signature[]) {
if (baseSignatures) {
for (var i = 0; i < baseSignatures.length; i++) {
signatures.push(baseSignatures[i]);
}
}
}
function resolveClassOrInterfaceMembers(type: InterfaceType): void {
var members = type.symbol.members;
var callSignatures = type.declaredCallSignatures;
var constructSignatures = type.declaredConstructSignatures;
var stringIndexType = type.declaredStringIndexType;
var numberIndexType = type.declaredNumberIndexType;
if (type.baseTypes.length) {
members = createSymbolTable(type.declaredProperties);
forEach(type.baseTypes, baseType => {
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 {
var target = type.target;
var mapper = createTypeMapper(target.typeParameters, type.typeArguments);
var members = createInstantiatedSymbolTable(target.declaredProperties, mapper);
var callSignatures = instantiateList(target.declaredCallSignatures, mapper, instantiateSignature);
var constructSignatures = instantiateList(target.declaredConstructSignatures, mapper, instantiateSignature);
var stringIndexType = target.declaredStringIndexType ? instantiateType(target.declaredStringIndexType, mapper) : undefined;
var numberIndexType = target.declaredNumberIndexType ? instantiateType(target.declaredNumberIndexType, mapper) : undefined;
forEach(target.baseTypes, baseType => {
var 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, minArgumentCount: number, hasRestParameter: boolean, hasStringLiterals: boolean): Signature {
var sig = new Signature(checker);
sig.declaration = declaration;
sig.typeParameters = typeParameters;
sig.parameters = parameters;
sig.resolvedReturnType = resolvedReturnType;
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.minArgumentCount, sig.hasRestParameter, sig.hasStringLiterals);
}
function getDefaultConstructSignatures(classType: InterfaceType): Signature[] {
if (classType.baseTypes.length) {
var baseType = classType.baseTypes[0];
var baseSignatures = getSignaturesOfType(getTypeOfSymbol(baseType.symbol), SignatureKind.Construct);
return map(baseSignatures, baseSignature => {
var signature = baseType.flags & TypeFlags.Reference ?
getSignatureInstantiation(baseSignature, (baseType).typeArguments) : cloneSignature(baseSignature);
signature.typeParameters = classType.typeParameters;
signature.resolvedReturnType = classType;
return signature;
});
}
return [createSignature(undefined, classType.typeParameters, emptyArray, classType, 0, false, false)];
}
function createTupleTypeMemberSymbols(memberTypes: Type[]): SymbolTable {
var members: SymbolTable = {};
for (var i = 0; i < memberTypes.length; i++) {
var symbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "" + i);
symbol.type = memberTypes[i];
members[i] = symbol;
}
return members;
}
function resolveTupleTypeMembers(type: TupleType) {
var arrayType = resolveObjectOrUnionTypeMembers(createArrayType(getUnionType(type.elementTypes)));
var 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 (var 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[] {
var signatureLists = map(types, t => getSignaturesOfType(t, kind));
var signatures = signatureLists[0];
for (var i = 0; i < signatures.length; i++) {
if (signatures[i].typeParameters) {
return emptyArray;
}
}
for (var i = 1; i < signatureLists.length; i++) {
if (!signatureListsIdentical(signatures, signatureLists[i])) {
return emptyArray;
}
}
var result = map(signatures, cloneSignature);
for (var i = 0; i < result.length; i++) {
var 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 {
var indexTypes: Type[] = [];
for (var i = 0; i < types.length; i++) {
var indexType = getIndexTypeOfType(types[i], 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).
var callSignatures = getUnionSignatures(type.types, SignatureKind.Call);
var constructSignatures = getUnionSignatures(type.types, SignatureKind.Construct);
var stringIndexType = getUnionIndexType(type.types, IndexKind.String);
var numberIndexType = getUnionIndexType(type.types, IndexKind.Number);
setObjectTypeMembers(type, emptySymbols, callSignatures, constructSignatures, stringIndexType, numberIndexType);
}
function resolveAnonymousTypeMembers(type: ObjectType) {
var symbol = type.symbol;
if (symbol.flags & SymbolFlags.TypeLiteral) {
var members = symbol.members;
var callSignatures = getSignaturesOfSymbol(members["__call"]);
var constructSignatures = getSignaturesOfSymbol(members["__new"]);
var stringIndexType = getIndexTypeOfSymbol(symbol, IndexKind.String);
var numberIndexType = getIndexTypeOfSymbol(symbol, IndexKind.Number);
}
else {
// Combinations of function, class, enum and module
var members = emptySymbols;
var callSignatures: Signature[] = emptyArray;
var constructSignatures: Signature[] = emptyArray;
if (symbol.flags & SymbolFlags.HasExports) {
members = symbol.exports;
}
if (symbol.flags & (SymbolFlags.Function | SymbolFlags.Method)) {
callSignatures = getSignaturesOfSymbol(symbol);
}
if (symbol.flags & SymbolFlags.Class) {
var classType = getDeclaredTypeOfClass(symbol);
constructSignatures = getSignaturesOfSymbol(symbol.members["__constructor"]);
if (!constructSignatures.length) {
constructSignatures = getDefaultConstructSignatures(classType);
}
if (classType.baseTypes.length) {
members = createSymbolTable(getNamedMembers(members));
addInheritedMembers(members, getPropertiesOfObjectType(getTypeOfSymbol(classType.baseTypes[0].symbol)));
}
}
var stringIndexType: Type = undefined;
var numberIndexType: Type = (symbol.flags & SymbolFlags.Enum) ? stringType : undefined;
}
setObjectTypeMembers(type, members, callSignatures, constructSignatures, stringIndexType, numberIndexType);
}
function resolveObjectOrUnionTypeMembers(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 {
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 resolveObjectOrUnionTypeMembers(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) {
var resolved = resolveObjectOrUnionTypeMembers(type);
if (hasProperty(resolved.members, name)) {
var symbol = resolved.members[name];
if (symbolIsValue(symbol)) {
return symbol;
}
}
}
}
function getPropertiesOfUnionType(type: UnionType): Symbol[] {
var result: Symbol[] = [];
forEach(getPropertiesOfType(type.types[0]), prop => {
var unionProp = getPropertyOfUnionType(type, prop.name);
if (unionProp) {
result.push(unionProp);
}
});
return result;
}
function getPropertiesOfType(type: Type): Symbol[] {
if (type.flags & TypeFlags.Union) {
return getPropertiesOfUnionType(type);
}
return getPropertiesOfObjectType(getApparentType(type));
}
// For a type parameter, return the base constraint of the type parameter. For the string, number, and
// boolean 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.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 createUnionProperty(unionType: UnionType, name: string): Symbol {
var types = unionType.types;
var props: Symbol[];
for (var i = 0; i < types.length; i++) {
var type = getApparentType(types[i]);
if (type !== unknownType) {
var prop = getPropertyOfType(type, name);
if (!prop) {
return undefined;
}
if (!props) {
props = [prop];
}
else {
props.push(prop);
}
}
}
var propTypes: Type[] = [];
var declarations: Declaration[] = [];
for (var i = 0; i < props.length; i++) {
var prop = props[i];
if (prop.declarations) {
declarations.push.apply(declarations, prop.declarations);
}
propTypes.push(getTypeOfSymbol(prop));
}
var result = createSymbol(SymbolFlags.Property | SymbolFlags.Transient | SymbolFlags.UnionProperty, name);
result.unionType = unionType;
result.declarations = declarations;
result.type = getUnionType(propTypes);
return result;
}
function getPropertyOfUnionType(type: UnionType, name: string): Symbol {
var properties = type.resolvedProperties || (type.resolvedProperties = {});
if (hasProperty(properties, name)) {
return properties[name];
}
var property = createUnionProperty(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 {
if (type.flags & TypeFlags.Union) {
return getPropertyOfUnionType(type, name);
}
if (!(type.flags & TypeFlags.ObjectType)) {
type = getApparentType(type);
if (!(type.flags & TypeFlags.ObjectType)) {
return undefined;
}
}
var resolved = resolveObjectOrUnionTypeMembers(type);
if (hasProperty(resolved.members, name)) {
var symbol = resolved.members[name];
if (symbolIsValue(symbol)) {
return symbol;
}
}
if (resolved === anyFunctionType || resolved.callSignatures.length || resolved.constructSignatures.length) {
var symbol = getPropertyOfObjectType(globalFunctionType, name);
if (symbol) return symbol;
}
return getPropertyOfObjectType(globalObjectType, name);
}
function getSignaturesOfObjectOrUnionType(type: Type, kind: SignatureKind): Signature[] {
if (type.flags & (TypeFlags.ObjectType | TypeFlags.Union)) {
var resolved = resolveObjectOrUnionTypeMembers(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 getSignaturesOfObjectOrUnionType(getApparentType(type), kind);
}
function getIndexTypeOfObjectOrUnionType(type: Type, kind: IndexKind): Type {
if (type.flags & (TypeFlags.ObjectType | TypeFlags.Union)) {
var resolved = resolveObjectOrUnionTypeMembers(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 getIndexTypeOfObjectOrUnionType(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[] {
var result: TypeParameter[] = [];
forEach(typeParameterDeclarations, node => {
var tp = getDeclaredTypeOfTypeParameter(node.symbol);
if (!contains(result, tp)) {
result.push(tp);
}
});
return result;
}
function getSignatureFromDeclaration(declaration: SignatureDeclaration): Signature {
var links = getNodeLinks(declaration);
if (!links.resolvedSignature) {
var classType = declaration.kind === SyntaxKind.Constructor ? getDeclaredTypeOfClass((declaration.parent).symbol) : undefined;
var typeParameters = classType ? classType.typeParameters :
declaration.typeParameters ? getTypeParametersFromDeclaration(declaration.typeParameters) : undefined;
var parameters: Symbol[] = [];
var hasStringLiterals = false;
var minArgumentCount = -1;
for (var i = 0, n = declaration.parameters.length; i < n; i++) {
var 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;
}
var returnType: Type;
if (classType) {
returnType = classType;
}
else if (declaration.type) {
returnType = getTypeFromTypeNode(declaration.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)) {
var setter = getDeclarationOfKind(declaration.symbol, SyntaxKind.SetAccessor);
returnType = getAnnotatedAccessorType(setter);
}
if (!returnType && nodeIsMissing((declaration).body)) {
returnType = anyType;
}
}
links.resolvedSignature = createSignature(declaration, typeParameters, parameters, returnType,
minArgumentCount, hasRestParameters(declaration), hasStringLiterals);
}
return links.resolvedSignature;
}
function getSignaturesOfSymbol(symbol: Symbol): Signature[] {
if (!symbol) return emptyArray;
var result: Signature[] = [];
for (var i = 0, len = symbol.declarations.length; i < len; i++) {
var 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) {
var 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) {
signature.resolvedReturnType = resolvingType;
if (signature.target) {
var type = instantiateType(getReturnTypeOfSignature(signature.target), signature.mapper);
}
else if (signature.unionSignatures) {
var type = getUnionType(map(signature.unionSignatures, getReturnTypeOfSignature));
}
else {
var type = getReturnTypeFromBody(signature.declaration);
}
if (signature.resolvedReturnType === resolvingType) {
signature.resolvedReturnType = type;
}
}
else if (signature.resolvedReturnType === resolvingType) {
signature.resolvedReturnType = anyType;
if (compilerOptions.noImplicitAny) {
var 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);
}
}
}
return signature.resolvedReturnType;
}
function getRestTypeOfSignature(signature: Signature): Type {
if (signature.hasRestParameter) {
var type = getTypeOfSymbol(signature.parameters[signature.parameters.length - 1]);
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) {
var isConstructor = signature.declaration.kind === SyntaxKind.Constructor || signature.declaration.kind === SyntaxKind.ConstructSignature;
var 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 {
var syntaxKind = kind === IndexKind.Number ? SyntaxKind.NumberKeyword : SyntaxKind.StringKeyword;
var indexSymbol = getIndexSymbol(symbol);
if (indexSymbol) {
var len = indexSymbol.declarations.length;
for (var i = 0; i < len; i++) {
var node = indexSymbol.declarations[i];
if (node.parameters.length === 1) {
var parameter = node.parameters[0];
if (parameter && parameter.type && parameter.type.kind === syntaxKind) {
return node;
}
}
}
}
return undefined;
}
function getIndexTypeOfSymbol(symbol: Symbol, kind: IndexKind): Type {
var declaration = getIndexDeclarationOfSymbol(symbol, kind);
return declaration
? declaration.type ? getTypeFromTypeNode(declaration.type) : anyType
: undefined;
}
function getConstraintOfTypeParameter(type: TypeParameter): Type {
if (!type.constraint) {
if (type.target) {
var 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 getTypeListId(types: Type[]) {
switch (types.length) {
case 1:
return "" + types[0].id;
case 2:
return types[0].id + "," + types[1].id;
default:
var result = "";
for (var 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 {
var result: TypeFlags = 0;
for (var i = 0; i < types.length; i++) {
result |= types[i].flags;
}
return result & TypeFlags.RequiresWidening;
}
function createTypeReference(target: GenericType, typeArguments: Type[]): TypeReference {
var id = getTypeListId(typeArguments);
var type = target.instantiations[id];
if (!type) {
var 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, typeParameterSymbol: Symbol): boolean {
var links = getNodeLinks(typeReferenceNode);
if (links.isIllegalTypeReferenceInConstraint !== undefined) {
return links.isIllegalTypeReferenceInConstraint;
}
// bubble up to the declaration
var 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 {
var typeParameterSymbol: Symbol;
function check(n: Node): void {
if (n.kind === SyntaxKind.TypeReference && (n).typeName.kind === SyntaxKind.Identifier) {
var links = getNodeLinks(n);
if (links.isIllegalTypeReferenceInConstraint === undefined) {
var 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);
}
}
function getTypeFromTypeReferenceNode(node: TypeReferenceNode): Type {
var links = getNodeLinks(node);
if (!links.resolvedType) {
var symbol = resolveEntityName(node, node.typeName, SymbolFlags.Type);
if (symbol) {
var type: 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
type = unknownType;
}
else {
type = getDeclaredTypeOfSymbol(symbol);
if (type.flags & (TypeFlags.Class | TypeFlags.Interface) && type.flags & TypeFlags.Reference) {
var typeParameters = (type).typeParameters;
if (node.typeArguments && node.typeArguments.length === typeParameters.length) {
type = createTypeReference(type, map(node.typeArguments, getTypeFromTypeNode));
}
else {
error(node, Diagnostics.Generic_type_0_requires_1_type_argument_s, typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType), typeParameters.length);
type = undefined;
}
}
else {
if (node.typeArguments) {
error(node, Diagnostics.Type_0_is_not_generic, typeToString(type));
type = undefined;
}
}
}
}
links.resolvedType = type || unknownType;
}
return links.resolvedType;
}
function getTypeFromTypeQueryNode(node: TypeQueryNode): Type {
var 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(checkExpressionOrQualifiedName(node.exprName));
}
return links.resolvedType;
}
function getTypeOfGlobalSymbol(symbol: Symbol, arity: number): ObjectType {
function getTypeDeclaration(symbol: Symbol): Declaration {
var declarations = symbol.declarations;
for (var i = 0; i < declarations.length; i++) {
var declaration = declarations[i];
switch (declaration.kind) {
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.EnumDeclaration:
return declaration;
}
}
}
if (!symbol) {
return emptyObjectType;
}
var 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 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 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): ObjectType {
return getTypeOfGlobalSymbol(getGlobalTypeSymbol(name), 0);
}
function getGlobalESSymbolConstructorSymbol() {
return globalESSymbolConstructorSymbol || (globalESSymbolConstructorSymbol = getGlobalValueSymbol("Symbol"));
}
function createArrayType(elementType: Type): Type {
// globalArrayType will be undefined if we get here during creation of the Array type. This for example happens if
// user code augments the Array type with call or construct signatures that have an array type as the return type.
// We instead use globalArraySymbol to obtain the (not yet fully constructed) Array type.
var arrayType = globalArrayType || getDeclaredTypeOfSymbol(globalArraySymbol);
return arrayType !== emptyObjectType ? createTypeReference(arrayType, [elementType]) : emptyObjectType;
}
function getTypeFromArrayTypeNode(node: ArrayTypeNode): Type {
var links = getNodeLinks(node);
if (!links.resolvedType) {
links.resolvedType = createArrayType(getTypeFromTypeNode(node.elementType));
}
return links.resolvedType;
}
function createTupleType(elementTypes: Type[]) {
var id = getTypeListId(elementTypes);
var type = tupleTypes[id];
if (!type) {
type = tupleTypes[id] = createObjectType(TypeFlags.Tuple);
type.elementTypes = elementTypes;
}
return type;
}
function getTypeFromTupleTypeNode(node: TupleTypeNode): Type {
var links = getNodeLinks(node);
if (!links.resolvedType) {
links.resolvedType = createTupleType(map(node.elementTypes, getTypeFromTypeNode));
}
return links.resolvedType;
}
function addTypeToSortedSet(sortedSet: Type[], type: Type) {
if (type.flags & TypeFlags.Union) {
addTypesToSortedSet(sortedSet, (type).types);
}
else {
var i = 0;
var id = type.id;
while (i < sortedSet.length && sortedSet[i].id < id) {
i++;
}
if (i === sortedSet.length || sortedSet[i].id !== id) {
sortedSet.splice(i, 0, type);
}
}
}
function addTypesToSortedSet(sortedTypes: Type[], types: Type[]) {
for (var i = 0, len = types.length; i < len; i++) {
addTypeToSortedSet(sortedTypes, types[i]);
}
}
function isSubtypeOfAny(candidate: Type, types: Type[]): boolean {
for (var i = 0, len = types.length; i < len; i++) {
if (candidate !== types[i] && isTypeSubtypeOf(candidate, types[i])) {
return true;
}
}
return false;
}
function removeSubtypes(types: Type[]) {
var i = types.length;
while (i > 0) {
i--;
if (isSubtypeOfAny(types[i], types)) {
types.splice(i, 1);
}
}
}
function containsAnyType(types: Type[]) {
for (var i = 0; i < types.length; i++) {
if (types[i].flags & TypeFlags.Any) {
return true;
}
}
return false;
}
function removeAllButLast(types: Type[], typeToRemove: Type) {
var i = types.length;
while (i > 0 && types.length > 1) {
i--;
if (types[i] === typeToRemove) {
types.splice(i, 1);
}
}
}
function getUnionType(types: Type[], noSubtypeReduction?: boolean): Type {
if (types.length === 0) {
return emptyObjectType;
}
var sortedTypes: Type[] = [];
addTypesToSortedSet(sortedTypes, types);
if (noSubtypeReduction) {
if (containsAnyType(sortedTypes)) {
return anyType;
}
removeAllButLast(sortedTypes, undefinedType);
removeAllButLast(sortedTypes, nullType);
}
else {
removeSubtypes(sortedTypes);
}
if (sortedTypes.length === 1) {
return sortedTypes[0];
}
var id = getTypeListId(sortedTypes);
var type = unionTypes[id];
if (!type) {
type = unionTypes[id] = createObjectType(TypeFlags.Union | getWideningFlagsOfTypes(sortedTypes));
type.types = sortedTypes;
}
return type;
}
function getTypeFromUnionTypeNode(node: UnionTypeNode): Type {
var links = getNodeLinks(node);
if (!links.resolvedType) {
links.resolvedType = getUnionType(map(node.types, getTypeFromTypeNode), /*noSubtypeReduction*/ true);
}
return links.resolvedType;
}
function getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node: Node): Type {
var 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: LiteralExpression): StringLiteralType {
if (hasProperty(stringLiteralTypes, node.text)) {
return stringLiteralTypes[node.text];
}
var type = stringLiteralTypes[node.text] = createType(TypeFlags.StringLiteral);
type.text = getTextOfNode(node);
return type;
}
function getTypeFromStringLiteral(node: LiteralExpression): Type {
var links = getNodeLinks(node);
if (!links.resolvedType) {
links.resolvedType = getStringLiteralType(node);
}
return links.resolvedType;
}
function getTypeFromTypeNode(node: TypeNode | LiteralExpression): 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 getTypeFromTypeReferenceNode(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.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:
var 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) {
var result: T[] = [];
for (var i = 0; i < items.length; i++) {
result.push(instantiator(items[i], 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 (var 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 (var i = 0; i < sources.length; i++) {
if (t === sources[i]) return anyType;
}
return t;
};
}
function createInferenceMapper(context: InferenceContext): TypeMapper {
return t => {
for (var i = 0; i < context.typeParameters.length; i++) {
if (t === context.typeParameters[i]) {
return getInferredType(context, i);
}
}
return t;
}
}
function identityMapper(type: Type): Type {
return type;
}
function combineTypeMappers(mapper1: TypeMapper, mapper2: TypeMapper): TypeMapper {
return t => mapper2(mapper1(t));
}
function instantiateTypeParameter(typeParameter: TypeParameter, mapper: TypeMapper): TypeParameter {
var 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 {
if (signature.typeParameters && !eraseTypeParameters) {
var freshTypeParameters = instantiateList(signature.typeParameters, mapper, instantiateTypeParameter);
mapper = combineTypeMappers(createTypeMapper(signature.typeParameters, freshTypeParameters), mapper);
}
var result = createSignature(signature.declaration, freshTypeParameters,
instantiateList(signature.parameters, mapper, instantiateSymbol),
signature.resolvedReturnType ? instantiateType(signature.resolvedReturnType, mapper) : undefined,
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) {
var 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.
var 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 {
var result = createObjectType(TypeFlags.Anonymous, 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);
var stringIndexType = getIndexTypeOfType(type, IndexKind.String);
var 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.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);
}
}
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).operator === 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 getTypeWithoutConstructors(type: Type): Type {
if (type.flags & TypeFlags.ObjectType) {
var resolved = resolveObjectOrUnionTypeMembers(type);
if (resolved.constructSignatures.length) {
var result = createObjectType(TypeFlags.Anonymous, type.symbol);
result.members = resolved.members;
result.properties = resolved.properties;
result.callSignatures = resolved.callSignatures;
result.constructSignatures = emptyArray;
type = result;
}
}
return type;
}
// TYPE CHECKING
var subtypeRelation: Map = {};
var assignableRelation: Map = {};
var identityRelation: Map = {};
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): boolean {
return checkTypeRelatedTo(source, target, assignableRelation, errorNode, headMessage);
}
function isSignatureAssignableTo(source: Signature, target: Signature): boolean {
var sourceType = getOrCreateTypeFromSignature(source);
var targetType = getOrCreateTypeFromSignature(target);
return checkTypeRelatedTo(sourceType, targetType, assignableRelation, /*errorNode*/ undefined);
}
function checkTypeRelatedTo(
source: Type,
target: Type,
relation: Map,
errorNode: Node,
headMessage?: DiagnosticMessage,
containingMessageChain?: DiagnosticMessageChain): boolean {
var errorInfo: DiagnosticMessageChain;
var sourceStack: ObjectType[];
var targetStack: ObjectType[];
var maybeStack: Map[];
var expandingFlags: number;
var depth = 0;
var overflow = false;
Debug.assert(relation !== identityRelation || !errorNode, "no error reporting in identity checking");
var 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;
isRelatedTo(source, target, errorNode !== undefined, headMessage, /* elaborateErrors */ true);
}
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, elaborateErrors = false): Ternary {
var 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 (target.flags & TypeFlags.Any) 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 (source.flags & TypeFlags.Any) return Ternary.True;
if (source === numberType && target.flags & TypeFlags.Enum) return Ternary.True;
}
}
if (source.flags & TypeFlags.Union || target.flags & TypeFlags.Union) {
if (relation === identityRelation) {
if (source.flags & TypeFlags.Union && target.flags & TypeFlags.Union) {
if (result = unionTypeRelatedToUnionType(source, target)) {
if (result &= unionTypeRelatedToUnionType(target, source)) {
return result;
}
}
}
else if (source.flags & TypeFlags.Union) {
if (result = unionTypeRelatedToType(source, target, reportErrors)) {
return result;
}
}
else {
if (result = unionTypeRelatedToType(target, source, reportErrors)) {
return result;
}
}
}
else {
if (source.flags & TypeFlags.Union) {
if (result = unionTypeRelatedToType(source, target, reportErrors)) {
return result;
}
}
else {
if (result = typeRelatedToUnionType(source, target, reportErrors)) {
return result;
}
}
}
}
else if (source.flags & TypeFlags.TypeParameter && target.flags & TypeFlags.TypeParameter) {
if (result = typeParameterRelatedTo(source, target, reportErrors)) {
return result;
}
}
else {
var 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;
}
}
// Even if relationship doesn't hold for type arguments, it may hold in a structural comparison
// Report structural errors only if we haven't reported any errors yet
var reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo;
// identity relation does not use apparent type
var sourceOrApparentType = relation === identityRelation ? source : getApparentType(source);
if (sourceOrApparentType.flags & TypeFlags.ObjectType && target.flags & TypeFlags.ObjectType &&
(result = objectTypeRelatedTo(sourceOrApparentType, target, reportStructuralErrors, elaborateErrors))) {
errorInfo = saveErrorInfo;
return result;
}
}
if (reportErrors) {
headMessage = headMessage || Diagnostics.Type_0_is_not_assignable_to_type_1;
var sourceType = typeToString(source);
var targetType = typeToString(target);
if (sourceType === targetType) {
sourceType = typeToString(source, /*enclosingDeclaration*/ undefined, TypeFormatFlags.UseFullyQualifiedType);
targetType = typeToString(target, /*enclosingDeclaration*/ undefined, TypeFormatFlags.UseFullyQualifiedType);
}
reportError(headMessage, sourceType, targetType);
}
return Ternary.False;
}
function unionTypeRelatedToUnionType(source: UnionType, target: UnionType): Ternary {
var result = Ternary.True;
var sourceTypes = source.types;
for (var i = 0, len = sourceTypes.length; i < len; i++) {
var related = typeRelatedToUnionType(sourceTypes[i], target, false);
if (!related) {
return Ternary.False;
}
result &= related;
}
return result;
}
function typeRelatedToUnionType(source: Type, target: UnionType, reportErrors: boolean): Ternary {
var targetTypes = target.types;
for (var i = 0, len = targetTypes.length; i < len; i++) {
var related = isRelatedTo(source, targetTypes[i], reportErrors && i === len - 1);
if (related) {
return related;
}
}
return Ternary.False;
}
function unionTypeRelatedToType(source: UnionType, target: Type, reportErrors: boolean): Ternary {
var result = Ternary.True;
var sourceTypes = source.types;
for (var i = 0, len = sourceTypes.length; i < len; i++) {
var related = isRelatedTo(sourceTypes[i], target, reportErrors);
if (!related) {
return Ternary.False;
}
result &= related;
}
return result;
}
function typesRelatedTo(sources: Type[], targets: Type[], reportErrors: boolean): Ternary {
var result = Ternary.True;
for (var i = 0, len = sources.length; i < len; i++) {
var related = isRelatedTo(sources[i], targets[i], reportErrors);
if (!related) {
return Ternary.False;
}
result &= related;
}
return result;
}
function typeParameterRelatedTo(source: TypeParameter, target: TypeParameter, reportErrors: boolean): Ternary {
if (relation === identityRelation) {
if (source.symbol.name !== target.symbol.name) {
return Ternary.False;
}
// covers case when both type parameters does not have constraint (both equal to noConstraintType)
if (source.constraint === target.constraint) {
return Ternary.True;
}
if (source.constraint === noConstraintType || target.constraint === noConstraintType) {
return Ternary.False;
}
return isRelatedTo(source.constraint, target.constraint, reportErrors);
}
else {
while (true) {
var constraint = getConstraintOfTypeParameter(source);
if (constraint === target) return Ternary.True;
if (!(constraint && constraint.flags & TypeFlags.TypeParameter)) break;
source = constraint;
}
return Ternary.False;
}
}
// Determine if two object types are related by structure. First, check if the result is already available in the global cache.
// Second, check if we have already started a comparison of the given two types in which case we assume the result to be true.
// Third, check if both types are part of deeply nested chains of generic type instantiations and if so assume the types are
// equal and infinitely expanding. Fourth, if we have reached a depth of 100 nested comparisons, assume we have runaway recursion
// and issue an error. Otherwise, actually compare the structure of the two types.
function objectTypeRelatedTo(source: ObjectType, target: ObjectType, reportErrors: boolean, elaborateErrors = false): Ternary {
if (overflow) {
return Ternary.False;
}
var id = relation !== identityRelation || source.id < target.id ? source.id + "," + target.id : target.id + "," + source.id;
var related = relation[id];
//var related: RelationComparisonResult = undefined; // relation[id];
if (related !== undefined) {
// If we computed this relation already and it was failed and reported, or if we're not being asked to elaborate
// errors, we can use the cached value. Otherwise, recompute the relation
if (!elaborateErrors || (related === RelationComparisonResult.FailedAndReported)) {
return related === RelationComparisonResult.Succeeded ? Ternary.True : Ternary.False;
}
}
if (depth > 0) {
for (var i = 0; i < depth; i++) {
// If source and target are already being compared, consider them related with assumptions
if (maybeStack[i][id]) {
return Ternary.Maybe;
}
}
if (depth === 100) {
overflow = true;
return Ternary.False;
}
}
else {
sourceStack = [];
targetStack = [];
maybeStack = [];
expandingFlags = 0;
}
sourceStack[depth] = source;
targetStack[depth] = target;
maybeStack[depth] = {};
maybeStack[depth][id] = RelationComparisonResult.Succeeded;
depth++;
var saveExpandingFlags = expandingFlags;
if (!(expandingFlags & 1) && isDeeplyNestedGeneric(source, sourceStack)) expandingFlags |= 1;
if (!(expandingFlags & 2) && isDeeplyNestedGeneric(target, targetStack)) expandingFlags |= 2;
if (expandingFlags === 3) {
var result = Ternary.Maybe;
}
else {
var result = propertiesRelatedTo(source, target, reportErrors);
if (result) {
result &= signaturesRelatedTo(source, target, SignatureKind.Call, reportErrors);
if (result) {
result &= signaturesRelatedTo(source, target, SignatureKind.Construct, reportErrors);
if (result) {
result &= stringIndexTypesRelatedTo(source, target, reportErrors);
if (result) {
result &= numberIndexTypesRelatedTo(source, target, reportErrors);
}
}
}
}
}
expandingFlags = saveExpandingFlags;
depth--;
if (result) {
var maybeCache = maybeStack[depth];
// If result is definitely true, copy assumptions to global cache, else copy to next level up
var destinationCache = (result === Ternary.True || depth === 0) ? relation : maybeStack[depth - 1];
copyMap(maybeCache, destinationCache);
}
else {
// A false result goes straight into global cache (when something is false under assumptions it
// will also be false without assumptions)
relation[id] = reportErrors ? RelationComparisonResult.FailedAndReported : RelationComparisonResult.Failed;
}
return result;
}
// Return true if the given type is part of a deeply nested chain of generic instantiations. We consider this to be the case
// when structural type comparisons have been started for 10 or more instantiations of the same generic type. It is possible,
// though highly unlikely, for this test to be true in a situation where a chain of instantiations is not infinitely expanding.
// Effectively, we will generate a false positive when two types are structurally equal to at least 10 levels, but unequal at
// some level beyond that.
function isDeeplyNestedGeneric(type: ObjectType, stack: ObjectType[]): boolean {
if (type.flags & TypeFlags.Reference && depth >= 10) {
var target = (type).target;
var count = 0;
for (var i = 0; i < depth; i++) {
var t = stack[i];
if (t.flags & TypeFlags.Reference && (t).target === target) {
count++;
if (count >= 10) return true;
}
}
}
return false;
}
function propertiesRelatedTo(source: ObjectType, target: ObjectType, reportErrors: boolean): Ternary {
if (relation === identityRelation) {
return propertiesIdenticalTo(source, target);
}
var result = Ternary.True;
var properties = getPropertiesOfObjectType(target);
var requireOptionalProperties = relation === subtypeRelation && !(source.flags & TypeFlags.ObjectLiteral);
for (var i = 0; i < properties.length; i++) {
var targetProp = properties[i];
var sourceProp = getPropertyOfType(source, targetProp.name);
if (sourceProp !== targetProp) {
if (!sourceProp) {
if (!(targetProp.flags & SymbolFlags.Optional) || requireOptionalProperties) {
if (reportErrors) {
reportError(Diagnostics.Property_0_is_missing_in_type_1, symbolToString(targetProp), typeToString(source));
}
return Ternary.False;
}
}
else if (!(targetProp.flags & SymbolFlags.Prototype)) {
var sourceFlags = getDeclarationFlagsFromSymbol(sourceProp);
var targetFlags = getDeclarationFlagsFromSymbol(targetProp);
if (sourceFlags & NodeFlags.Private || targetFlags & NodeFlags.Private) {
if (sourceProp.valueDeclaration !== targetProp.valueDeclaration) {
if (reportErrors) {
if (sourceFlags & NodeFlags.Private && targetFlags & NodeFlags.Private) {
reportError(Diagnostics.Types_have_separate_declarations_of_a_private_property_0, symbolToString(targetProp));
}
else {
reportError(Diagnostics.Property_0_is_private_in_type_1_but_not_in_type_2, symbolToString(targetProp),
typeToString(sourceFlags & NodeFlags.Private ? source : target),
typeToString(sourceFlags & NodeFlags.Private ? target : source));
}
}
return Ternary.False;
}
}
else if (targetFlags & NodeFlags.Protected) {
var sourceDeclaredInClass = sourceProp.parent && sourceProp.parent.flags & SymbolFlags.Class;
var sourceClass = sourceDeclaredInClass ? getDeclaredTypeOfSymbol(sourceProp.parent) : undefined;
var targetClass = getDeclaredTypeOfSymbol(targetProp.parent);
if (!sourceClass || !hasBaseType(sourceClass, targetClass)) {
if (reportErrors) {
reportError(Diagnostics.Property_0_is_protected_but_type_1_is_not_a_class_derived_from_2,
symbolToString(targetProp), typeToString(sourceClass || source), typeToString(targetClass));
}
return Ternary.False;
}
}
else if (sourceFlags & NodeFlags.Protected) {
if (reportErrors) {
reportError(Diagnostics.Property_0_is_protected_in_type_1_but_public_in_type_2,
symbolToString(targetProp), typeToString(source), typeToString(target));
}
return Ternary.False;
}
var related = isRelatedTo(getTypeOfSymbol(sourceProp), getTypeOfSymbol(targetProp), reportErrors);
if (!related) {
if (reportErrors) {
reportError(Diagnostics.Types_of_property_0_are_incompatible, symbolToString(targetProp));
}
return Ternary.False;
}
result &= related;
if (sourceProp.flags & SymbolFlags.Optional && !(targetProp.flags & SymbolFlags.Optional)) {
// TypeScript 1.0 spec (April 2014): 3.8.3
// S is a subtype of a type T, and T is a supertype of S if ...
// S' and T are object types and, for each member M in T..
// M is a property and S' contains a property N where
// if M is a required property, N is also a required property
// (M - property in T)
// (N - property in S)
if (reportErrors) {
reportError(Diagnostics.Property_0_is_optional_in_type_1_but_required_in_type_2,
symbolToString(targetProp), typeToString(source), typeToString(target));
}
return Ternary.False;
}
}
}
}
return result;
}
function propertiesIdenticalTo(source: ObjectType, target: ObjectType): Ternary {
var sourceProperties = getPropertiesOfObjectType(source);
var targetProperties = getPropertiesOfObjectType(target);
if (sourceProperties.length !== targetProperties.length) {
return Ternary.False;
}
var result = Ternary.True;
for (var i = 0, len = sourceProperties.length; i < len; ++i) {
var sourceProp = sourceProperties[i];
var targetProp = getPropertyOfObjectType(target, sourceProp.name);
if (!targetProp) {
return Ternary.False;
}
var related = compareProperties(sourceProp, targetProp, isRelatedTo);
if (!related) {
return Ternary.False;
}
result &= related;
}
return result;
}
function signaturesRelatedTo(source: ObjectType, target: ObjectType, kind: SignatureKind, reportErrors: boolean): Ternary {
if (relation === identityRelation) {
return signaturesIdenticalTo(source, target, kind);
}
if (target === anyFunctionType || source === anyFunctionType) {
return Ternary.True;
}
var sourceSignatures = getSignaturesOfType(source, kind);
var targetSignatures = getSignaturesOfType(target, kind);
var result = Ternary.True;
var saveErrorInfo = errorInfo;
outer: for (var i = 0; i < targetSignatures.length; i++) {
var t = targetSignatures[i];
if (!t.hasStringLiterals || target.flags & TypeFlags.FromSignature) {
var localErrors = reportErrors;
for (var j = 0; j < sourceSignatures.length; j++) {
var s = sourceSignatures[j];
if (!s.hasStringLiterals || source.flags & TypeFlags.FromSignature) {
var related = signatureRelatedTo(s, t, localErrors);
if (related) {
result &= related;
errorInfo = saveErrorInfo;
continue outer;
}
// Only report errors from the first failure
localErrors = false;
}
}
return Ternary.False;
}
}
return result;
}
function signatureRelatedTo(source: Signature, target: Signature, reportErrors: boolean): Ternary {
if (source === target) {
return Ternary.True;
}
if (!target.hasRestParameter && source.minArgumentCount > target.parameters.length) {
return Ternary.False;
}
var sourceMax = source.parameters.length;
var targetMax = target.parameters.length;
var checkCount: number;
if (source.hasRestParameter && target.hasRestParameter) {
checkCount = sourceMax > targetMax ? sourceMax : targetMax;
sourceMax--;
targetMax--;
}
else if (source.hasRestParameter) {
sourceMax--;
checkCount = targetMax;
}
else if (target.hasRestParameter) {
targetMax--;
checkCount = sourceMax;
}
else {
checkCount = sourceMax < targetMax ? sourceMax : targetMax;
}
// Spec 1.0 Section 3.8.3 & 3.8.4:
// M and N (the signatures) are instantiated using type Any as the type argument for all type parameters declared by M and N
source = getErasedSignature(source);
target = getErasedSignature(target);
var result = Ternary.True;
for (var i = 0; i < checkCount; i++) {
var s = i < sourceMax ? getTypeOfSymbol(source.parameters[i]) : getRestTypeOfSignature(source);
var t = i < targetMax ? getTypeOfSymbol(target.parameters[i]) : getRestTypeOfSignature(target);
var saveErrorInfo = errorInfo;
var related = isRelatedTo(s, t, reportErrors);
if (!related) {
related = isRelatedTo(t, s, false);
if (!related) {
if (reportErrors) {
reportError(Diagnostics.Types_of_parameters_0_and_1_are_incompatible,
source.parameters[i < sourceMax ? i : sourceMax].name,
target.parameters[i < targetMax ? i : targetMax].name);
}
return Ternary.False;
}
errorInfo = saveErrorInfo;
}
result &= related;
}
var t = getReturnTypeOfSignature(target);
if (t === voidType) return result;
var s = getReturnTypeOfSignature(source);
return result & isRelatedTo(s, t, reportErrors);
}
function signaturesIdenticalTo(source: ObjectType, target: ObjectType, kind: SignatureKind): Ternary {
var sourceSignatures = getSignaturesOfType(source, kind);
var targetSignatures = getSignaturesOfType(target, kind);
if (sourceSignatures.length !== targetSignatures.length) {
return Ternary.False;
}
var result = Ternary.True;
for (var i = 0, len = sourceSignatures.length; i < len; ++i) {
var related = compareSignatures(sourceSignatures[i], targetSignatures[i], /*compareReturnTypes*/ true, isRelatedTo);
if (!related) {
return Ternary.False;
}
result &= related;
}
return result;
}
function stringIndexTypesRelatedTo(source: ObjectType, target: ObjectType, reportErrors: boolean): Ternary {
if (relation === identityRelation) {
return indexTypesIdenticalTo(IndexKind.String, source, target);
}
var targetType = getIndexTypeOfType(target, IndexKind.String);
if (targetType) {
var sourceType = getIndexTypeOfType(source, IndexKind.String);
if (!sourceType) {
if (reportErrors) {
reportError(Diagnostics.Index_signature_is_missing_in_type_0, typeToString(source));
}
return Ternary.False;
}
var related = isRelatedTo(sourceType, targetType, reportErrors);
if (!related) {
if (reportErrors) {
reportError(Diagnostics.Index_signatures_are_incompatible);
}
return Ternary.False;
}
return related;
}
return Ternary.True;
}
function numberIndexTypesRelatedTo(source: ObjectType, target: ObjectType, reportErrors: boolean): Ternary {
if (relation === identityRelation) {
return indexTypesIdenticalTo(IndexKind.Number, source, target);
}
var targetType = getIndexTypeOfType(target, IndexKind.Number);
if (targetType) {
var sourceStringType = getIndexTypeOfType(source, IndexKind.String);
var sourceNumberType = getIndexTypeOfType(source, IndexKind.Number);
if (!(sourceStringType || sourceNumberType)) {
if (reportErrors) {
reportError(Diagnostics.Index_signature_is_missing_in_type_0, typeToString(source));
}
return Ternary.False;
}
if (sourceStringType && sourceNumberType) {
// If we know for sure we're testing both string and numeric index types then only report errors from the second one
var related = isRelatedTo(sourceStringType, targetType, false) || isRelatedTo(sourceNumberType, targetType, reportErrors);
}
else {
var related = isRelatedTo(sourceStringType || sourceNumberType, targetType, reportErrors);
}
if (!related) {
if (reportErrors) {
reportError(Diagnostics.Index_signatures_are_incompatible);
}
return Ternary.False;
}
return related;
}
return Ternary.True;
}
function indexTypesIdenticalTo(indexKind: IndexKind, source: ObjectType, target: ObjectType): Ternary {
var targetType = getIndexTypeOfType(target, indexKind);
var sourceType = getIndexTypeOfType(source, indexKind);
if (!sourceType && !targetType) {
return Ternary.True;
}
if (sourceType && targetType) {
return isRelatedTo(sourceType, targetType);
}
return Ternary.False;
}
}
function isPropertyIdenticalTo(sourceProp: Symbol, targetProp: Symbol): boolean {
return compareProperties(sourceProp, targetProp, compareTypes) !== Ternary.False;
}
function compareProperties(sourceProp: Symbol, targetProp: Symbol, compareTypes: (source: Type, target: Type) => Ternary): Ternary {
// Two members are considered identical when
// - they are public properties with identical names, optionality, and types,
// - they are private or protected properties originating in the same declaration and having identical types
if (sourceProp === targetProp) {
return Ternary.True;
}
var sourcePropAccessibility = getDeclarationFlagsFromSymbol(sourceProp) & (NodeFlags.Private | NodeFlags.Protected);
var targetPropAccessibility = getDeclarationFlagsFromSymbol(targetProp) & (NodeFlags.Private | NodeFlags.Protected);
if (sourcePropAccessibility !== targetPropAccessibility) {
return Ternary.False;
}
if (sourcePropAccessibility) {
if (getTargetSymbol(sourceProp) !== getTargetSymbol(targetProp)) {
return Ternary.False;
}
}
else {
if ((sourceProp.flags & SymbolFlags.Optional) !== (targetProp.flags & SymbolFlags.Optional)) {
return Ternary.False;
}
}
return compareTypes(getTypeOfSymbol(sourceProp), getTypeOfSymbol(targetProp));
}
function compareSignatures(source: Signature, target: Signature, compareReturnTypes: boolean, compareTypes: (s: Type, t: Type) => Ternary): Ternary {
if (source === target) {
return Ternary.True;
}
if (source.parameters.length !== target.parameters.length ||
source.minArgumentCount !== target.minArgumentCount ||
source.hasRestParameter !== target.hasRestParameter) {
return Ternary.False;
}
var result = Ternary.True;
if (source.typeParameters && target.typeParameters) {
if (source.typeParameters.length !== target.typeParameters.length) {
return Ternary.False;
}
for (var i = 0, len = source.typeParameters.length; i < len; ++i) {
var related = compareTypes(source.typeParameters[i], target.typeParameters[i]);
if (!related) {
return Ternary.False;
}
result &= related;
}
}
else if (source.typeParameters || target.typeParameters) {
return Ternary.False;
}
// Spec 1.0 Section 3.8.3 & 3.8.4:
// M and N (the signatures) are instantiated using type Any as the type argument for all type parameters declared by M and N
source = getErasedSignature(source);
target = getErasedSignature(target);
for (var i = 0, len = source.parameters.length; i < len; i++) {
var s = source.hasRestParameter && i === len - 1 ? getRestTypeOfSignature(source) : getTypeOfSymbol(source.parameters[i]);
var t = target.hasRestParameter && i === len - 1 ? getRestTypeOfSignature(target) : getTypeOfSymbol(target.parameters[i]);
var related = compareTypes(s, t);
if (!related) {
return Ternary.False;
}
result &= related;
}
if (compareReturnTypes) {
result &= compareTypes(getReturnTypeOfSignature(source), getReturnTypeOfSignature(target));
}
return result;
}
function isSupertypeOfEach(candidate: Type, types: Type[]): boolean {
for (var i = 0, len = types.length; i < len; i++) {
if (candidate !== types[i] && !isTypeSubtypeOf(types[i], candidate)) return false;
}
return true;
}
function getCommonSupertype(types: Type[]): Type {
return forEach(types, t => isSupertypeOfEach(t, types) ? t : undefined);
}
function reportNoCommonSupertypeError(types: Type[], errorLocation: Node, errorMessageChainHead: DiagnosticMessageChain): void {
var bestSupertype: Type;
var bestSupertypeDownfallType: Type; // The type that caused bestSupertype not to be the common supertype
var bestSupertypeScore = 0;
for (var i = 0; i < types.length; i++) {
var score = 0;
var downfallType: Type = undefined;
for (var j = 0; j < types.length; j++) {
if (isTypeSubtypeOf(types[j], types[i])) {
score++;
}
else if (!downfallType) {
downfallType = types[j];
}
}
if (score > bestSupertypeScore) {
bestSupertype = types[i];
bestSupertypeDownfallType = downfallType;
bestSupertypeScore = score;
}
// types.length - 1 is the maximum score, given that getCommonSupertype returned false
if (bestSupertypeScore === types.length - 1) {
break;
}
}
// In the following errors, the {1} slot is before the {0} slot because checkTypeSubtypeOf supplies the
// subtype as the first argument to the error
checkTypeSubtypeOf(bestSupertypeDownfallType, bestSupertype, errorLocation,
Diagnostics.Type_argument_candidate_1_is_not_a_valid_type_argument_because_it_is_not_a_supertype_of_candidate_0,
errorMessageChainHead);
}
function isArrayType(type: Type): boolean {
return type.flags & TypeFlags.Reference && (type).target === globalArrayType;
}
function isTupleLikeType(type: Type): boolean {
return !!getPropertyOfType(type, "0");
}
function getWidenedTypeOfObjectLiteral(type: Type): Type {
var properties = getPropertiesOfObjectType(type);
var members: SymbolTable = {};
forEach(properties, p => {
var propType = getTypeOfSymbol(p);
var widenedType = getWidenedType(propType);
if (propType !== widenedType) {
var symbol = createSymbol(p.flags | SymbolFlags.Transient, p.name);
symbol.declarations = p.declarations;
symbol.parent = p.parent;
symbol.type = widenedType;
symbol.target = p;
if (p.valueDeclaration) symbol.valueDeclaration = p.valueDeclaration;
p = symbol;
}
members[p.name] = p;
});
var stringIndexType = getIndexTypeOfType(type, IndexKind.String);
var numberIndexType = getIndexTypeOfType(type, IndexKind.Number);
if (stringIndexType) stringIndexType = getWidenedType(stringIndexType);
if (numberIndexType) numberIndexType = getWidenedType(numberIndexType);
return createAnonymousType(type.symbol, members, emptyArray, emptyArray, stringIndexType, numberIndexType);
}
function getWidenedType(type: Type): Type {
if (type.flags & TypeFlags.RequiresWidening) {
if (type.flags & (TypeFlags.Undefined | TypeFlags.Null)) {
return anyType;
}
if (type.flags & TypeFlags.ObjectLiteral) {
return getWidenedTypeOfObjectLiteral(type);
}
if (type.flags & TypeFlags.Union) {
return getUnionType(map((type).types, getWidenedType));
}
if (isArrayType(type)) {
return createArrayType(getWidenedType((type).typeArguments[0]));
}
}
return type;
}
function reportWideningErrorsInType(type: Type): boolean {
if (type.flags & TypeFlags.Union) {
var errorReported = false;
forEach((type).types, t => {
if (reportWideningErrorsInType(t)) {
errorReported = true;
}
});
return errorReported;
}
if (isArrayType(type)) {
return reportWideningErrorsInType((type).typeArguments[0]);
}
if (type.flags & TypeFlags.ObjectLiteral) {
var errorReported = false;
forEach(getPropertiesOfObjectType(type), p => {
var t = getTypeOfSymbol(p);
if (t.flags & TypeFlags.ContainsUndefinedOrNull) {
if (!reportWideningErrorsInType(t)) {
error(p.valueDeclaration, Diagnostics.Object_literal_s_property_0_implicitly_has_an_1_type, p.name, typeToString(getWidenedType(t)));
}
errorReported = true;
}
});
return errorReported;
}
return false;
}
function reportImplicitAnyError(declaration: Declaration, type: Type) {
var typeAsString = typeToString(getWidenedType(type));
switch (declaration.kind) {
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.PropertySignature:
var diagnostic = Diagnostics.Member_0_implicitly_has_an_1_type;
break;
case SyntaxKind.Parameter:
var diagnostic = (declaration).dotDotDotToken ?
Diagnostics.Rest_parameter_0_implicitly_has_an_any_type :
Diagnostics.Parameter_0_implicitly_has_an_1_type;
break;
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
if (!declaration.name) {
error(declaration, Diagnostics.Function_expression_which_lacks_return_type_annotation_implicitly_has_an_0_return_type, typeAsString);
return;
}
var diagnostic = Diagnostics._0_which_lacks_return_type_annotation_implicitly_has_an_1_return_type;
break;
default:
var diagnostic = Diagnostics.Variable_0_implicitly_has_an_1_type;
}
error(declaration, diagnostic, declarationNameToString(declaration.name), typeAsString);
}
function reportErrorsFromWidening(declaration: Declaration, type: Type) {
if (produceDiagnostics && compilerOptions.noImplicitAny && type.flags & TypeFlags.ContainsUndefinedOrNull) {
// Report implicit any error within type if possible, otherwise report error on declaration
if (!reportWideningErrorsInType(type)) {
reportImplicitAnyError(declaration, type);
}
}
}
function forEachMatchingParameterType(source: Signature, target: Signature, callback: (s: Type, t: Type) => void) {
var sourceMax = source.parameters.length;
var targetMax = target.parameters.length;
var count: number;
if (source.hasRestParameter && target.hasRestParameter) {
count = sourceMax > targetMax ? sourceMax : targetMax;
sourceMax--;
targetMax--;
}
else if (source.hasRestParameter) {
sourceMax--;
count = targetMax;
}
else if (target.hasRestParameter) {
targetMax--;
count = sourceMax;
}
else {
count = sourceMax < targetMax ? sourceMax : targetMax;
}
for (var i = 0; i < count; i++) {
var s = i < sourceMax ? getTypeOfSymbol(source.parameters[i]) : getRestTypeOfSignature(source);
var t = i < targetMax ? getTypeOfSymbol(target.parameters[i]) : getRestTypeOfSignature(target);
callback(s, t);
}
}
function createInferenceContext(typeParameters: TypeParameter[], inferUnionTypes: boolean): InferenceContext {
var inferences: TypeInferences[] = [];
for (var i = 0; i < typeParameters.length; i++) {
inferences.push({ primary: undefined, secondary: undefined });
}
return {
typeParameters: typeParameters,
inferUnionTypes: inferUnionTypes,
inferenceCount: 0,
inferences: inferences,
inferredTypes: new Array(typeParameters.length),
};
}
function inferTypes(context: InferenceContext, source: Type, target: Type) {
var sourceStack: Type[];
var targetStack: Type[];
var depth = 0;
var inferiority = 0;
inferFromTypes(source, target);
function isInProcess(source: Type, target: Type) {
for (var i = 0; i < depth; i++) {
if (source === sourceStack[i] && target === targetStack[i]) return true;
}
return false;
}
function isWithinDepthLimit(type: Type, stack: Type[]) {
if (depth >= 5) {
var target = (type).target;
var count = 0;
for (var i = 0; i < depth; i++) {
var t = stack[i];
if (t.flags & TypeFlags.Reference && (t).target === target) count++;
}
return count < 5;
}
return true;
}
function inferFromTypes(source: Type, target: Type) {
if (source === anyFunctionType) {
return;
}
if (target.flags & TypeFlags.TypeParameter) {
// If target is a type parameter, make an inference
var typeParameters = context.typeParameters;
for (var i = 0; i < typeParameters.length; i++) {
if (target === typeParameters[i]) {
var inferences = context.inferences[i];
var candidates = inferiority ?
inferences.secondary || (inferences.secondary = []) :
inferences.primary || (inferences.primary = []);
if (!contains(candidates, source)) candidates.push(source);
break;
}
}
}
else if (source.flags & TypeFlags.Reference && target.flags & TypeFlags.Reference && (source).target === (target).target) {
// If source and target are references to the same generic type, infer from type arguments
var sourceTypes = (source).typeArguments;
var targetTypes = (target).typeArguments;
for (var i = 0; i < sourceTypes.length; i++) {
inferFromTypes(sourceTypes[i], targetTypes[i]);
}
}
else if (target.flags & TypeFlags.Union) {
var targetTypes = (target).types;
var typeParameterCount = 0;
var typeParameter: TypeParameter;
// First infer to each type in union that isn't a type parameter
for (var i = 0; i < targetTypes.length; i++) {
var t = targetTypes[i];
if (t.flags & TypeFlags.TypeParameter && contains(context.typeParameters, t)) {
typeParameter = t;
typeParameterCount++;
}
else {
inferFromTypes(source, t);
}
}
// If union contains a single naked type parameter, make a secondary inference to that type parameter
if (typeParameterCount === 1) {
inferiority++;
inferFromTypes(source, typeParameter);
inferiority--;
}
}
else if (source.flags & TypeFlags.Union) {
// Source is a union type, infer from each consituent type
var sourceTypes = (source).types;
for (var i = 0; i < sourceTypes.length; i++) {
inferFromTypes(sourceTypes[i], target);
}
}
else if (source.flags & TypeFlags.ObjectType && (target.flags & (TypeFlags.Reference | TypeFlags.Tuple) ||
(target.flags & TypeFlags.Anonymous) && target.symbol && target.symbol.flags & (SymbolFlags.Method | SymbolFlags.TypeLiteral))) {
// If source is an object type, and target is a type reference, a tuple type, the type of a method, or a type literal, infer from members
if (!isInProcess(source, target) && isWithinDepthLimit(source, sourceStack) && isWithinDepthLimit(target, targetStack)) {
if (depth === 0) {
sourceStack = [];
targetStack = [];
}
sourceStack[depth] = source;
targetStack[depth] = target;
depth++;
inferFromProperties(source, target);
inferFromSignatures(source, target, SignatureKind.Call);
inferFromSignatures(source, target, SignatureKind.Construct);
inferFromIndexTypes(source, target, IndexKind.String, IndexKind.String);
inferFromIndexTypes(source, target, IndexKind.Number, IndexKind.Number);
inferFromIndexTypes(source, target, IndexKind.String, IndexKind.Number);
depth--;
}
}
}
function inferFromProperties(source: Type, target: Type) {
var properties = getPropertiesOfObjectType(target);
for (var i = 0; i < properties.length; i++) {
var targetProp = properties[i];
var sourceProp = getPropertyOfObjectType(source, targetProp.name);
if (sourceProp) {
inferFromTypes(getTypeOfSymbol(sourceProp), getTypeOfSymbol(targetProp));
}
}
}
function inferFromSignatures(source: Type, target: Type, kind: SignatureKind) {
var sourceSignatures = getSignaturesOfType(source, kind);
var targetSignatures = getSignaturesOfType(target, kind);
var sourceLen = sourceSignatures.length;
var targetLen = targetSignatures.length;
var len = sourceLen < targetLen ? sourceLen : targetLen;
for (var i = 0; i < len; i++) {
inferFromSignature(getErasedSignature(sourceSignatures[sourceLen - len + i]), getErasedSignature(targetSignatures[targetLen - len + i]));
}
}
function inferFromSignature(source: Signature, target: Signature) {
forEachMatchingParameterType(source, target, inferFromTypes);
inferFromTypes(getReturnTypeOfSignature(source), getReturnTypeOfSignature(target));
}
function inferFromIndexTypes(source: Type, target: Type, sourceKind: IndexKind, targetKind: IndexKind) {
var targetIndexType = getIndexTypeOfType(target, targetKind);
if (targetIndexType) {
var sourceIndexType = getIndexTypeOfType(source, sourceKind);
if (sourceIndexType) {
inferFromTypes(sourceIndexType, targetIndexType);
}
}
}
}
function getInferenceCandidates(context: InferenceContext, index: number): Type[] {
var inferences = context.inferences[index];
return inferences.primary || inferences.secondary || emptyArray;
}
function getInferredType(context: InferenceContext, index: number): Type {
var inferredType = context.inferredTypes[index];
if (!inferredType) {
var inferences = getInferenceCandidates(context, index);
if (inferences.length) {
// Infer widened union or supertype, or the undefined type for no common supertype
var unionOrSuperType = context.inferUnionTypes ? getUnionType(inferences) : getCommonSupertype(inferences);
inferredType = unionOrSuperType ? getWidenedType(unionOrSuperType) : inferenceFailureType;
}
else {
// Infer the empty object type when no inferences were made
inferredType = emptyObjectType;
}
if (inferredType !== inferenceFailureType) {
var constraint = getConstraintOfTypeParameter(context.typeParameters[index]);
inferredType = constraint && !isTypeAssignableTo(inferredType, constraint) ? constraint : inferredType;
}
context.inferredTypes[index] = inferredType;
}
return inferredType;
}
function getInferredTypes(context: InferenceContext): Type[] {
for (var i = 0; i < context.inferredTypes.length; i++) {
getInferredType(context, i);
}
return context.inferredTypes;
}
function hasAncestor(node: Node, kind: SyntaxKind): boolean {
return getAncestor(node, kind) !== undefined;
}
// EXPRESSION TYPE CHECKING
function getResolvedSymbol(node: Identifier): Symbol {
var links = getNodeLinks(node);
if (!links.resolvedSymbol) {
links.resolvedSymbol = (getFullWidth(node) > 0 && resolveName(node, node.text, SymbolFlags.Value | SymbolFlags.ExportValue, Diagnostics.Cannot_find_name_0, node)) || unknownSymbol;
}
return links.resolvedSymbol;
}
function isInTypeQuery(node: Node): boolean {
// TypeScript 1.0 spec (April 2014): 3.6.3
// A type query consists of the keyword typeof followed by an expression.
// The expression is restricted to a single identifier or a sequence of identifiers separated by periods
while (node) {
switch (node.kind) {
case SyntaxKind.TypeQuery:
return true;
case SyntaxKind.Identifier:
case SyntaxKind.QualifiedName:
node = node.parent;
continue;
default:
return false;
}
}
Debug.fail("should not get here");
}
// For a union type, remove all constituent types that are of the given type kind (when isOfTypeKind is true)
// or not of the given type kind (when isOfTypeKind is false)
function removeTypesFromUnionType(type: Type, typeKind: TypeFlags, isOfTypeKind: boolean): Type {
if (type.flags & TypeFlags.Union) {
var types = (type).types;
if (forEach(types, t => !!(t.flags & typeKind) === isOfTypeKind)) {
// Above we checked if we have anything to remove, now use the opposite test to do the removal
var narrowedType = getUnionType(filter(types, t => !(t.flags & typeKind) === isOfTypeKind));
if (narrowedType !== emptyObjectType) {
return narrowedType;
}
}
}
return type;
}
function hasInitializer(node: VariableLikeDeclaration): boolean {
return !!(node.initializer || isBindingPattern(node.parent) && hasInitializer(node.parent.parent));
}
// Check if a given variable is assigned within a given syntax node
function isVariableAssignedWithin(symbol: Symbol, node: Node): boolean {
var links = getNodeLinks(node);
if (links.assignmentChecks) {
var cachedResult = links.assignmentChecks[symbol.id];
if (cachedResult !== undefined) {
return cachedResult;
}
}
else {
links.assignmentChecks = {};
}
return links.assignmentChecks[symbol.id] = isAssignedIn(node);
function isAssignedInBinaryExpression(node: BinaryExpression) {
if (node.operator >= SyntaxKind.FirstAssignment && node.operator <= SyntaxKind.LastAssignment) {
var n = node.left;
while (n.kind === SyntaxKind.ParenthesizedExpression) {
n = (n).expression;
}
if (n.kind === SyntaxKind.Identifier && getResolvedSymbol(n) === symbol) {
return true;
}
}
return forEachChild(node, isAssignedIn);
}
function isAssignedInVariableDeclaration(node: VariableLikeDeclaration) {
if (!isBindingPattern(node.name) && getSymbolOfNode(node) === symbol && hasInitializer(node)) {
return true;
}
return forEachChild(node, isAssignedIn);
}
function isAssignedIn(node: Node): boolean {
switch (node.kind) {
case SyntaxKind.BinaryExpression:
return isAssignedInBinaryExpression(node);
case SyntaxKind.VariableDeclaration:
case SyntaxKind.BindingElement:
return isAssignedInVariableDeclaration(node);
case SyntaxKind.ObjectBindingPattern:
case SyntaxKind.ArrayBindingPattern:
case SyntaxKind.ArrayLiteralExpression:
case SyntaxKind.ObjectLiteralExpression:
case SyntaxKind.PropertyAccessExpression:
case SyntaxKind.ElementAccessExpression:
case SyntaxKind.CallExpression:
case SyntaxKind.NewExpression:
case SyntaxKind.TypeAssertionExpression:
case SyntaxKind.ParenthesizedExpression:
case SyntaxKind.PrefixUnaryExpression:
case SyntaxKind.DeleteExpression:
case SyntaxKind.TypeOfExpression:
case SyntaxKind.VoidExpression:
case SyntaxKind.PostfixUnaryExpression:
case SyntaxKind.ConditionalExpression:
case SyntaxKind.SpreadElementExpression:
case SyntaxKind.Block:
case SyntaxKind.VariableStatement:
case SyntaxKind.ExpressionStatement:
case SyntaxKind.IfStatement:
case SyntaxKind.DoStatement:
case SyntaxKind.WhileStatement:
case SyntaxKind.ForStatement:
case SyntaxKind.ForInStatement:
case SyntaxKind.ReturnStatement:
case SyntaxKind.WithStatement:
case SyntaxKind.SwitchStatement:
case SyntaxKind.CaseClause:
case SyntaxKind.DefaultClause:
case SyntaxKind.LabeledStatement:
case SyntaxKind.ThrowStatement:
case SyntaxKind.TryStatement:
case SyntaxKind.CatchClause:
return forEachChild(node, isAssignedIn);
}
return false;
}
}
function resolveLocation(node: Node) {
// Resolve location from top down towards node if it is a context sensitive expression
// That helps in making sure not assigning types as any when resolved out of order
var containerNodes: Node[] = [];
for (var parent = node.parent; parent; parent = parent.parent) {
if ((isExpression(parent) || isObjectLiteralMethod(node)) &&
isContextSensitive(parent)) {
containerNodes.unshift(parent);
}
}
ts.forEach(containerNodes, node => { getTypeOfNode(node); });
}
function getSymbolAtLocation(node: Node): Symbol {
resolveLocation(node);
return getSymbolInfo(node);
}
function getTypeAtLocation(node: Node): Type {
resolveLocation(node);
return getTypeOfNode(node);
}
function getTypeOfSymbolAtLocation(symbol: Symbol, node: Node): Type {
resolveLocation(node);
// Get the narrowed type of symbol at given location instead of just getting
// the type of the symbol.
// eg.
// function foo(a: string | number) {
// if (typeof a === "string") {
// a/**/
// }
// }
// getTypeOfSymbol for a would return type of parameter symbol string | number
// Unless we provide location /**/, checker wouldn't know how to narrow the type
// By using getNarrowedTypeOfSymbol would return string since it would be able to narrow
// it by typeguard in the if true condition
return getNarrowedTypeOfSymbol(symbol, node);
}
// Get the narrowed type of a given symbol at a given location
function getNarrowedTypeOfSymbol(symbol: Symbol, node: Node) {
var type = getTypeOfSymbol(symbol);
// Only narrow when symbol is variable of type any or an object, union, or type parameter type
if (node && symbol.flags & SymbolFlags.Variable && type.flags & (TypeFlags.Any | TypeFlags.ObjectType | TypeFlags.Union | TypeFlags.TypeParameter)) {
loop: while (node.parent) {
var child = node;
node = node.parent;
var narrowedType = type;
switch (node.kind) {
case SyntaxKind.IfStatement:
// In a branch of an if statement, narrow based on controlling expression
if (child !== (node).expression) {
narrowedType = narrowType(type, (node).expression, /*assumeTrue*/ child === (node).thenStatement);
}
break;
case SyntaxKind.ConditionalExpression:
// In a branch of a conditional expression, narrow based on controlling condition
if (child !== (node).condition) {
narrowedType = narrowType(type, (node).condition, /*assumeTrue*/ child === (node).whenTrue);
}
break;
case SyntaxKind.BinaryExpression:
// In the right operand of an && or ||, narrow based on left operand
if (child === (node).right) {
if ((node).operator === SyntaxKind.AmpersandAmpersandToken) {
narrowedType = narrowType(type, (node).left, /*assumeTrue*/ true);
}
else if ((node).operator === SyntaxKind.BarBarToken) {
narrowedType = narrowType(type, (node).left, /*assumeTrue*/ false);
}
}
break;
case SyntaxKind.SourceFile:
case SyntaxKind.ModuleDeclaration:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.Constructor:
// Stop at the first containing function or module declaration
break loop;
}
// Use narrowed type if construct contains no assignments to variable
if (narrowedType !== type) {
if (isVariableAssignedWithin(symbol, node)) {
break;
}
type = narrowedType;
}
}
}
return type;
function narrowTypeByEquality(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type {
// Check that we have 'typeof ' on the left and string literal on the right
if (expr.left.kind !== SyntaxKind.TypeOfExpression || expr.right.kind !== SyntaxKind.StringLiteral) {
return type;
}
var left = expr.left;
var right = expr.right;
if (left.expression.kind !== SyntaxKind.Identifier || getResolvedSymbol(left.expression) !== symbol) {
return type;
}
var typeInfo = primitiveTypeInfo[right.text];
if (expr.operator === SyntaxKind.ExclamationEqualsEqualsToken) {
assumeTrue = !assumeTrue;
}
if (assumeTrue) {
// Assumed result is true. If check was not for a primitive type, remove all primitive types
if (!typeInfo) {
return removeTypesFromUnionType(type, /*typeKind*/ TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.Boolean | TypeFlags.ESSymbol, /*isOfTypeKind*/ true);
}
// Check was for a primitive type, return that primitive type if it is a subtype
if (isTypeSubtypeOf(typeInfo.type, type)) {
return typeInfo.type;
}
// Otherwise, remove all types that aren't of the primitive type kind. This can happen when the type is
// union of enum types and other types.
return removeTypesFromUnionType(type, /*typeKind*/ typeInfo.flags, /*isOfTypeKind*/ false);
}
else {
// Assumed result is false. If check was for a primitive type, remove that primitive type
if (typeInfo) {
return removeTypesFromUnionType(type, /*typeKind*/ typeInfo.flags, /*isOfTypeKind*/ true);
}
// Otherwise we don't have enough information to do anything.
return type;
}
}
function narrowTypeByAnd(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type {
if (assumeTrue) {
// The assumed result is true, therefore we narrow assuming each operand to be true.
return narrowType(narrowType(type, expr.left, /*assumeTrue*/ true), expr.right, /*assumeTrue*/ true);
}
else {
// The assumed result is false. This means either the first operand was false, or the first operand was true
// and the second operand was false. We narrow with those assumptions and union the two resulting types.
return getUnionType([
narrowType(type, expr.left, /*assumeTrue*/ false),
narrowType(narrowType(type, expr.left, /*assumeTrue*/ true), expr.right, /*assumeTrue*/ false)
]);
}
}
function narrowTypeByOr(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type {
if (assumeTrue) {
// The assumed result is true. This means either the first operand was true, or the first operand was false
// and the second operand was true. We narrow with those assumptions and union the two resulting types.
return getUnionType([
narrowType(type, expr.left, /*assumeTrue*/ true),
narrowType(narrowType(type, expr.left, /*assumeTrue*/ false), expr.right, /*assumeTrue*/ true)
]);
}
else {
// The assumed result is false, therefore we narrow assuming each operand to be false.
return narrowType(narrowType(type, expr.left, /*assumeTrue*/ false), expr.right, /*assumeTrue*/ false);
}
}
function narrowTypeByInstanceof(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type {
// Check that type is not any, assumed result is true, and we have variable symbol on the left
if (type.flags & TypeFlags.Any || !assumeTrue || expr.left.kind !== SyntaxKind.Identifier || getResolvedSymbol(expr.left) !== symbol) {
return type;
}
// Check that right operand is a function type with a prototype property
var rightType = checkExpression(expr.right);
if (!isTypeSubtypeOf(rightType, globalFunctionType)) {
return type;
}
// Target type is type of prototype property
var prototypeProperty = getPropertyOfType(rightType, "prototype");
if (!prototypeProperty) {
return type;
}
var targetType = getTypeOfSymbol(prototypeProperty);
// Narrow to target type if it is a subtype of current type
if (isTypeSubtypeOf(targetType, type)) {
return targetType;
}
// If current type is a union type, remove all constituents that aren't subtypes of target type
if (type.flags & TypeFlags.Union) {
return getUnionType(filter((type).types, t => isTypeSubtypeOf(t, targetType)));
}
return type;
}
// Narrow the given type based on the given expression having the assumed boolean value. The returned type
// will be a subtype or the same type as the argument.
function narrowType(type: Type, expr: Expression, assumeTrue: boolean): Type {
switch (expr.kind) {
case SyntaxKind.ParenthesizedExpression:
return narrowType(type, (expr).expression, assumeTrue);
case SyntaxKind.BinaryExpression:
var operator = (expr).operator;
if (operator === SyntaxKind.EqualsEqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken) {
return narrowTypeByEquality(type, expr, assumeTrue);
}
else if (operator === SyntaxKind.AmpersandAmpersandToken) {
return narrowTypeByAnd(type, expr, assumeTrue);
}
else if (operator === SyntaxKind.BarBarToken) {
return narrowTypeByOr(type, expr, assumeTrue);
}
else if (operator === SyntaxKind.InstanceOfKeyword) {
return narrowTypeByInstanceof(type,