TypeScript/src/compiler/checker.ts
2015-05-30 17:11:38 -07:00

13502 lines
719 KiB
TypeScript

/// <reference path="binder.ts"/>
/* @internal */
module ts {
let nextSymbolId = 1;
let nextNodeId = 1;
let nextMergeId = 1;
export function getNodeId(node: Node): number {
if (!node.id) node.id = nextNodeId++;
return node.id;
}
export let checkTime = 0;
export function getSymbolId(symbol: Symbol): number {
if (!symbol.id) {
symbol.id = nextSymbolId++;
}
return symbol.id;
}
export function createTypeChecker(host: TypeCheckerHost, produceDiagnostics: boolean): TypeChecker {
let Symbol = objectAllocator.getSymbolConstructor();
let Type = objectAllocator.getTypeConstructor();
let Signature = objectAllocator.getSignatureConstructor();
let typeCount = 0;
let emptyArray: any[] = [];
let emptySymbols: SymbolTable = {};
let compilerOptions = host.getCompilerOptions();
let languageVersion = compilerOptions.target || ScriptTarget.ES3;
let emitResolver = createResolver();
let undefinedSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "undefined");
let argumentsSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "arguments");
let checker: TypeChecker = {
getNodeCount: () => sum(host.getSourceFiles(), "nodeCount"),
getIdentifierCount: () => sum(host.getSourceFiles(), "identifierCount"),
getSymbolCount: () => sum(host.getSourceFiles(), "symbolCount"),
getTypeCount: () => typeCount,
isUndefinedSymbol: symbol => symbol === undefinedSymbol,
isArgumentsSymbol: symbol => symbol === argumentsSymbol,
getDiagnostics,
getGlobalDiagnostics,
getTypeOfSymbolAtLocation,
getDeclaredTypeOfSymbol,
getPropertiesOfType,
getPropertyOfType,
getSignaturesOfType,
getIndexTypeOfType,
getReturnTypeOfSignature,
getSymbolsInScope,
getSymbolAtLocation,
getShorthandAssignmentValueSymbol,
getTypeAtLocation,
typeToString,
getSymbolDisplayBuilder,
symbolToString,
getAugmentedPropertiesOfType,
getRootSymbols,
getContextualType,
getFullyQualifiedName,
getResolvedSignature,
getConstantValue,
isValidPropertyAccess,
getSignatureFromDeclaration,
isImplementationOfOverload,
getAliasedSymbol: resolveAlias,
getEmitResolver,
getExportsOfModule: getExportsOfModuleAsArray,
};
let unknownSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "unknown");
let resolvingSymbol = createSymbol(SymbolFlags.Transient, "__resolving__");
let anyType = createIntrinsicType(TypeFlags.Any, "any");
let stringType = createIntrinsicType(TypeFlags.String, "string");
let numberType = createIntrinsicType(TypeFlags.Number, "number");
let booleanType = createIntrinsicType(TypeFlags.Boolean, "boolean");
let esSymbolType = createIntrinsicType(TypeFlags.ESSymbol, "symbol");
let voidType = createIntrinsicType(TypeFlags.Void, "void");
let undefinedType = createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsUndefinedOrNull, "undefined");
let nullType = createIntrinsicType(TypeFlags.Null | TypeFlags.ContainsUndefinedOrNull, "null");
let unknownType = createIntrinsicType(TypeFlags.Any, "unknown");
let circularType = createIntrinsicType(TypeFlags.Any, "__circular__");
let emptyObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
let emptyGenericType = <GenericType><ObjectType>createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
emptyGenericType.instantiations = {};
let anyFunctionType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
let noConstraintType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
let anySignature = createSignature(undefined, undefined, emptyArray, anyType, 0, false, false);
let unknownSignature = createSignature(undefined, undefined, emptyArray, unknownType, 0, false, false);
let globals: SymbolTable = {};
let globalESSymbolConstructorSymbol: Symbol;
let globalObjectType: ObjectType;
let globalFunctionType: ObjectType;
let globalArrayType: GenericType;
let globalStringType: ObjectType;
let globalNumberType: ObjectType;
let globalBooleanType: ObjectType;
let globalRegExpType: ObjectType;
let globalTemplateStringsArrayType: ObjectType;
let globalESSymbolType: ObjectType;
let globalIterableType: GenericType;
let globalIteratorType: GenericType;
let globalIterableIteratorType: GenericType;
let anyArrayType: Type;
let getGlobalClassDecoratorType: () => ObjectType;
let getGlobalParameterDecoratorType: () => ObjectType;
let getGlobalPropertyDecoratorType: () => ObjectType;
let getGlobalMethodDecoratorType: () => ObjectType;
let tupleTypes: Map<TupleType> = {};
let unionTypes: Map<UnionType> = {};
let stringLiteralTypes: Map<StringLiteralType> = {};
let emitExtends = false;
let emitDecorate = false;
let emitParam = false;
let resolutionTargets: Object[] = [];
let resolutionResults: boolean[] = [];
let mergedSymbols: Symbol[] = [];
let symbolLinks: SymbolLinks[] = [];
let nodeLinks: NodeLinks[] = [];
let potentialThisCollisions: Node[] = [];
let diagnostics = createDiagnosticCollection();
let primitiveTypeInfo: Map<{ type: Type; flags: TypeFlags }> = {
"string": {
type: stringType,
flags: TypeFlags.StringLike
},
"number": {
type: numberType,
flags: TypeFlags.NumberLike
},
"boolean": {
type: booleanType,
flags: TypeFlags.Boolean
},
"symbol": {
type: esSymbolType,
flags: TypeFlags.ESSymbol
}
};
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 {
let diagnostic = location
? createDiagnosticForNode(location, message, arg0, arg1, arg2)
: createCompilerDiagnostic(message, arg0, arg1, arg2);
diagnostics.add(diagnostic);
}
function createSymbol(flags: SymbolFlags, name: string): Symbol {
return new Symbol(flags, name);
}
function getExcludedSymbolFlags(flags: SymbolFlags): SymbolFlags {
let result: SymbolFlags = 0;
if (flags & SymbolFlags.BlockScopedVariable) result |= SymbolFlags.BlockScopedVariableExcludes;
if (flags & SymbolFlags.FunctionScopedVariable) result |= SymbolFlags.FunctionScopedVariableExcludes;
if (flags & SymbolFlags.Property) result |= SymbolFlags.PropertyExcludes;
if (flags & SymbolFlags.EnumMember) result |= SymbolFlags.EnumMemberExcludes;
if (flags & SymbolFlags.Function) result |= SymbolFlags.FunctionExcludes;
if (flags & SymbolFlags.Class) result |= SymbolFlags.ClassExcludes;
if (flags & SymbolFlags.Interface) result |= SymbolFlags.InterfaceExcludes;
if (flags & SymbolFlags.RegularEnum) result |= SymbolFlags.RegularEnumExcludes;
if (flags & SymbolFlags.ConstEnum) result |= SymbolFlags.ConstEnumExcludes;
if (flags & SymbolFlags.ValueModule) result |= SymbolFlags.ValueModuleExcludes;
if (flags & SymbolFlags.Method) result |= SymbolFlags.MethodExcludes;
if (flags & SymbolFlags.GetAccessor) result |= SymbolFlags.GetAccessorExcludes;
if (flags & SymbolFlags.SetAccessor) result |= SymbolFlags.SetAccessorExcludes;
if (flags & SymbolFlags.TypeParameter) result |= SymbolFlags.TypeParameterExcludes;
if (flags & SymbolFlags.TypeAlias) result |= SymbolFlags.TypeAliasExcludes;
if (flags & SymbolFlags.Alias) result |= SymbolFlags.AliasExcludes;
return result;
}
function recordMergedSymbol(target: Symbol, source: Symbol) {
if (!source.mergeId) source.mergeId = nextMergeId++;
mergedSymbols[source.mergeId] = target;
}
function cloneSymbol(symbol: Symbol): Symbol {
let result = createSymbol(symbol.flags | SymbolFlags.Merged, symbol.name);
result.declarations = symbol.declarations.slice(0);
result.parent = symbol.parent;
if (symbol.valueDeclaration) result.valueDeclaration = symbol.valueDeclaration;
if (symbol.constEnumOnlyModule) result.constEnumOnlyModule = true;
if (symbol.members) result.members = cloneSymbolTable(symbol.members);
if (symbol.exports) result.exports = cloneSymbolTable(symbol.exports);
recordMergedSymbol(result, symbol);
return result;
}
function mergeSymbol(target: Symbol, source: Symbol) {
if (!(target.flags & getExcludedSymbolFlags(source.flags))) {
if (source.flags & SymbolFlags.ValueModule && target.flags & SymbolFlags.ValueModule && target.constEnumOnlyModule && !source.constEnumOnlyModule) {
// reset flag when merging instantiated module into value module that has only const enums
target.constEnumOnlyModule = false;
}
target.flags |= source.flags;
if (!target.valueDeclaration && source.valueDeclaration) target.valueDeclaration = source.valueDeclaration;
forEach(source.declarations, node => {
target.declarations.push(node);
});
if (source.members) {
if (!target.members) target.members = {};
mergeSymbolTable(target.members, source.members);
}
if (source.exports) {
if (!target.exports) target.exports = {};
mergeSymbolTable(target.exports, source.exports);
}
recordMergedSymbol(target, source);
}
else {
let message = target.flags & SymbolFlags.BlockScopedVariable || source.flags & SymbolFlags.BlockScopedVariable
? Diagnostics.Cannot_redeclare_block_scoped_variable_0 : Diagnostics.Duplicate_identifier_0;
forEach(source.declarations, node => {
error(node.name ? node.name : node, message, symbolToString(source));
});
forEach(target.declarations, node => {
error(node.name ? node.name : node, message, symbolToString(source));
});
}
}
function cloneSymbolTable(symbolTable: SymbolTable): SymbolTable {
let result: SymbolTable = {};
for (let id in symbolTable) {
if (hasProperty(symbolTable, id)) {
result[id] = symbolTable[id];
}
}
return result;
}
function mergeSymbolTable(target: SymbolTable, source: SymbolTable) {
for (let id in source) {
if (hasProperty(source, id)) {
if (!hasProperty(target, id)) {
target[id] = source[id];
}
else {
let symbol = target[id];
if (!(symbol.flags & SymbolFlags.Merged)) {
target[id] = symbol = cloneSymbol(symbol);
}
mergeSymbol(symbol, source[id]);
}
}
}
}
function getSymbolLinks(symbol: Symbol): SymbolLinks {
if (symbol.flags & SymbolFlags.Transient) return <TransientSymbol>symbol;
var id = getSymbolId(symbol);
return symbolLinks[id] || (symbolLinks[id] = {});
}
function getNodeLinks(node: Node): NodeLinks {
let nodeId = getNodeId(node);
return nodeLinks[nodeId] || (nodeLinks[nodeId] = {});
}
function getSourceFile(node: Node): SourceFile {
return <SourceFile>getAncestor(node, SyntaxKind.SourceFile);
}
function isGlobalSourceFile(node: Node) {
return node.kind === SyntaxKind.SourceFile && !isExternalModule(<SourceFile>node);
}
function getSymbol(symbols: SymbolTable, name: string, meaning: SymbolFlags): Symbol {
if (meaning && hasProperty(symbols, name)) {
let symbol = symbols[name];
Debug.assert((symbol.flags & SymbolFlags.Instantiated) === 0, "Should never get an instantiated symbol here.");
if (symbol.flags & meaning) {
return symbol;
}
if (symbol.flags & SymbolFlags.Alias) {
let target = resolveAlias(symbol);
// Unknown symbol means an error occurred in alias resolution, treat it as positive answer to avoid cascading errors
if (target === unknownSymbol || target.flags & meaning) {
return symbol;
}
}
}
// return undefined if we can't find a symbol.
}
/** Returns true if node1 is defined before node 2**/
function isDefinedBefore(node1: Node, node2: Node): boolean {
let file1 = getSourceFileOfNode(node1);
let file2 = getSourceFileOfNode(node2);
if (file1 === file2) {
return node1.pos <= node2.pos;
}
if (!compilerOptions.out) {
return true;
}
let sourceFiles = host.getSourceFiles();
return sourceFiles.indexOf(file1) <= sourceFiles.indexOf(file2);
}
// Resolve a given name for a given meaning at a given location. An error is reported if the name was not found and
// the nameNotFoundMessage argument is not undefined. Returns the resolved symbol, or undefined if no symbol with
// the given name can be found.
function resolveName(location: Node, name: string, meaning: SymbolFlags, nameNotFoundMessage: DiagnosticMessage, nameArg: string | Identifier): Symbol {
let result: Symbol;
let lastLocation: Node;
let propertyWithInvalidInitializer: Node;
let errorLocation = location;
let grandparent: Node;
loop: while (location) {
// Locals of a source file are not in scope (because they get merged into the global symbol table)
if (location.locals && !isGlobalSourceFile(location)) {
if (result = getSymbol(location.locals, name, meaning)) {
// Type parameters of a function are in scope in the entire function declaration, including the parameter
// list and return type. However, local types are only in scope in the function body.
if (!(meaning & SymbolFlags.Type) ||
!(result.flags & (SymbolFlags.Type & ~SymbolFlags.TypeParameter)) ||
!isFunctionLike(location) ||
lastLocation === (<FunctionLikeDeclaration>location).body) {
break loop;
}
result = undefined;
}
}
switch (location.kind) {
case SyntaxKind.SourceFile:
if (!isExternalModule(<SourceFile>location)) break;
case SyntaxKind.ModuleDeclaration:
if (result = getSymbol(getSymbolOfNode(location).exports, name, meaning & SymbolFlags.ModuleMember)) {
if (result.flags & meaning || !(result.flags & SymbolFlags.Alias && getDeclarationOfAliasSymbol(result).kind === SyntaxKind.ExportSpecifier)) {
break loop;
}
result = undefined;
}
else if (location.kind === SyntaxKind.SourceFile ||
(location.kind === SyntaxKind.ModuleDeclaration && (<ModuleDeclaration>location).name.kind === SyntaxKind.StringLiteral)) {
result = getSymbolOfNode(location).exports["default"];
let localSymbol = getLocalSymbolForExportDefault(result);
if (result && localSymbol && (result.flags & meaning) && localSymbol.name === name) {
break loop;
}
result = undefined;
}
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)) {
let ctor = findConstructorDeclaration(<ClassDeclaration>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<T>() { return '' }
// class C<T> { // <-- Class's own type parameter T
// [foo<T>()]() { } // <-- Reference to T from class's own computed property
// }
//
case SyntaxKind.ComputedPropertyName:
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;
}
let functionName = (<FunctionExpression>location).name;
if (functionName && name === functionName.text) {
result = location.symbol;
break loop;
}
break;
case SyntaxKind.ClassExpression:
let className = (<ClassExpression>location).name;
if (className && name === className.text) {
result = location.symbol;
break loop;
}
break;
case SyntaxKind.Decorator:
// Decorators are resolved at the class declaration. Resolving at the parameter
// or member would result in looking up locals in the method.
//
// function y() {}
// class C {
// method(@y x, y) {} // <-- decorator y should be resolved at the class declaration, not the parameter.
// }
//
if (location.parent && location.parent.kind === SyntaxKind.Parameter) {
location = location.parent;
}
//
// function y() {}
// class C {
// @y method(x, y) {} // <-- decorator y should be resolved at the class declaration, not the method.
// }
//
if (location.parent && isClassElement(location.parent)) {
location = location.parent;
}
break;
}
lastLocation = location;
location = location.parent;
}
if (!result) {
result = getSymbol(globals, name, meaning);
}
if (!result) {
if (nameNotFoundMessage) {
error(errorLocation, nameNotFoundMessage, typeof nameArg === "string" ? nameArg : declarationNameToString(nameArg));
}
return undefined;
}
// Perform extra checks only if error reporting was requested
if (nameNotFoundMessage) {
if (propertyWithInvalidInitializer) {
// We have a match, but the reference occurred within a property initializer and the identifier also binds
// to a local variable in the constructor where the code will be emitted.
let propertyName = (<PropertyDeclaration>propertyWithInvalidInitializer).name;
error(errorLocation, Diagnostics.Initializer_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor,
declarationNameToString(propertyName), typeof nameArg === "string" ? nameArg : declarationNameToString(nameArg));
return undefined;
}
if (result.flags & SymbolFlags.BlockScopedVariable) {
checkResolvedBlockScopedVariable(result, errorLocation);
}
}
return result;
}
function checkResolvedBlockScopedVariable(result: Symbol, errorLocation: Node): void {
Debug.assert((result.flags & SymbolFlags.BlockScopedVariable) !== 0)
// Block-scoped variables cannot be used before their definition
let declaration = forEach(result.declarations, d => isBlockOrCatchScoped(d) ? d : undefined);
Debug.assert(declaration !== undefined, "Block-scoped variable declaration is undefined");
// first check if usage is lexically located after the declaration
let isUsedBeforeDeclaration = !isDefinedBefore(declaration, errorLocation);
if (!isUsedBeforeDeclaration) {
// lexical check succeeded however code still can be illegal.
// - block scoped variables cannot be used in its initializers
// let x = x; // illegal but usage is lexically after definition
// - in ForIn/ForOf statements variable cannot be contained in expression part
// for (let x in x)
// for (let x of x)
// climb up to the variable declaration skipping binding patterns
let variableDeclaration = <VariableDeclaration>getAncestor(declaration, SyntaxKind.VariableDeclaration);
let container = getEnclosingBlockScopeContainer(variableDeclaration);
if (variableDeclaration.parent.parent.kind === SyntaxKind.VariableStatement ||
variableDeclaration.parent.parent.kind === SyntaxKind.ForStatement) {
// variable statement/for statement case,
// use site should not be inside variable declaration (initializer of declaration or binding element)
isUsedBeforeDeclaration = isSameScopeDescendentOf(errorLocation, variableDeclaration, container);
}
else if (variableDeclaration.parent.parent.kind === SyntaxKind.ForOfStatement ||
variableDeclaration.parent.parent.kind === SyntaxKind.ForInStatement) {
// ForIn/ForOf case - use site should not be used in expression part
let expression = (<ForInStatement | ForOfStatement>variableDeclaration.parent.parent).expression;
isUsedBeforeDeclaration = isSameScopeDescendentOf(errorLocation, expression, container);
}
}
if (isUsedBeforeDeclaration) {
error(errorLocation, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, declarationNameToString(declaration.name));
}
}
/* Starting from 'initial' node walk up the parent chain until 'stopAt' node is reached.
* If at any point current node is equal to 'parent' node - return true.
* Return false if 'stopAt' node is reached or isFunctionLike(current) === true.
*/
function isSameScopeDescendentOf(initial: Node, parent: Node, stopAt: Node): boolean {
if (!parent) {
return false;
}
for (let current = initial; current && current !== stopAt && !isFunctionLike(current); current = current.parent) {
if (current === parent) {
return true;
}
}
return false;
}
function getAnyImportSyntax(node: Node): AnyImportSyntax {
if (isAliasSymbolDeclaration(node)) {
if (node.kind === SyntaxKind.ImportEqualsDeclaration) {
return <ImportEqualsDeclaration>node;
}
while (node && node.kind !== SyntaxKind.ImportDeclaration) {
node = node.parent;
}
return <ImportDeclaration>node;
}
}
function getDeclarationOfAliasSymbol(symbol: Symbol): Declaration {
return forEach(symbol.declarations, d => isAliasSymbolDeclaration(d) ? d : undefined);
}
function getTargetOfImportEqualsDeclaration(node: ImportEqualsDeclaration): Symbol {
if (node.moduleReference.kind === SyntaxKind.ExternalModuleReference) {
return resolveExternalModuleSymbol(resolveExternalModuleName(node, getExternalModuleImportEqualsDeclarationExpression(node)));
}
return getSymbolOfPartOfRightHandSideOfImportEquals(<EntityName>node.moduleReference, node);
}
function getTargetOfImportClause(node: ImportClause): Symbol {
let moduleSymbol = resolveExternalModuleName(node, (<ImportDeclaration>node.parent).moduleSpecifier);
if (moduleSymbol) {
let exportDefaultSymbol = resolveSymbol(moduleSymbol.exports["default"]);
if (!exportDefaultSymbol) {
error(node.name, Diagnostics.Module_0_has_no_default_export, symbolToString(moduleSymbol));
}
return exportDefaultSymbol;
}
}
function getTargetOfNamespaceImport(node: NamespaceImport): Symbol {
var moduleSpecifier = (<ImportDeclaration>node.parent.parent).moduleSpecifier;
return resolveESModuleSymbol(resolveExternalModuleName(node, moduleSpecifier), moduleSpecifier);
}
function getMemberOfModuleVariable(moduleSymbol: Symbol, name: string): Symbol {
if (moduleSymbol.flags & SymbolFlags.Variable) {
let typeAnnotation = (<VariableDeclaration>moduleSymbol.valueDeclaration).type;
if (typeAnnotation) {
return getPropertyOfType(getTypeFromTypeNode(typeAnnotation), name);
}
}
}
// This function creates a synthetic symbol that combines the value side of one symbol with the
// type/namespace side of another symbol. Consider this example:
//
// declare module graphics {
// interface Point {
// x: number;
// y: number;
// }
// }
// declare var graphics: {
// Point: new (x: number, y: number) => graphics.Point;
// }
// declare module "graphics" {
// export = graphics;
// }
//
// An 'import { Point } from "graphics"' needs to create a symbol that combines the value side 'Point'
// property with the type/namespace side interface 'Point'.
function combineValueAndTypeSymbols(valueSymbol: Symbol, typeSymbol: Symbol): Symbol {
if (valueSymbol.flags & (SymbolFlags.Type | SymbolFlags.Namespace)) {
return valueSymbol;
}
let result = createSymbol(valueSymbol.flags | typeSymbol.flags, valueSymbol.name);
result.declarations = concatenate(valueSymbol.declarations, typeSymbol.declarations);
result.parent = valueSymbol.parent || typeSymbol.parent;
if (valueSymbol.valueDeclaration) result.valueDeclaration = valueSymbol.valueDeclaration;
if (typeSymbol.members) result.members = typeSymbol.members;
if (valueSymbol.exports) result.exports = valueSymbol.exports;
return result;
}
function getExportOfModule(symbol: Symbol, name: string): Symbol {
if (symbol.flags & SymbolFlags.Module) {
let exports = getExportsOfSymbol(symbol);
if (hasProperty(exports, name)) {
return resolveSymbol(exports[name]);
}
}
}
function getPropertyOfVariable(symbol: Symbol, name: string): Symbol {
if (symbol.flags & SymbolFlags.Variable) {
var typeAnnotation = (<VariableDeclaration>symbol.valueDeclaration).type;
if (typeAnnotation) {
return resolveSymbol(getPropertyOfType(getTypeFromTypeNode(typeAnnotation), name));
}
}
}
function getExternalModuleMember(node: ImportDeclaration | ExportDeclaration, specifier: ImportOrExportSpecifier): Symbol {
let moduleSymbol = resolveExternalModuleName(node, node.moduleSpecifier);
let targetSymbol = resolveESModuleSymbol(moduleSymbol, node.moduleSpecifier);
if (targetSymbol) {
let name = specifier.propertyName || specifier.name;
if (name.text) {
let symbolFromModule = getExportOfModule(targetSymbol, name.text);
let symbolFromVariable = getPropertyOfVariable(targetSymbol, name.text);
let symbol = symbolFromModule && symbolFromVariable ?
combineValueAndTypeSymbols(symbolFromVariable, symbolFromModule) :
symbolFromModule || symbolFromVariable;
if (!symbol) {
error(name, Diagnostics.Module_0_has_no_exported_member_1, getFullyQualifiedName(moduleSymbol), declarationNameToString(name));
}
return symbol;
}
}
}
function getTargetOfImportSpecifier(node: ImportSpecifier): Symbol {
return getExternalModuleMember(<ImportDeclaration>node.parent.parent.parent, node);
}
function getTargetOfExportSpecifier(node: ExportSpecifier): Symbol {
return (<ExportDeclaration>node.parent.parent).moduleSpecifier ?
getExternalModuleMember(<ExportDeclaration>node.parent.parent, node) :
resolveEntityName(node.propertyName || node.name, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace);
}
function getTargetOfExportAssignment(node: ExportAssignment): Symbol {
return resolveEntityName(<Identifier>node.expression, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace);
}
function getTargetOfAliasDeclaration(node: Declaration): Symbol {
switch (node.kind) {
case SyntaxKind.ImportEqualsDeclaration:
return getTargetOfImportEqualsDeclaration(<ImportEqualsDeclaration>node);
case SyntaxKind.ImportClause:
return getTargetOfImportClause(<ImportClause>node);
case SyntaxKind.NamespaceImport:
return getTargetOfNamespaceImport(<NamespaceImport>node);
case SyntaxKind.ImportSpecifier:
return getTargetOfImportSpecifier(<ImportSpecifier>node);
case SyntaxKind.ExportSpecifier:
return getTargetOfExportSpecifier(<ExportSpecifier>node);
case SyntaxKind.ExportAssignment:
return getTargetOfExportAssignment(<ExportAssignment>node);
}
}
function resolveSymbol(symbol: Symbol): Symbol {
return symbol && symbol.flags & SymbolFlags.Alias && !(symbol.flags & (SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace)) ? resolveAlias(symbol) : symbol;
}
function resolveAlias(symbol: Symbol): Symbol {
Debug.assert((symbol.flags & SymbolFlags.Alias) !== 0, "Should only get Alias here.");
let links = getSymbolLinks(symbol);
if (!links.target) {
links.target = resolvingSymbol;
let node = getDeclarationOfAliasSymbol(symbol);
let target = getTargetOfAliasDeclaration(node);
if (links.target === resolvingSymbol) {
links.target = target || unknownSymbol;
}
else {
error(node, Diagnostics.Circular_definition_of_import_alias_0, symbolToString(symbol));
}
}
else if (links.target === resolvingSymbol) {
links.target = unknownSymbol;
}
return links.target;
}
function markExportAsReferenced(node: ImportEqualsDeclaration | ExportAssignment | ExportSpecifier) {
let symbol = getSymbolOfNode(node);
let target = resolveAlias(symbol);
if (target) {
let markAlias =
(target === unknownSymbol && compilerOptions.separateCompilation) ||
(target !== unknownSymbol && (target.flags & SymbolFlags.Value) && !isConstEnumOrConstEnumOnlyModule(target));
if (markAlias) {
markAliasSymbolAsReferenced(symbol);
}
}
}
// When an alias symbol is referenced, we need to mark the entity it references as referenced and in turn repeat that until
// we reach a non-alias or an exported entity (which is always considered referenced). We do this by checking the target of
// the alias as an expression (which recursively takes us back here if the target references another alias).
function markAliasSymbolAsReferenced(symbol: Symbol) {
let links = getSymbolLinks(symbol);
if (!links.referenced) {
links.referenced = true;
let node = getDeclarationOfAliasSymbol(symbol);
if (node.kind === SyntaxKind.ExportAssignment) {
// export default <symbol>
checkExpressionCached((<ExportAssignment>node).expression);
}
else if (node.kind === SyntaxKind.ExportSpecifier) {
// export { <symbol> } or export { <symbol> as foo }
checkExpressionCached((<ExportSpecifier>node).propertyName || (<ExportSpecifier>node).name);
}
else if (isInternalModuleImportEqualsDeclaration(node)) {
// import foo = <symbol>
checkExpressionCached(<Expression>(<ImportEqualsDeclaration>node).moduleReference);
}
}
}
// This function is only for imports with entity names
function getSymbolOfPartOfRightHandSideOfImportEquals(entityName: EntityName, importDeclaration?: ImportEqualsDeclaration): Symbol {
if (!importDeclaration) {
importDeclaration = <ImportEqualsDeclaration>getAncestor(entityName, SyntaxKind.ImportEqualsDeclaration);
Debug.assert(importDeclaration !== undefined);
}
// There are three things we might try to look for. In the following examples,
// the search term is enclosed in |...|:
//
// import a = |b|; // Namespace
// import a = |b.c|; // Value, type, namespace
// import a = |b.c|.d; // Namespace
if (entityName.kind === SyntaxKind.Identifier && isRightSideOfQualifiedNameOrPropertyAccess(entityName)) {
entityName = <QualifiedName>entityName.parent;
}
// Check for case 1 and 3 in the above example
if (entityName.kind === SyntaxKind.Identifier || entityName.parent.kind === SyntaxKind.QualifiedName) {
return resolveEntityName(entityName, SymbolFlags.Namespace);
}
else {
// Case 2 in above example
// entityName.kind could be a QualifiedName or a Missing identifier
Debug.assert(entityName.parent.kind === SyntaxKind.ImportEqualsDeclaration);
return resolveEntityName(entityName, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace);
}
}
function getFullyQualifiedName(symbol: Symbol): string {
return symbol.parent ? getFullyQualifiedName(symbol.parent) + "." + symbolToString(symbol) : symbolToString(symbol);
}
// Resolves a qualified name and any involved aliases
function resolveEntityName(name: EntityName | Expression, meaning: SymbolFlags): Symbol {
if (nodeIsMissing(name)) {
return undefined;
}
let symbol: Symbol;
if (name.kind === SyntaxKind.Identifier) {
let message = meaning === SymbolFlags.Namespace ? Diagnostics.Cannot_find_namespace_0 : Diagnostics.Cannot_find_name_0;
symbol = resolveName(name, (<Identifier>name).text, meaning, message, <Identifier>name);
if (!symbol) {
return undefined;
}
}
else if (name.kind === SyntaxKind.QualifiedName || name.kind === SyntaxKind.PropertyAccessExpression) {
let left = name.kind === SyntaxKind.QualifiedName ? (<QualifiedName>name).left : (<PropertyAccessExpression>name).expression;
let right = name.kind === SyntaxKind.QualifiedName ? (<QualifiedName>name).right : (<PropertyAccessExpression>name).name;
let namespace = resolveEntityName(left, SymbolFlags.Namespace);
if (!namespace || namespace === unknownSymbol || nodeIsMissing(right)) {
return undefined;
}
symbol = getSymbol(getExportsOfSymbol(namespace), right.text, meaning);
if (!symbol) {
error(right, Diagnostics.Module_0_has_no_exported_member_1, getFullyQualifiedName(namespace), declarationNameToString(right));
return undefined;
}
}
else {
Debug.fail("Unknown entity name kind.");
}
Debug.assert((symbol.flags & SymbolFlags.Instantiated) === 0, "Should never get an instantiated symbol here.");
return symbol.flags & meaning ? symbol : resolveAlias(symbol);
}
function isExternalModuleNameRelative(moduleName: string): boolean {
// TypeScript 1.0 spec (April 2014): 11.2.1
// An external module name is "relative" if the first term is "." or "..".
return moduleName.substr(0, 2) === "./" || moduleName.substr(0, 3) === "../" || moduleName.substr(0, 2) === ".\\" || moduleName.substr(0, 3) === "..\\";
}
function resolveExternalModuleName(location: Node, moduleReferenceExpression: Expression): Symbol {
if (moduleReferenceExpression.kind !== SyntaxKind.StringLiteral) {
return;
}
let moduleReferenceLiteral = <LiteralExpression>moduleReferenceExpression;
let searchPath = getDirectoryPath(getSourceFile(location).fileName);
// Module names are escaped in our symbol table. However, string literal values aren't.
// Escape the name in the "require(...)" clause to ensure we find the right symbol.
let moduleName = escapeIdentifier(moduleReferenceLiteral.text);
if (!moduleName) return;
let isRelative = isExternalModuleNameRelative(moduleName);
if (!isRelative) {
let symbol = getSymbol(globals, '"' + moduleName + '"', SymbolFlags.ValueModule);
if (symbol) {
return symbol;
}
}
let fileName: string;
let sourceFile: SourceFile;
while (true) {
fileName = normalizePath(combinePaths(searchPath, moduleName));
sourceFile = forEach(supportedExtensions, extension => host.getSourceFile(fileName + extension));
if (sourceFile || isRelative) {
break;
}
let parentPath = getDirectoryPath(searchPath);
if (parentPath === searchPath) {
break;
}
searchPath = parentPath;
}
if (sourceFile) {
if (sourceFile.symbol) {
return sourceFile.symbol;
}
error(moduleReferenceLiteral, Diagnostics.File_0_is_not_a_module, sourceFile.fileName);
return;
}
error(moduleReferenceLiteral, Diagnostics.Cannot_find_module_0, moduleName);
}
// An external module with an 'export =' declaration resolves to the target of the 'export =' declaration,
// and an external module with no 'export =' declaration resolves to the module itself.
function resolveExternalModuleSymbol(moduleSymbol: Symbol): Symbol {
return moduleSymbol && resolveSymbol(moduleSymbol.exports["export="]) || moduleSymbol;
}
// An external module with an 'export =' declaration may be referenced as an ES6 module provided the 'export ='
// references a symbol that is at least declared as a module or a variable. The target of the 'export =' may
// combine other declarations with the module or variable (e.g. a class/module, function/module, interface/variable).
function resolveESModuleSymbol(moduleSymbol: Symbol, moduleReferenceExpression: Expression): Symbol {
let symbol = resolveExternalModuleSymbol(moduleSymbol);
if (symbol && !(symbol.flags & (SymbolFlags.Module | SymbolFlags.Variable))) {
error(moduleReferenceExpression, Diagnostics.Module_0_resolves_to_a_non_module_entity_and_cannot_be_imported_using_this_construct, symbolToString(moduleSymbol));
symbol = undefined;
}
return symbol;
}
function getExportAssignmentSymbol(moduleSymbol: Symbol): Symbol {
return moduleSymbol.exports["export="];
}
function getExportsOfModuleAsArray(moduleSymbol: Symbol): Symbol[] {
return symbolsToArray(getExportsOfModule(moduleSymbol));
}
function getExportsOfSymbol(symbol: Symbol): SymbolTable {
return symbol.flags & SymbolFlags.Module ? getExportsOfModule(symbol) : symbol.exports || emptySymbols;
}
function getExportsOfModule(moduleSymbol: Symbol): SymbolTable {
let links = getSymbolLinks(moduleSymbol);
return links.resolvedExports || (links.resolvedExports = getExportsForModule(moduleSymbol));
}
function extendExportSymbols(target: SymbolTable, source: SymbolTable) {
for (let id in source) {
if (id !== "default" && !hasProperty(target, id)) {
target[id] = source[id];
}
}
}
function getExportsForModule(moduleSymbol: Symbol): SymbolTable {
let result: SymbolTable;
let visitedSymbols: Symbol[] = [];
visit(moduleSymbol);
return result || moduleSymbol.exports;
// The ES6 spec permits export * declarations in a module to circularly reference the module itself. For example,
// module 'a' can 'export * from "b"' and 'b' can 'export * from "a"' without error.
function visit(symbol: Symbol) {
if (symbol && symbol.flags & SymbolFlags.HasExports && !contains(visitedSymbols, symbol)) {
visitedSymbols.push(symbol);
if (symbol !== moduleSymbol) {
if (!result) {
result = cloneSymbolTable(moduleSymbol.exports);
}
extendExportSymbols(result, symbol.exports);
}
// All export * declarations are collected in an __export symbol by the binder
let exportStars = symbol.exports["__export"];
if (exportStars) {
for (let node of exportStars.declarations) {
visit(resolveExternalModuleName(node, (<ExportDeclaration>node).moduleSpecifier));
}
}
}
}
}
function getMergedSymbol(symbol: Symbol): Symbol {
let merged: Symbol;
return symbol && symbol.mergeId && (merged = mergedSymbols[symbol.mergeId]) ? merged : symbol;
}
function getSymbolOfNode(node: Node): Symbol {
return getMergedSymbol(node.symbol);
}
function getParentOfSymbol(symbol: Symbol): Symbol {
return getMergedSymbol(symbol.parent);
}
function getExportSymbolOfValueSymbolIfExported(symbol: Symbol): Symbol {
return symbol && (symbol.flags & SymbolFlags.ExportValue) !== 0
? getMergedSymbol(symbol.exportSymbol)
: symbol;
}
function symbolIsValue(symbol: Symbol): boolean {
// If it is an instantiated symbol, then it is a value if the symbol it is an
// instantiation of is a value.
if (symbol.flags & SymbolFlags.Instantiated) {
return symbolIsValue(getSymbolLinks(symbol).target);
}
// If the symbol has the value flag, it is trivially a value.
if (symbol.flags & SymbolFlags.Value) {
return true;
}
// If it is an alias, then it is a value if the symbol it resolves to is a value.
if (symbol.flags & SymbolFlags.Alias) {
return (resolveAlias(symbol).flags & SymbolFlags.Value) !== 0;
}
return false;
}
function findConstructorDeclaration(node: ClassDeclaration): ConstructorDeclaration {
let members = node.members;
for (let member of members) {
if (member.kind === SyntaxKind.Constructor && nodeIsPresent((<ConstructorDeclaration>member).body)) {
return <ConstructorDeclaration>member;
}
}
}
function createType(flags: TypeFlags): Type {
let result = new Type(checker, flags);
result.id = typeCount++;
return result;
}
function createIntrinsicType(kind: TypeFlags, intrinsicName: string): IntrinsicType {
let type = <IntrinsicType>createType(kind);
type.intrinsicName = intrinsicName;
return type;
}
function createObjectType(kind: TypeFlags, symbol?: Symbol): ObjectType {
let type = <ObjectType>createType(kind);
type.symbol = symbol;
return type;
}
// A reserved member name starts with two underscores, but the third character cannot be an underscore
// or the @ symbol. A third underscore indicates an escaped form of an identifer that started
// with at least two underscores. The @ character indicates that the name is denoted by a well known ES
// Symbol instance.
function isReservedMemberName(name: string) {
return name.charCodeAt(0) === CharacterCodes._ &&
name.charCodeAt(1) === CharacterCodes._ &&
name.charCodeAt(2) !== CharacterCodes._ &&
name.charCodeAt(2) !== CharacterCodes.at;
}
function getNamedMembers(members: SymbolTable): Symbol[] {
let result: Symbol[];
for (let id in members) {
if (hasProperty(members, id)) {
if (!isReservedMemberName(id)) {
if (!result) result = [];
let symbol = members[id];
if (symbolIsValue(symbol)) {
result.push(symbol);
}
}
}
}
return result || emptyArray;
}
function setObjectTypeMembers(type: ObjectType, members: SymbolTable, callSignatures: Signature[], constructSignatures: Signature[], stringIndexType: Type, numberIndexType: Type): ResolvedType {
(<ResolvedType>type).members = members;
(<ResolvedType>type).properties = getNamedMembers(members);
(<ResolvedType>type).callSignatures = callSignatures;
(<ResolvedType>type).constructSignatures = constructSignatures;
if (stringIndexType) (<ResolvedType>type).stringIndexType = stringIndexType;
if (numberIndexType) (<ResolvedType>type).numberIndexType = numberIndexType;
return <ResolvedType>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<T>(enclosingDeclaration: Node, callback: (symbolTable: SymbolTable) => T): T {
let result: T;
for (let location = enclosingDeclaration; location; location = location.parent) {
// Locals of a source file are not in scope (because they get merged into the global symbol table)
if (location.locals && !isGlobalSourceFile(location)) {
if (result = callback(location.locals)) {
return result;
}
}
switch (location.kind) {
case SyntaxKind.SourceFile:
if (!isExternalModule(<SourceFile>location)) {
break;
}
case SyntaxKind.ModuleDeclaration:
if (result = callback(getSymbolOfNode(location).exports)) {
return result;
}
break;
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
if (result = callback(getSymbolOfNode(location).members)) {
return result;
}
break;
}
}
return callback(globals);
}
function getQualifiedLeftMeaning(rightMeaning: SymbolFlags) {
// If we are looking in value space, the parent meaning is value, other wise it is namespace
return rightMeaning === SymbolFlags.Value ? SymbolFlags.Value : SymbolFlags.Namespace;
}
function getAccessibleSymbolChain(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags, useOnlyExternalAliasing: boolean): Symbol[] {
function getAccessibleSymbolChainFromSymbolTable(symbols: SymbolTable): Symbol[] {
function canQualifySymbol(symbolFromSymbolTable: Symbol, meaning: SymbolFlags) {
// If the symbol is equivalent and doesn't need further qualification, this symbol is accessible
if (!needsQualification(symbolFromSymbolTable, enclosingDeclaration, meaning)) {
return true;
}
// If symbol needs qualification, make sure that parent is accessible, if it is then this symbol is accessible too
let accessibleParent = getAccessibleSymbolChain(symbolFromSymbolTable.parent, enclosingDeclaration, getQualifiedLeftMeaning(meaning), useOnlyExternalAliasing);
return !!accessibleParent;
}
function isAccessible(symbolFromSymbolTable: Symbol, resolvedAliasSymbol?: Symbol) {
if (symbol === (resolvedAliasSymbol || symbolFromSymbolTable)) {
// if the symbolFromSymbolTable is not external module (it could be if it was determined as ambient external module and would be in globals table)
// and if symbolfrom symbolTable or alias resolution matches the symbol,
// check the symbol can be qualified, it is only then this symbol is accessible
return !forEach(symbolFromSymbolTable.declarations, hasExternalModuleSymbol) &&
canQualifySymbol(symbolFromSymbolTable, meaning);
}
}
// If symbol is directly available by its name in the symbol table
if (isAccessible(lookUp(symbols, symbol.name))) {
return [symbol];
}
// Check if symbol is any of the alias
return forEachValue(symbols, symbolFromSymbolTable => {
if (symbolFromSymbolTable.flags & SymbolFlags.Alias && symbolFromSymbolTable.name !== "export=") {
if (!useOnlyExternalAliasing || // We can use any type of alias to get the name
// Is this external alias, then use it to name
ts.forEach(symbolFromSymbolTable.declarations, isExternalModuleImportEqualsDeclaration)) {
let resolvedImportedSymbol = resolveAlias(symbolFromSymbolTable);
if (isAccessible(symbolFromSymbolTable, resolveAlias(symbolFromSymbolTable))) {
return [symbolFromSymbolTable];
}
// Look in the exported members, if we can find accessibleSymbolChain, symbol is accessible using this chain
// but only if the symbolFromSymbolTable can be qualified
let accessibleSymbolsFromExports = resolvedImportedSymbol.exports ? getAccessibleSymbolChainFromSymbolTable(resolvedImportedSymbol.exports) : undefined;
if (accessibleSymbolsFromExports && canQualifySymbol(symbolFromSymbolTable, getQualifiedLeftMeaning(meaning))) {
return [symbolFromSymbolTable].concat(accessibleSymbolsFromExports);
}
}
}
});
}
if (symbol) {
return forEachSymbolTableInScope(enclosingDeclaration, getAccessibleSymbolChainFromSymbolTable);
}
}
function needsQualification(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags) {
let qualify = false;
forEachSymbolTableInScope(enclosingDeclaration, symbolTable => {
// If symbol of this name is not available in the symbol table we are ok
if (!hasProperty(symbolTable, symbol.name)) {
// Continue to the next symbol table
return false;
}
// If the symbol with this name is present it should refer to the symbol
let symbolFromSymbolTable = symbolTable[symbol.name];
if (symbolFromSymbolTable === symbol) {
// No need to qualify
return true;
}
// Qualify if the symbol from symbol table has same meaning as expected
symbolFromSymbolTable = (symbolFromSymbolTable.flags & SymbolFlags.Alias) ? resolveAlias(symbolFromSymbolTable) : symbolFromSymbolTable;
if (symbolFromSymbolTable.flags & meaning) {
qualify = true;
return true;
}
// Continue to the next symbol table
return false;
});
return qualify;
}
function isSymbolAccessible(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags): SymbolAccessiblityResult {
if (symbol && enclosingDeclaration && !(symbol.flags & SymbolFlags.TypeParameter)) {
let initialSymbol = symbol;
let meaningToLook = meaning;
while (symbol) {
// Symbol is accessible if it by itself is accessible
let accessibleSymbolChain = getAccessibleSymbolChain(symbol, enclosingDeclaration, meaningToLook, /*useOnlyExternalAliasing*/ false);
if (accessibleSymbolChain) {
let hasAccessibleDeclarations = hasVisibleDeclarations(accessibleSymbolChain[0]);
if (!hasAccessibleDeclarations) {
return <SymbolAccessiblityResult>{
accessibility: SymbolAccessibility.NotAccessible,
errorSymbolName: symbolToString(initialSymbol, enclosingDeclaration, meaning),
errorModuleName: symbol !== initialSymbol ? symbolToString(symbol, enclosingDeclaration, SymbolFlags.Namespace) : undefined,
};
}
return hasAccessibleDeclarations;
}
// If we haven't got the accessible symbol, it doesn't mean the symbol is actually inaccessible.
// It could be a qualified symbol and hence verify the path
// e.g.:
// module m {
// export class c {
// }
// }
// let x: typeof m.c
// In the above example when we start with checking if typeof m.c symbol is accessible,
// we are going to see if c can be accessed in scope directly.
// But it can't, hence the accessible is going to be undefined, but that doesn't mean m.c is inaccessible
// It is accessible if the parent m is accessible because then m.c can be accessed through qualification
meaningToLook = getQualifiedLeftMeaning(meaning);
symbol = getParentOfSymbol(symbol);
}
// This could be a symbol that is not exported in the external module
// or it could be a symbol from different external module that is not aliased and hence cannot be named
let symbolExternalModule = forEach(initialSymbol.declarations, getExternalModuleContainer);
if (symbolExternalModule) {
let enclosingExternalModule = getExternalModuleContainer(enclosingDeclaration);
if (symbolExternalModule !== enclosingExternalModule) {
// name from different external module that is not visible
return {
accessibility: SymbolAccessibility.CannotBeNamed,
errorSymbolName: symbolToString(initialSymbol, enclosingDeclaration, meaning),
errorModuleName: symbolToString(symbolExternalModule)
};
}
}
// Just a local name that is not accessible
return {
accessibility: SymbolAccessibility.NotAccessible,
errorSymbolName: symbolToString(initialSymbol, enclosingDeclaration, meaning),
};
}
return { accessibility: SymbolAccessibility.Accessible };
function getExternalModuleContainer(declaration: Node) {
for (; declaration; declaration = declaration.parent) {
if (hasExternalModuleSymbol(declaration)) {
return getSymbolOfNode(declaration);
}
}
}
}
function hasExternalModuleSymbol(declaration: Node) {
return (declaration.kind === SyntaxKind.ModuleDeclaration && (<ModuleDeclaration>declaration).name.kind === SyntaxKind.StringLiteral) ||
(declaration.kind === SyntaxKind.SourceFile && isExternalModule(<SourceFile>declaration));
}
function hasVisibleDeclarations(symbol: Symbol): SymbolVisibilityResult {
let aliasesToMakeVisible: AnyImportSyntax[];
if (forEach(symbol.declarations, declaration => !getIsDeclarationVisible(declaration))) {
return undefined;
}
return { accessibility: SymbolAccessibility.Accessible, aliasesToMakeVisible };
function getIsDeclarationVisible(declaration: Declaration) {
if (!isDeclarationVisible(declaration)) {
// Mark the unexported alias as visible if its parent is visible
// because these kind of aliases can be used to name types in declaration file
var anyImportSyntax = getAnyImportSyntax(declaration);
if (anyImportSyntax &&
!(anyImportSyntax.flags & NodeFlags.Export) && // import clause without export
isDeclarationVisible(<Declaration>anyImportSyntax.parent)) {
getNodeLinks(declaration).isVisible = true;
if (aliasesToMakeVisible) {
if (!contains(aliasesToMakeVisible, anyImportSyntax)) {
aliasesToMakeVisible.push(anyImportSyntax);
}
}
else {
aliasesToMakeVisible = [anyImportSyntax];
}
return true;
}
// Declaration is not visible
return false;
}
return true;
}
}
function isEntityNameVisible(entityName: EntityName | Expression, enclosingDeclaration: Node): SymbolVisibilityResult {
// get symbol of the first identifier of the entityName
let meaning: SymbolFlags;
if (entityName.parent.kind === SyntaxKind.TypeQuery) {
// Typeof value
meaning = SymbolFlags.Value | SymbolFlags.ExportValue;
}
else if (entityName.kind === SyntaxKind.QualifiedName || entityName.kind === SyntaxKind.PropertyAccessExpression ||
entityName.parent.kind === SyntaxKind.ImportEqualsDeclaration) {
// Left identifier from type reference or TypeAlias
// Entity name of the import declaration
meaning = SymbolFlags.Namespace;
}
else {
// Type Reference or TypeAlias entity = Identifier
meaning = SymbolFlags.Type;
}
let firstIdentifier = getFirstIdentifier(entityName);
let symbol = resolveName(enclosingDeclaration, (<Identifier>firstIdentifier).text, meaning, /*nodeNotFoundErrorMessage*/ undefined, /*nameArg*/ undefined);
// Verify if the symbol is accessible
return (symbol && hasVisibleDeclarations(symbol)) || <SymbolVisibilityResult>{
accessibility: SymbolAccessibility.NotAccessible,
errorSymbolName: getTextOfNode(firstIdentifier),
errorNode: firstIdentifier
};
}
function writeKeyword(writer: SymbolWriter, kind: SyntaxKind) {
writer.writeKeyword(tokenToString(kind));
}
function writePunctuation(writer: SymbolWriter, kind: SyntaxKind) {
writer.writePunctuation(tokenToString(kind));
}
function writeSpace(writer: SymbolWriter) {
writer.writeSpace(" ");
}
function symbolToString(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): string {
let writer = getSingleLineStringWriter();
getSymbolDisplayBuilder().buildSymbolDisplay(symbol, writer, enclosingDeclaration, meaning);
let result = writer.string();
releaseStringWriter(writer);
return result;
}
function typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string {
let writer = getSingleLineStringWriter();
getSymbolDisplayBuilder().buildTypeDisplay(type, writer, enclosingDeclaration, flags);
let result = writer.string();
releaseStringWriter(writer);
let maxLength = compilerOptions.noErrorTruncation || flags & TypeFormatFlags.NoTruncation ? undefined : 100;
if (maxLength && result.length >= maxLength) {
result = result.substr(0, maxLength - "...".length) + "...";
}
return result;
}
function getTypeAliasForTypeLiteral(type: Type): Symbol {
if (type.symbol && type.symbol.flags & SymbolFlags.TypeLiteral) {
let node = type.symbol.declarations[0].parent;
while (node.kind === SyntaxKind.ParenthesizedType) {
node = node.parent;
}
if (node.kind === SyntaxKind.TypeAliasDeclaration) {
return getSymbolOfNode(node);
}
}
return undefined;
}
// This is for caching the result of getSymbolDisplayBuilder. Do not access directly.
let _displayBuilder: SymbolDisplayBuilder;
function getSymbolDisplayBuilder(): SymbolDisplayBuilder {
/**
* 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) {
let 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 {
let parentSymbol: Symbol;
function appendParentTypeArgumentsAndSymbolName(symbol: Symbol): void {
if (parentSymbol) {
// Write type arguments of instantiated class/interface here
if (flags & SymbolFormatFlags.WriteTypeParametersOrArguments) {
if (symbol.flags & SymbolFlags.Instantiated) {
buildDisplayForTypeArgumentsAndDelimiters(getTypeParametersOfClassOrInterface(parentSymbol),
(<TransientSymbol>symbol).mapper, writer, enclosingDeclaration);
}
else {
buildTypeParameterDisplayFromSymbol(parentSymbol, writer, enclosingDeclaration);
}
}
writePunctuation(writer, SyntaxKind.DotToken);
}
parentSymbol = symbol;
appendSymbolNameOnly(symbol, writer);
}
// Let the writer know we just wrote out a symbol. The declaration emitter writer uses
// this to determine if an import it has previously seen (and not written out) needs
// to be written to the file once the walk of the tree is complete.
//
// NOTE(cyrusn): This approach feels somewhat unfortunate. A simple pass over the tree
// up front (for example, during checking) could determine if we need to emit the imports
// and we could then access that data during declaration emit.
writer.trackSymbol(symbol, enclosingDeclaration, meaning);
function walkSymbol(symbol: Symbol, meaning: SymbolFlags): void {
if (symbol) {
let accessibleSymbolChain = getAccessibleSymbolChain(symbol, enclosingDeclaration, meaning, !!(flags & SymbolFormatFlags.UseOnlyExternalAliasing));
if (!accessibleSymbolChain ||
needsQualification(accessibleSymbolChain[0], enclosingDeclaration, accessibleSymbolChain.length === 1 ? meaning : getQualifiedLeftMeaning(meaning))) {
// Go up and add our parent.
walkSymbol(
getParentOfSymbol(accessibleSymbolChain ? accessibleSymbolChain[0] : symbol),
getQualifiedLeftMeaning(meaning));
}
if (accessibleSymbolChain) {
for (let accessibleSymbol of accessibleSymbolChain) {
appendParentTypeArgumentsAndSymbolName(accessibleSymbol);
}
}
else {
// If we didn't find accessible symbol chain for this symbol, break if this is external module
if (!parentSymbol && ts.forEach(symbol.declarations, hasExternalModuleSymbol)) {
return;
}
// if this is anonymous type break
if (symbol.flags & SymbolFlags.TypeLiteral || symbol.flags & SymbolFlags.ObjectLiteral) {
return;
}
appendParentTypeArgumentsAndSymbolName(symbol);
}
}
}
// Get qualified name if the symbol is not a type parameter
// and there is an enclosing declaration or we specifically
// asked for it
let isTypeParameter = symbol.flags & SymbolFlags.TypeParameter;
let typeFormatFlag = TypeFormatFlags.UseFullyQualifiedType & typeFlags;
if (!isTypeParameter && (enclosingDeclaration || typeFormatFlag)) {
walkSymbol(symbol, meaning);
return;
}
return appendParentTypeArgumentsAndSymbolName(symbol);
}
function buildTypeDisplay(type: Type, writer: SymbolWriter, enclosingDeclaration?: Node, globalFlags?: TypeFormatFlags, typeStack?: Type[]) {
let globalFlagsToPass = globalFlags & TypeFormatFlags.WriteOwnNameForAnyLike;
return writeType(type, globalFlags);
function writeType(type: Type, flags: TypeFormatFlags) {
// Write undefined/null type as any
if (type.flags & TypeFlags.Intrinsic) {
// Special handling for unknown / resolving types, they should show up as any and not unknown or __resolving
writer.writeKeyword(!(globalFlags & TypeFormatFlags.WriteOwnNameForAnyLike) &&
(type.flags & TypeFlags.Any) ? "any" : (<IntrinsicType>type).intrinsicName);
}
else if (type.flags & TypeFlags.Reference) {
writeTypeReference(<TypeReference>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(<TupleType>type);
}
else if (type.flags & TypeFlags.Union) {
writeUnionType(<UnionType>type, flags);
}
else if (type.flags & TypeFlags.Anonymous) {
writeAnonymousType(<ObjectType>type, flags);
}
else if (type.flags & TypeFlags.StringLiteral) {
writer.writeStringLiteral((<StringLiteralType>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 (let 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 writeSymbolTypeReference(symbol: Symbol, typeArguments: Type[], pos: number, end: number) {
// Unnamed function expressions, arrow functions, and unnamed class expressions have reserved names that
// we don't want to display
if (!isReservedMemberName(symbol.name)) {
buildSymbolDisplay(symbol, writer, enclosingDeclaration, SymbolFlags.Type);
}
if (pos < end) {
writePunctuation(writer, SyntaxKind.LessThanToken);
writeType(typeArguments[pos++], TypeFormatFlags.None);
while (pos < end) {
writePunctuation(writer, SyntaxKind.CommaToken);
writeSpace(writer);
writeType(typeArguments[pos++], TypeFormatFlags.None);
}
writePunctuation(writer, SyntaxKind.GreaterThanToken);
}
}
function writeTypeReference(type: TypeReference, flags: TypeFormatFlags) {
let typeArguments = type.typeArguments;
if (type.target === globalArrayType && !(flags & TypeFormatFlags.WriteArrayAsGenericType)) {
writeType(typeArguments[0], TypeFormatFlags.InElementType);
writePunctuation(writer, SyntaxKind.OpenBracketToken);
writePunctuation(writer, SyntaxKind.CloseBracketToken);
}
else {
// Write the type reference in the format f<A>.g<B>.C<X, Y> where A and B are type arguments
// for outer type parameters, and f and g are the respective declaring containers of those
// type parameters.
let outerTypeParameters = type.target.outerTypeParameters;
let i = 0;
if (outerTypeParameters) {
let length = outerTypeParameters.length;
while (i < length) {
// Find group of type arguments for type parameters with the same declaring container.
let start = i;
let parent = getParentSymbolOfTypeParameter(outerTypeParameters[i]);
do {
i++;
} while (i < length && getParentSymbolOfTypeParameter(outerTypeParameters[i]) === parent);
// When type parameters are their own type arguments for the whole group (i.e. we have
// the default outer type arguments), we don't show the group.
if (!rangeEquals(outerTypeParameters, typeArguments, start, i)) {
writeSymbolTypeReference(parent, typeArguments, start, i);
writePunctuation(writer, SyntaxKind.DotToken);
}
}
}
writeSymbolTypeReference(type.symbol, typeArguments, i, typeArguments.length);
}
}
function writeTupleType(type: TupleType) {
writePunctuation(writer, SyntaxKind.OpenBracketToken);
writeTypeList(type.elementTypes, /*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
let typeAlias = getTypeAliasForTypeLiteral(type);
if (typeAlias) {
// The specified symbol flags need to be reinterpreted as type flags
buildSymbolDisplay(typeAlias, writer, enclosingDeclaration, SymbolFlags.Type, SymbolFormatFlags.None, flags);
}
else {
// Recursive usage, use any
writeKeyword(writer, SyntaxKind.AnyKeyword);
}
}
else {
if (!typeStack) {
typeStack = [];
}
typeStack.push(type);
writeLiteralType(type, flags);
typeStack.pop();
}
function shouldWriteTypeOfFunctionSymbol() {
if (type.symbol) {
let isStaticMethodSymbol = !!(type.symbol.flags & SymbolFlags.Method && // typeof static method
ts.forEach(type.symbol.declarations, declaration => declaration.flags & NodeFlags.Static));
let 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 {
let declaration = <SignatureDeclaration>getIndexDeclarationOfSymbol(type.symbol, indexKind);
if (!declaration) {
// declaration might not be found if indexer was added from the contextual type.
// in this case use fallback name
return fallbackName;
}
Debug.assert(declaration.parameters.length !== 0);
return declarationNameToString(declaration.parameters[0].name);
}
function writeLiteralType(type: ObjectType, flags: TypeFormatFlags) {
let resolved = 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 (let signature of resolved.callSignatures) {
buildSignatureDisplay(signature, writer, enclosingDeclaration, globalFlagsToPass, typeStack);
writePunctuation(writer, SyntaxKind.SemicolonToken);
writer.writeLine();
}
for (let signature of resolved.constructSignatures) {
writeKeyword(writer, SyntaxKind.NewKeyword);
writeSpace(writer);
buildSignatureDisplay(signature, 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 (let p of resolved.properties) {
let t = getTypeOfSymbol(p);
if (p.flags & (SymbolFlags.Function | SymbolFlags.Method) && !getPropertiesOfObjectType(t).length) {
let signatures = getSignaturesOfType(t, SignatureKind.Call);
for (let signature of signatures) {
buildSymbolDisplay(p, writer);
if (p.flags & SymbolFlags.Optional) {
writePunctuation(writer, SyntaxKind.QuestionToken);
}
buildSignatureDisplay(signature, writer, enclosingDeclaration, globalFlagsToPass, 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) {
let targetSymbol = getTargetSymbol(symbol);
if (targetSymbol.flags & SymbolFlags.Class || targetSymbol.flags & SymbolFlags.Interface) {
buildDisplayForTypeParametersAndDelimiters(getLocalTypeParametersOfClassOrInterface(symbol), writer, enclosingDeclaraiton, flags);
}
}
function buildTypeParameterDisplay(tp: TypeParameter, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, typeStack?: Type[]) {
appendSymbolNameOnly(tp.symbol, writer);
let 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) || (<ParameterDeclaration>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 (let 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 (let i = 0; i < typeParameters.length; i++) {
if (i > 0) {
writePunctuation(writer, SyntaxKind.CommaToken);
writeSpace(writer);
}
buildTypeDisplay(mapper(typeParameters[i]), writer, enclosingDeclaration, TypeFormatFlags.None);
}
writePunctuation(writer, SyntaxKind.GreaterThanToken);
}
}
function buildDisplayForParametersAndDelimiters(parameters: Symbol[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, typeStack?: Type[]) {
writePunctuation(writer, SyntaxKind.OpenParenToken);
for (let i = 0; i < parameters.length; i++) {
if (i > 0) {
writePunctuation(writer, SyntaxKind.CommaToken);
writeSpace(writer);
}
buildParameterDisplay(parameters[i], writer, enclosingDeclaration, flags, 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 ((<ModuleDeclaration>node).name.kind === SyntaxKind.StringLiteral) {
return node;
}
}
else if (node.kind === SyntaxKind.SourceFile) {
return isExternalModule(<SourceFile>node) ? node : undefined;
}
}
Debug.fail("getContainingModule cant reach here");
}
function isUsedInExportAssignment(node: Node) {
// Get source File and see if it is external module and has export assigned symbol
let externalModule = getContainingExternalModule(node);
let exportAssignmentSymbol: Symbol;
let resolvedExportSymbol: Symbol;
if (externalModule) {
// This is export assigned symbol node
let externalModuleSymbol = getSymbolOfNode(externalModule);
exportAssignmentSymbol = getExportAssignmentSymbol(externalModuleSymbol);
let symbolOfNode = getSymbolOfNode(node);
if (isSymbolUsedInExportAssignment(symbolOfNode)) {
return true;
}
// if symbolOfNode is alias declaration, resolve the symbol declaration and check
if (symbolOfNode.flags & SymbolFlags.Alias) {
return isSymbolUsedInExportAssignment(resolveAlias(symbolOfNode));
}
}
// Check if the symbol is used in export assignment
function isSymbolUsedInExportAssignment(symbol: Symbol) {
if (exportAssignmentSymbol === symbol) {
return true;
}
if (exportAssignmentSymbol && !!(exportAssignmentSymbol.flags & SymbolFlags.Alias)) {
// if export assigned symbol is alias declaration, resolve the alias
resolvedExportSymbol = resolvedExportSymbol || resolveAlias(exportAssignmentSymbol);
if (resolvedExportSymbol === symbol) {
return true;
}
// Container of resolvedExportSymbol is visible
return forEach(resolvedExportSymbol.declarations, (current: Node) => {
while (current) {
if (current === node) {
return true;
}
current = current.parent;
}
});
}
}
}
function determineIfDeclarationIsVisible() {
switch (node.kind) {
case SyntaxKind.BindingElement:
return isDeclarationVisible(<Declaration>node.parent.parent);
case SyntaxKind.VariableDeclaration:
if (isBindingPattern(node.name) &&
!(<BindingPattern>node.name).elements.length) {
// If the binding pattern is empty, this variable declaration is not visible
return false;
}
// Otherwise fall through
case SyntaxKind.ModuleDeclaration:
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.TypeAliasDeclaration:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.EnumDeclaration:
case SyntaxKind.ImportEqualsDeclaration:
let parent = getDeclarationContainer(node);
// If the node is not exported or it is not ambient module element (except import declaration)
if (!(getCombinedNodeFlags(node) & NodeFlags.Export) &&
!(node.kind !== SyntaxKind.ImportEqualsDeclaration && parent.kind !== SyntaxKind.SourceFile && isInAmbientContext(parent))) {
return isGlobalSourceFile(parent);
}
// Exported members/ambient module elements (exception import declaration) are visible if parent is visible
return isDeclarationVisible(<Declaration>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(<Declaration>node.parent);
// Default binding, import specifier and namespace import is visible
// only on demand so by default it is not visible
case SyntaxKind.ImportClause:
case SyntaxKind.NamespaceImport:
case SyntaxKind.ImportSpecifier:
return false;
// Type parameters are always visible
case SyntaxKind.TypeParameter:
// Source file is always visible
case SyntaxKind.SourceFile:
return true;
// Export assignements do not create name bindings outside the module
case SyntaxKind.ExportAssignment:
return false;
default:
Debug.fail("isDeclarationVisible unknown: SyntaxKind: " + node.kind);
}
}
if (node) {
let links = getNodeLinks(node);
if (links.isVisible === undefined) {
links.isVisible = !!determineIfDeclarationIsVisible();
}
return links.isVisible;
}
}
function collectLinkedAliases(node: Identifier): Node[] {
var exportSymbol: Symbol;
if (node.parent && node.parent.kind === SyntaxKind.ExportAssignment) {
exportSymbol = resolveName(node.parent, node.text, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace, Diagnostics.Cannot_find_name_0, node);
}
else if (node.parent.kind === SyntaxKind.ExportSpecifier) {
exportSymbol = getTargetOfExportSpecifier(<ExportSpecifier>node.parent);
}
var result: Node[] = [];
if (exportSymbol) {
buildVisibleNodeList(exportSymbol.declarations);
}
return result;
function buildVisibleNodeList(declarations: Declaration[]) {
forEach(declarations, declaration => {
getNodeLinks(declaration).isVisible = true;
var resultNode = getAnyImportSyntax(declaration) || declaration;
if (!contains(result, resultNode)) {
result.push(resultNode);
}
if (isInternalModuleImportEqualsDeclaration(declaration)) {
// Add the referenced top container visible
var internalModuleReference = <Identifier | QualifiedName>(<ImportEqualsDeclaration>declaration).moduleReference;
var firstIdentifier = getFirstIdentifier(internalModuleReference);
var importSymbol = resolveName(declaration, firstIdentifier.text, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace,
Diagnostics.Cannot_find_name_0, firstIdentifier);
buildVisibleNodeList(importSymbol.declarations);
}
});
}
}
// Push an entry on the type resolution stack. If an entry with the given target is not already on the stack,
// a new entry with that target and an associated result value of true is pushed on the stack, and the value
// true is returned. Otherwise, a circularity has occurred and the result values of the existing entry and
// all entries pushed after it are changed to false, and the value false is returned. The target object provides
// a unique identity for a particular type resolution result: Symbol instances are used to track resolution of
// SymbolLinks.type, SymbolLinks instances are used to track resolution of SymbolLinks.declaredType, and
// Signature instances are used to track resolution of Signature.resolvedReturnType.
function pushTypeResolution(target: Object): boolean {
let i = 0;
let count = resolutionTargets.length;
while (i < count && resolutionTargets[i] !== target) {
i++;
}
if (i < count) {
do {
resolutionResults[i++] = false;
}
while (i < count);
return false;
}
resolutionTargets.push(target);
resolutionResults.push(true);
return true;
}
// Pop an entry from the type resolution stack and return its associated result value. The result value will
// be true if no circularities were detected, or false if a circularity was found.
function popTypeResolution(): boolean {
resolutionTargets.pop();
return resolutionResults.pop();
}
function getDeclarationContainer(node: Node): Node {
node = getRootDeclaration(node);
// Parent chain:
// VaribleDeclaration -> VariableDeclarationList -> VariableStatement -> 'Declaration Container'
return node.kind === SyntaxKind.VariableDeclaration ? node.parent.parent.parent : node.parent;
}
function getTypeOfPrototypeProperty(prototype: Symbol): Type {
// TypeScript 1.0 spec (April 2014): 8.4
// Every class automatically contains a static property member named 'prototype',
// the type of which is an instantiation of the class type with type Any supplied as a type argument for each type parameter.
// It is an error to explicitly declare a static property member with the name 'prototype'.
let classType = <InterfaceType>getDeclaredTypeOfSymbol(prototype.parent);
return classType.typeParameters ? createTypeReference(<GenericType>classType, map(classType.typeParameters, _ => anyType)) : classType;
}
// Return the type of the given property in the given type, or undefined if no such property exists
function getTypeOfPropertyOfType(type: Type, name: string): Type {
let prop = getPropertyOfType(type, name);
return prop ? getTypeOfSymbol(prop) : undefined;
}
// Return the inferred type for a binding element
function getTypeForBindingElement(declaration: BindingElement): Type {
let pattern = <BindingPattern>declaration.parent;
let parentType = getTypeForVariableLikeDeclaration(<VariableLikeDeclaration>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;
}
let type: Type;
if (pattern.kind === SyntaxKind.ObjectBindingPattern) {
// Use explicitly specified property name ({ p: xxx } form), or otherwise the implied name ({ p } form)
let name = declaration.propertyName || <Identifier>declaration.name;
// Use type of the specified property, or otherwise, for a numeric name, the type of the numeric index signature,
// or otherwise the type of the string index signature.
type = getTypeOfPropertyOfType(parentType, name.text) ||
isNumericLiteralName(name.text) && getIndexTypeOfType(parentType, IndexKind.Number) ||
getIndexTypeOfType(parentType, IndexKind.String);
if (!type) {
error(name, Diagnostics.Type_0_has_no_property_1_and_no_string_index_signature, typeToString(parentType), declarationNameToString(name));
return unknownType;
}
}
else {
// This elementType will be used if the specific property corresponding to this index is not
// present (aka the tuple element property). This call also checks that the parentType is in
// fact an iterable or array (depending on target language).
let elementType = checkIteratedTypeOrElementType(parentType, pattern, /*allowStringInput*/ false);
if (!declaration.dotDotDotToken) {
if (elementType.flags & TypeFlags.Any) {
return elementType;
}
// Use specific property type when parent is a tuple or numeric index type when parent is an array
let propName = "" + indexOf(pattern.elements, declaration);
type = isTupleLikeType(parentType)
? getTypeOfPropertyOfType(parentType, propName)
: elementType;
if (!type) {
if (isTupleType(parentType)) {
error(declaration, Diagnostics.Tuple_type_0_with_length_1_cannot_be_assigned_to_tuple_with_length_2, typeToString(parentType), (<TupleType>parentType).elementTypes.length, pattern.elements.length);
}
else {
error(declaration, Diagnostics.Type_0_has_no_property_1, typeToString(parentType), propName);
}
return unknownType;
}
}
else {
// Rest element has an array type with the same element type as the parent type
type = createArrayType(elementType);
}
}
return type;
}
// Return the inferred type for a variable, parameter, or property declaration
function getTypeForVariableLikeDeclaration(declaration: VariableLikeDeclaration): Type {
// A variable declared in a for..in statement is always of type any
if (declaration.parent.parent.kind === SyntaxKind.ForInStatement) {
return anyType;
}
if (declaration.parent.parent.kind === SyntaxKind.ForOfStatement) {
// checkRightHandSideOfForOf will return undefined if the for-of expression type was
// missing properties/signatures required to get its iteratedType (like
// [Symbol.iterator] or next). This may be because we accessed properties from anyType,
// or it may have led to an error inside getElementTypeOfIterable.
return checkRightHandSideOfForOf((<ForOfStatement>declaration.parent.parent).expression) || anyType;
}
if (isBindingPattern(declaration.parent)) {
return getTypeForBindingElement(<BindingElement>declaration);
}
// Use type from type annotation if one is present
if (declaration.type) {
return getTypeFromTypeNode(declaration.type);
}
if (declaration.kind === SyntaxKind.Parameter) {
let func = <FunctionLikeDeclaration>declaration.parent;
// For a parameter of a set accessor, use the type of the get accessor if one is present
if (func.kind === SyntaxKind.SetAccessor && !hasDynamicName(func)) {
let getter = <AccessorDeclaration>getDeclarationOfKind(declaration.parent.symbol, SyntaxKind.GetAccessor);
if (getter) {
return getReturnTypeOfSignature(getSignatureFromDeclaration(getter));
}
}
// Use contextual parameter type if one is available
let type = getContextuallyTypedParameterType(<ParameterDeclaration>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(<Identifier>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(<BindingPattern>element.name);
}
return anyType;
}
// Return the type implied by an object binding pattern
function getTypeFromObjectBindingPattern(pattern: BindingPattern): Type {
let members: SymbolTable = {};
forEach(pattern.elements, e => {
let flags = SymbolFlags.Property | SymbolFlags.Transient | (e.initializer ? SymbolFlags.Optional : 0);
let name = e.propertyName || <Identifier>e.name;
let symbol = <TransientSymbol>createSymbol(flags, name.text);
symbol.type = getTypeFromBindingElement(e);
members[symbol.name] = symbol;
});
return createAnonymousType(undefined, members, emptyArray, emptyArray, undefined, undefined);
}
// Return the type implied by an array binding pattern
function getTypeFromArrayBindingPattern(pattern: BindingPattern): Type {
let hasSpreadElement: boolean = false;
let elementTypes: Type[] = [];
forEach(pattern.elements, e => {
elementTypes.push(e.kind === SyntaxKind.OmittedExpression || e.dotDotDotToken ? anyType : getTypeFromBindingElement(e));
if (e.dotDotDotToken) {
hasSpreadElement = true;
}
});
if (!elementTypes.length) {
return languageVersion >= ScriptTarget.ES6 ? createIterableType(anyType) : anyArrayType;
}
else if (hasSpreadElement) {
let unionOfElements = getUnionType(elementTypes);
return languageVersion >= ScriptTarget.ES6 ? createIterableType(unionOfElements) : createArrayType(unionOfElements);
}
// If the pattern has at least one element, and no rest element, then it should imply a tuple type.
return createTupleType(elementTypes);
}
// Return the type implied by a binding pattern. This is the type implied purely by the binding pattern itself
// and without regard to its context (i.e. without regard any type annotation or initializer associated with the
// declaration in which the binding pattern is contained). For example, the implied type of [x, y] is [any, any]
// and the implied type of { x, y: z = 1 } is { x: any; y: number; }. The type implied by a binding pattern is
// used as the contextual type of an initializer associated with the binding pattern. Also, for a destructuring
// parameter with no type annotation or initializer, the type implied by the binding pattern becomes the type of
// the parameter.
function getTypeFromBindingPattern(pattern: BindingPattern): Type {
return pattern.kind === SyntaxKind.ObjectBindingPattern
? getTypeFromObjectBindingPattern(pattern)
: getTypeFromArrayBindingPattern(pattern);
}
// Return the type associated with a variable, parameter, or property declaration. In the simple case this is the type
// specified in a type annotation or inferred from an initializer. However, in the case of a destructuring declaration it
// is a bit more involved. For example:
//
// var [x, s = ""] = [1, "one"];
//
// Here, the array literal [1, "one"] is contextually typed by the type [any, string], which is the implied type of the
// binding pattern [x, s = ""]. Because the contextual type is a tuple type, the resulting type of [1, "one"] is the
// tuple type [number, string]. Thus, the type inferred for 'x' is number and the type inferred for 's' is string.
function getWidenedTypeForVariableLikeDeclaration(declaration: VariableLikeDeclaration, reportErrors?: boolean): Type {
let type = getTypeForVariableLikeDeclaration(declaration);
if (type) {
if (reportErrors) {
reportErrorsFromWidening(declaration, type);
}
// During a normal type check we'll never get to here with a property assignment (the check of the containing
// object literal uses a different path). We exclude widening only so that language services and type verification
// tools see the actual type.
return declaration.kind !== SyntaxKind.PropertyAssignment ? getWidenedType(type) : type;
}
// If no type was specified and nothing could be inferred, and if the declaration specifies a binding pattern, use
// the type implied by the binding pattern
if (isBindingPattern(declaration.name)) {
return getTypeFromBindingPattern(<BindingPattern>declaration.name);
}
// Rest parameters default to type any[], other parameters default to type any
type = declaration.dotDotDotToken ? anyArrayType : anyType;
// Report implicit any errors unless this is a private property within an ambient declaration
if (reportErrors && compilerOptions.noImplicitAny) {
let root = getRootDeclaration(declaration);
if (!isPrivateWithinAmbient(root) && !(root.kind === SyntaxKind.Parameter && isPrivateWithinAmbient(root.parent))) {
reportImplicitAnyError(declaration, type);
}
}
return type;
}
function getTypeOfVariableOrParameterOrProperty(symbol: Symbol): Type {
let links = getSymbolLinks(symbol);
if (!links.type) {
// Handle prototype property
if (symbol.flags & SymbolFlags.Prototype) {
return links.type = getTypeOfPrototypeProperty(symbol);
}
// Handle catch clause variables
let declaration = symbol.valueDeclaration;
if (declaration.parent.kind === SyntaxKind.CatchClause) {
return links.type = anyType;
}
// Handle export default expressions
if (declaration.kind === SyntaxKind.ExportAssignment) {
return links.type = checkExpression((<ExportAssignment>declaration).expression);
}
// Handle variable, parameter or property
if (!pushTypeResolution(symbol)) {
return unknownType;
}
let type = getWidenedTypeForVariableLikeDeclaration(<VariableLikeDeclaration>declaration, /*reportErrors*/ true);
if (!popTypeResolution()) {
if ((<VariableLikeDeclaration>symbol.valueDeclaration).type) {
// Variable has type annotation that circularly references the variable itself
type = unknownType;
error(symbol.valueDeclaration, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_type_annotation,
symbolToString(symbol));
}
else {
// Variable has initializer that circularly references the variable itself
type = anyType;
if (compilerOptions.noImplicitAny) {
error(symbol.valueDeclaration, Diagnostics._0_implicitly_has_type_any_because_it_is_does_not_have_a_type_annotation_and_is_referenced_directly_or_indirectly_in_its_own_initializer,
symbolToString(symbol));
}
}
}
links.type = type;
}
return links.type;
}
function getSetAccessorTypeAnnotationNode(accessor: AccessorDeclaration): TypeNode {
return accessor && accessor.parameters.length > 0 && accessor.parameters[0].type;
}
function getAnnotatedAccessorType(accessor: AccessorDeclaration): Type {
if (accessor) {
if (accessor.kind === SyntaxKind.GetAccessor) {
return accessor.type && getTypeFromTypeNode(accessor.type);
}
else {
let setterTypeAnnotation = getSetAccessorTypeAnnotationNode(accessor);
return setterTypeAnnotation && getTypeFromTypeNode(setterTypeAnnotation);
}
}
return undefined;
}
function getTypeOfAccessors(symbol: Symbol): Type {
let links = getSymbolLinks(symbol);
if (!links.type) {
if (!pushTypeResolution(symbol)) {
return unknownType;
}
let getter = <AccessorDeclaration>getDeclarationOfKind(symbol, SyntaxKind.GetAccessor);
let setter = <AccessorDeclaration>getDeclarationOfKind(symbol, SyntaxKind.SetAccessor);
let type: Type;
// First try to see if the user specified a return type on the get-accessor.
let getterReturnType = getAnnotatedAccessorType(getter);
if (getterReturnType) {
type = getterReturnType;
}
else {
// If the user didn't specify a return type, try to use the set-accessor's parameter type.
let setterParameterType = getAnnotatedAccessorType(setter);
if (setterParameterType) {
type = setterParameterType;
}
else {
// If there are no specified types, try to infer it from the body of the get accessor if it exists.
if (getter && getter.body) {
type = getReturnTypeFromBody(getter);
}
// Otherwise, fall back to 'any'.
else {
if (compilerOptions.noImplicitAny) {
error(setter, Diagnostics.Property_0_implicitly_has_type_any_because_its_set_accessor_lacks_a_type_annotation, symbolToString(symbol));
}
type = anyType;
}
}
}
if (!popTypeResolution()) {
type = anyType;
if (compilerOptions.noImplicitAny) {
let getter = <AccessorDeclaration>getDeclarationOfKind(symbol, SyntaxKind.GetAccessor);
error(getter, Diagnostics._0_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions, symbolToString(symbol));
}
}
links.type = type;
}
return links.type;
}
function getTypeOfFuncClassEnumModule(symbol: Symbol): Type {
let links = getSymbolLinks(symbol);
if (!links.type) {
links.type = createObjectType(TypeFlags.Anonymous, symbol);
}
return links.type;
}
function getTypeOfEnumMember(symbol: Symbol): Type {
let links = getSymbolLinks(symbol);
if (!links.type) {
links.type = getDeclaredTypeOfEnum(getParentOfSymbol(symbol));
}
return links.type;
}
function getTypeOfAlias(symbol: Symbol): Type {
let links = getSymbolLinks(symbol);
if (!links.type) {
let targetSymbol = resolveAlias(symbol);
// It only makes sense to get the type of a value symbol. If the result of resolving
// the alias is not a value, then it has no type. To get the type associated with a
// type symbol, call getDeclaredTypeOfSymbol.
// This check is important because without it, a call to getTypeOfSymbol could end
// up recursively calling getTypeOfAlias, causing a stack overflow.
links.type = targetSymbol.flags & SymbolFlags.Value
? getTypeOfSymbol(targetSymbol)
: unknownType;
}
return links.type;
}
function getTypeOfInstantiatedSymbol(symbol: Symbol): Type {
let links = getSymbolLinks(symbol);
if (!links.type) {
links.type = instantiateType(getTypeOfSymbol(links.target), links.mapper);
}
return links.type;
}
function getTypeOfSymbol(symbol: Symbol): Type {
if (symbol.flags & SymbolFlags.Instantiated) {
return getTypeOfInstantiatedSymbol(symbol);
}
if (symbol.flags & (SymbolFlags.Variable | SymbolFlags.Property)) {
return getTypeOfVariableOrParameterOrProperty(symbol);
}
if (symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.Enum | SymbolFlags.ValueModule)) {
return getTypeOfFuncClassEnumModule(symbol);
}
if (symbol.flags & SymbolFlags.EnumMember) {
return getTypeOfEnumMember(symbol);
}
if (symbol.flags & SymbolFlags.Accessor) {
return getTypeOfAccessors(symbol);
}
if (symbol.flags & SymbolFlags.Alias) {
return getTypeOfAlias(symbol);
}
return unknownType;
}
function getTargetType(type: ObjectType): Type {
return type.flags & TypeFlags.Reference ? (<TypeReference>type).target : type;
}
function hasBaseType(type: InterfaceType, checkBase: InterfaceType) {
return check(type);
function check(type: InterfaceType): boolean {
let target = <InterfaceType>getTargetType(type);
return target === checkBase || forEach(getBaseTypes(target), check);
}
}
// Appends the type parameters given by a list of declarations to a set of type parameters and returns the resulting set.
// The function allocates a new array if the input type parameter set is undefined, but otherwise it modifies the set
// in-place and returns the same array.
function appendTypeParameters(typeParameters: TypeParameter[], declarations: TypeParameterDeclaration[]): TypeParameter[] {
for (let declaration of declarations) {
let tp = getDeclaredTypeOfTypeParameter(getSymbolOfNode(declaration));
if (!typeParameters) {
typeParameters = [tp];
}
else if (!contains(typeParameters, tp)) {
typeParameters.push(tp);
}
}
return typeParameters;
}
// Appends the outer type parameters of a node to a set of type parameters and returns the resulting set. The function
// allocates a new array if the input type parameter set is undefined, but otherwise it modifies the set in-place and
// returns the same array.
function appendOuterTypeParameters(typeParameters: TypeParameter[], node: Node): TypeParameter[]{
while (true) {
node = node.parent;
if (!node) {
return typeParameters;
}
if (node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.FunctionDeclaration ||
node.kind === SyntaxKind.FunctionExpression || node.kind === SyntaxKind.MethodDeclaration ||
node.kind === SyntaxKind.ArrowFunction) {
let declarations = (<ClassDeclaration | FunctionLikeDeclaration>node).typeParameters;
if (declarations) {
return appendTypeParameters(appendOuterTypeParameters(typeParameters, node), declarations);
}
}
}
}
// The outer type parameters are those defined by enclosing generic classes, methods, or functions.
function getOuterTypeParametersOfClassOrInterface(symbol: Symbol): TypeParameter[] {
var kind = symbol.flags & SymbolFlags.Class ? SyntaxKind.ClassDeclaration : SyntaxKind.InterfaceDeclaration;
return appendOuterTypeParameters(undefined, getDeclarationOfKind(symbol, kind));
}
// The local type parameters are the combined set of type parameters from all declarations of the class or interface.
function getLocalTypeParametersOfClassOrInterface(symbol: Symbol): TypeParameter[] {
let result: TypeParameter[];
for (let node of symbol.declarations) {
if (node.kind === SyntaxKind.InterfaceDeclaration || node.kind === SyntaxKind.ClassDeclaration) {
let declaration = <InterfaceDeclaration>node;
if (declaration.typeParameters) {
result = appendTypeParameters(result, declaration.typeParameters);
}
}
}
return result;
}
// The full set of type parameters for a generic class or interface type consists of its outer type parameters plus
// its locally declared type parameters.
function getTypeParametersOfClassOrInterface(symbol: Symbol): TypeParameter[] {
return concatenate(getOuterTypeParametersOfClassOrInterface(symbol), getLocalTypeParametersOfClassOrInterface(symbol));
}
function getBaseTypes(type: InterfaceType): ObjectType[] {
let typeWithBaseTypes = <InterfaceTypeWithBaseTypes>type;
if (!typeWithBaseTypes.baseTypes) {
if (type.symbol.flags & SymbolFlags.Class) {
resolveBaseTypesOfClass(typeWithBaseTypes);
}
else if (type.symbol.flags & SymbolFlags.Interface) {
resolveBaseTypesOfInterface(typeWithBaseTypes);
}
else {
Debug.fail("type must be class or interface");
}
}
return typeWithBaseTypes.baseTypes;
}
function resolveBaseTypesOfClass(type: InterfaceTypeWithBaseTypes): void {
type.baseTypes = [];
let declaration = <ClassDeclaration>getDeclarationOfKind(type.symbol, SyntaxKind.ClassDeclaration);
let baseTypeNode = getClassExtendsHeritageClauseElement(declaration);
if (baseTypeNode) {
let baseType = getTypeFromTypeNode(baseTypeNode);
if (baseType !== unknownType) {
if (getTargetType(baseType).flags & TypeFlags.Class) {
if (type !== baseType && !hasBaseType(<InterfaceType>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);
}
}
}
}
function resolveBaseTypesOfInterface(type: InterfaceTypeWithBaseTypes): void {
type.baseTypes = [];
for (let declaration of type.symbol.declarations) {
if (declaration.kind === SyntaxKind.InterfaceDeclaration && getInterfaceBaseTypeNodes(<InterfaceDeclaration>declaration)) {
for (let node of getInterfaceBaseTypeNodes(<InterfaceDeclaration>declaration)) {
let baseType = getTypeFromTypeNode(node);
if (baseType !== unknownType) {
if (getTargetType(baseType).flags & (TypeFlags.Class | TypeFlags.Interface)) {
if (type !== baseType && !hasBaseType(<InterfaceType>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);
}
}
}
}
}
}
function getDeclaredTypeOfClassOrInterface(symbol: Symbol): InterfaceType {
let links = getSymbolLinks(symbol);
if (!links.declaredType) {
let kind = symbol.flags & SymbolFlags.Class ? TypeFlags.Class : TypeFlags.Interface;
let type = links.declaredType = <InterfaceType>createObjectType(kind, symbol);
let outerTypeParameters = getOuterTypeParametersOfClassOrInterface(symbol);
let localTypeParameters = getLocalTypeParametersOfClassOrInterface(symbol);
if (outerTypeParameters || localTypeParameters) {
type.flags |= TypeFlags.Reference;
type.typeParameters = concatenate(outerTypeParameters, localTypeParameters);
type.outerTypeParameters = outerTypeParameters;
type.localTypeParameters = localTypeParameters;
(<GenericType>type).instantiations = {};
(<GenericType>type).instantiations[getTypeListId(type.typeParameters)] = <GenericType>type;
(<GenericType>type).target = <GenericType>type;
(<GenericType>type).typeArguments = type.typeParameters;
}
}
return <InterfaceType>links.declaredType;
}
function getDeclaredTypeOfTypeAlias(symbol: Symbol): Type {
let links = getSymbolLinks(symbol);
if (!links.declaredType) {
// Note that we use the links object as the target here because the symbol object is used as the unique
// identity for resolution of the 'type' property in SymbolLinks.
if (!pushTypeResolution(links)) {
return unknownType;
}
let declaration = <TypeAliasDeclaration>getDeclarationOfKind(symbol, SyntaxKind.TypeAliasDeclaration);
let type = getTypeFromTypeNode(declaration.type);
if (!popTypeResolution()) {
type = unknownType;
error(declaration.name, Diagnostics.Type_alias_0_circularly_references_itself, symbolToString(symbol));
}
links.declaredType = type;
}
return links.declaredType;
}
function getDeclaredTypeOfEnum(symbol: Symbol): Type {
let links = getSymbolLinks(symbol);
if (!links.declaredType) {
let type = createType(TypeFlags.Enum);
type.symbol = symbol;
links.declaredType = type;
}
return links.declaredType;
}
function getDeclaredTypeOfTypeParameter(symbol: Symbol): TypeParameter {
let links = getSymbolLinks(symbol);
if (!links.declaredType) {
let type = <TypeParameter>createType(TypeFlags.TypeParameter);
type.symbol = symbol;
if (!(<TypeParameterDeclaration>getDeclarationOfKind(symbol, SyntaxKind.TypeParameter)).constraint) {
type.constraint = noConstraintType;
}
links.declaredType = type;
}
return <TypeParameter>links.declaredType;
}
function getDeclaredTypeOfAlias(symbol: Symbol): Type {
let links = getSymbolLinks(symbol);
if (!links.declaredType) {
links.declaredType = getDeclaredTypeOfSymbol(resolveAlias(symbol));
}
return links.declaredType;
}
function getDeclaredTypeOfSymbol(symbol: Symbol): Type {
Debug.assert((symbol.flags & SymbolFlags.Instantiated) === 0);
if (symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) {
return getDeclaredTypeOfClassOrInterface(symbol);
}
if (symbol.flags & SymbolFlags.TypeAlias) {
return getDeclaredTypeOfTypeAlias(symbol);
}
if (symbol.flags & SymbolFlags.Enum) {
return getDeclaredTypeOfEnum(symbol);
}
if (symbol.flags & SymbolFlags.TypeParameter) {
return getDeclaredTypeOfTypeParameter(symbol);
}
if (symbol.flags & SymbolFlags.Alias) {
return getDeclaredTypeOfAlias(symbol);
}
return unknownType;
}
function createSymbolTable(symbols: Symbol[]): SymbolTable {
let result: SymbolTable = {};
for (let symbol of symbols) {
result[symbol.name] = symbol;
}
return result;
}
function createInstantiatedSymbolTable(symbols: Symbol[], mapper: TypeMapper): SymbolTable {
let result: SymbolTable = {};
for (let symbol of symbols) {
result[symbol.name] = instantiateSymbol(symbol, mapper);
}
return result;
}
function addInheritedMembers(symbols: SymbolTable, baseSymbols: Symbol[]) {
for (let s of baseSymbols) {
if (!hasProperty(symbols, s.name)) {
symbols[s.name] = s;
}
}
}
function addInheritedSignatures(signatures: Signature[], baseSignatures: Signature[]) {
if (baseSignatures) {
for (let signature of baseSignatures) {
signatures.push(signature);
}
}
}
function resolveDeclaredMembers(type: InterfaceType): InterfaceTypeWithDeclaredMembers {
if (!(<InterfaceTypeWithDeclaredMembers>type).declaredProperties) {
var symbol = type.symbol;
(<InterfaceTypeWithDeclaredMembers>type).declaredProperties = getNamedMembers(symbol.members);
(<InterfaceTypeWithDeclaredMembers>type).declaredCallSignatures = getSignaturesOfSymbol(symbol.members["__call"]);
(<InterfaceTypeWithDeclaredMembers>type).declaredConstructSignatures = getSignaturesOfSymbol(symbol.members["__new"]);
(<InterfaceTypeWithDeclaredMembers>type).declaredStringIndexType = getIndexTypeOfSymbol(symbol, IndexKind.String);
(<InterfaceTypeWithDeclaredMembers>type).declaredNumberIndexType = getIndexTypeOfSymbol(symbol, IndexKind.Number);
}
return <InterfaceTypeWithDeclaredMembers>type;
}
function resolveClassOrInterfaceMembers(type: InterfaceType): void {
let target = resolveDeclaredMembers(type);
let members = target.symbol.members;
let callSignatures = target.declaredCallSignatures;
let constructSignatures = target.declaredConstructSignatures;
let stringIndexType = target.declaredStringIndexType;
let numberIndexType = target.declaredNumberIndexType;
let baseTypes = getBaseTypes(target);
if (baseTypes.length) {
members = createSymbolTable(target.declaredProperties);
for (let baseType of baseTypes) {
addInheritedMembers(members, getPropertiesOfObjectType(baseType));
callSignatures = concatenate(callSignatures, getSignaturesOfType(baseType, SignatureKind.Call));
constructSignatures = concatenate(constructSignatures, getSignaturesOfType(baseType, SignatureKind.Construct));
stringIndexType = stringIndexType || getIndexTypeOfType(baseType, IndexKind.String);
numberIndexType = numberIndexType || getIndexTypeOfType(baseType, IndexKind.Number);
}
}
setObjectTypeMembers(type, members, callSignatures, constructSignatures, stringIndexType, numberIndexType);
}
function resolveTypeReferenceMembers(type: TypeReference): void {
let target = resolveDeclaredMembers(type.target);
let mapper = createTypeMapper(target.typeParameters, type.typeArguments);
let members = createInstantiatedSymbolTable(target.declaredProperties, mapper);
let callSignatures = instantiateList(target.declaredCallSignatures, mapper, instantiateSignature);
let constructSignatures = instantiateList(target.declaredConstructSignatures, mapper, instantiateSignature);
let stringIndexType = target.declaredStringIndexType ? instantiateType(target.declaredStringIndexType, mapper) : undefined;
let numberIndexType = target.declaredNumberIndexType ? instantiateType(target.declaredNumberIndexType, mapper) : undefined;
forEach(getBaseTypes(target), baseType => {
let instantiatedBaseType = instantiateType(baseType, mapper);
addInheritedMembers(members, getPropertiesOfObjectType(instantiatedBaseType));
callSignatures = concatenate(callSignatures, getSignaturesOfType(instantiatedBaseType, SignatureKind.Call));
constructSignatures = concatenate(constructSignatures, getSignaturesOfType(instantiatedBaseType, SignatureKind.Construct));
stringIndexType = stringIndexType || getIndexTypeOfType(instantiatedBaseType, IndexKind.String);
numberIndexType = numberIndexType || getIndexTypeOfType(instantiatedBaseType, IndexKind.Number);
});
setObjectTypeMembers(type, members, callSignatures, constructSignatures, stringIndexType, numberIndexType);
}
function createSignature(declaration: SignatureDeclaration, typeParameters: TypeParameter[], parameters: Symbol[],
resolvedReturnType: Type, minArgumentCount: number, hasRestParameter: boolean, hasStringLiterals: boolean): Signature {
let sig = new Signature(checker);
sig.declaration = declaration;
sig.typeParameters = typeParameters;
sig.parameters = parameters;
sig.resolvedReturnType = resolvedReturnType;
sig.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[]{
let baseTypes = getBaseTypes(classType);
if (baseTypes.length) {
let baseType = baseTypes[0];
let baseSignatures = getSignaturesOfType(getTypeOfSymbol(baseType.symbol), SignatureKind.Construct);
return map(baseSignatures, baseSignature => {
let signature = baseType.flags & TypeFlags.Reference ?
getSignatureInstantiation(baseSignature, (<TypeReference>baseType).typeArguments) : cloneSignature(baseSignature);
signature.typeParameters = classType.localTypeParameters;
signature.resolvedReturnType = classType;
return signature;
});
}
return [createSignature(undefined, classType.localTypeParameters, emptyArray, classType, 0, false, false)];
}
function createTupleTypeMemberSymbols(memberTypes: Type[]): SymbolTable {
let members: SymbolTable = {};
for (let i = 0; i < memberTypes.length; i++) {
let symbol = <TransientSymbol>createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "" + i);
symbol.type = memberTypes[i];
members[i] = symbol;
}
return members;
}
function resolveTupleTypeMembers(type: TupleType) {
let arrayType = resolveObjectOrUnionTypeMembers(createArrayType(getUnionType(type.elementTypes)));
let members = createTupleTypeMemberSymbols(type.elementTypes);
addInheritedMembers(members, arrayType.properties);
setObjectTypeMembers(type, members, arrayType.callSignatures, arrayType.constructSignatures, arrayType.stringIndexType, arrayType.numberIndexType);
}
function signatureListsIdentical(s: Signature[], t: Signature[]): boolean {
if (s.length !== t.length) {
return false;
}
for (let i = 0; i < s.length; i++) {
if (!compareSignatures(s[i], t[i], /*compareReturnTypes*/ false, compareTypes)) {
return false;
}
}
return true;
}
// If the lists of call or construct signatures in the given types are all identical except for return types,
// and if none of the signatures are generic, return a list of signatures that has substitutes a union of the
// return types of the corresponding signatures in each resulting signature.
function getUnionSignatures(types: Type[], kind: SignatureKind): Signature[] {
let signatureLists = map(types, t => getSignaturesOfType(t, kind));
let signatures = signatureLists[0];
for (let signature of signatures) {
if (signature.typeParameters) {
return emptyArray;
}
}
for (let i = 1; i < signatureLists.length; i++) {
if (!signatureListsIdentical(signatures, signatureLists[i])) {
return emptyArray;
}
}
let result = map(signatures, cloneSignature);
for (var i = 0; i < result.length; i++) {
let s = result[i];
// Clear resolved return type we possibly got from cloneSignature
s.resolvedReturnType = undefined;
s.unionSignatures = map(signatureLists, signatures => signatures[i]);
}
return result;
}
function getUnionIndexType(types: Type[], kind: IndexKind): Type {
let indexTypes: Type[] = [];
for (let type of types) {
let indexType = getIndexTypeOfType(type, kind);
if (!indexType) {
return undefined;
}
indexTypes.push(indexType);
}
return getUnionType(indexTypes);
}
function resolveUnionTypeMembers(type: UnionType) {
// The members and properties collections are empty for union types. To get all properties of a union
// type use getPropertiesOfType (only the language service uses this).
let callSignatures = getUnionSignatures(type.types, SignatureKind.Call);
let constructSignatures = getUnionSignatures(type.types, SignatureKind.Construct);
let stringIndexType = getUnionIndexType(type.types, IndexKind.String);
let numberIndexType = getUnionIndexType(type.types, IndexKind.Number);
setObjectTypeMembers(type, emptySymbols, callSignatures, constructSignatures, stringIndexType, numberIndexType);
}
function resolveAnonymousTypeMembers(type: ObjectType) {
let symbol = type.symbol;
let members: SymbolTable;
let callSignatures: Signature[];
let constructSignatures: Signature[];
let stringIndexType: Type;
let numberIndexType: Type;
if (symbol.flags & SymbolFlags.TypeLiteral) {
members = symbol.members;
callSignatures = getSignaturesOfSymbol(members["__call"]);
constructSignatures = getSignaturesOfSymbol(members["__new"]);
stringIndexType = getIndexTypeOfSymbol(symbol, IndexKind.String);
numberIndexType = getIndexTypeOfSymbol(symbol, IndexKind.Number);
}
else {
// Combinations of function, class, enum and module
members = emptySymbols;
callSignatures = emptyArray;
constructSignatures = emptyArray;
if (symbol.flags & SymbolFlags.HasExports) {
members = getExportsOfSymbol(symbol);
}
if (symbol.flags & (SymbolFlags.Function | SymbolFlags.Method)) {
callSignatures = getSignaturesOfSymbol(symbol);
}
if (symbol.flags & SymbolFlags.Class) {
let classType = getDeclaredTypeOfClassOrInterface(symbol);
constructSignatures = getSignaturesOfSymbol(symbol.members["__constructor"]);
if (!constructSignatures.length) {
constructSignatures = getDefaultConstructSignatures(classType);
}
let baseTypes = getBaseTypes(classType);
if (baseTypes.length) {
members = createSymbolTable(getNamedMembers(members));
addInheritedMembers(members, getPropertiesOfObjectType(getTypeOfSymbol(baseTypes[0].symbol)));
}
}
stringIndexType = undefined;
numberIndexType = (symbol.flags & SymbolFlags.Enum) ? stringType : undefined;
}
setObjectTypeMembers(type, members, callSignatures, constructSignatures, stringIndexType, numberIndexType);
}
function resolveObjectOrUnionTypeMembers(type: ObjectType): ResolvedType {
if (!(<ResolvedType>type).members) {
if (type.flags & (TypeFlags.Class | TypeFlags.Interface)) {
resolveClassOrInterfaceMembers(<InterfaceType>type);
}
else if (type.flags & TypeFlags.Anonymous) {
resolveAnonymousTypeMembers(<ObjectType>type);
}
else if (type.flags & TypeFlags.Tuple) {
resolveTupleTypeMembers(<TupleType>type);
}
else if (type.flags & TypeFlags.Union) {
resolveUnionTypeMembers(<UnionType>type);
}
else {
resolveTypeReferenceMembers(<TypeReference>type);
}
}
return <ResolvedType>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(<ObjectType>type).properties;
}
return emptyArray;
}
// If the given type is an object type and that type has a property by the given name, return
// the symbol for that property. Otherwise return undefined.
function getPropertyOfObjectType(type: Type, name: string): Symbol {
if (type.flags & TypeFlags.ObjectType) {
let resolved = resolveObjectOrUnionTypeMembers(<ObjectType>type);
if (hasProperty(resolved.members, name)) {
let symbol = resolved.members[name];
if (symbolIsValue(symbol)) {
return symbol;
}
}
}
}
function getPropertiesOfUnionType(type: UnionType): Symbol[] {
let result: Symbol[] = [];
forEach(getPropertiesOfType(type.types[0]), prop => {
let unionProp = getPropertyOfUnionType(type, prop.name);
if (unionProp) {
result.push(unionProp);
}
});
return result;
}
function getPropertiesOfType(type: Type): Symbol[] {
type = getApparentType(type);
return type.flags & TypeFlags.Union ? getPropertiesOfUnionType(<UnionType>type) : getPropertiesOfObjectType(type);
}
// For a type parameter, return the base constraint of the type parameter. For the string, number,
// boolean, and symbol primitive types, return the corresponding object types. Otherwise return the
// type itself. Note that the apparent type of a union type is the union type itself.
function getApparentType(type: Type): Type {
if (type.flags & TypeFlags.Union) {
type = getReducedTypeOfUnionType(<UnionType>type);
}
if (type.flags & TypeFlags.TypeParameter) {
do {
type = getConstraintOfTypeParameter(<TypeParameter>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 {
let types = unionType.types;
let props: Symbol[];
for (let current of types) {
let type = getApparentType(current);
if (type !== unknownType) {
let prop = getPropertyOfType(type, name);
if (!prop || getDeclarationFlagsFromSymbol(prop) & (NodeFlags.Private | NodeFlags.Protected)) {
return undefined;
}
if (!props) {
props = [prop];
}
else {
props.push(prop);
}
}
}
let propTypes: Type[] = [];
let declarations: Declaration[] = [];
for (let prop of props) {
if (prop.declarations) {
declarations.push.apply(declarations, prop.declarations);
}
propTypes.push(getTypeOfSymbol(prop));
}
let result = <TransientSymbol>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 {
let properties = type.resolvedProperties || (type.resolvedProperties = {});
if (hasProperty(properties, name)) {
return properties[name];
}
let 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 {
type = getApparentType(type);
if (type.flags & TypeFlags.ObjectType) {
let resolved = resolveObjectOrUnionTypeMembers(type);
if (hasProperty(resolved.members, name)) {
let symbol = resolved.members[name];
if (symbolIsValue(symbol)) {
return symbol;
}
}
if (resolved === anyFunctionType || resolved.callSignatures.length || resolved.constructSignatures.length) {
let symbol = getPropertyOfObjectType(globalFunctionType, name);
if (symbol) {
return symbol;
}
}
return getPropertyOfObjectType(globalObjectType, name);
}
if (type.flags & TypeFlags.Union) {
return getPropertyOfUnionType(<UnionType>type, name);
}
return undefined;
}
function getSignaturesOfObjectOrUnionType(type: Type, kind: SignatureKind): Signature[] {
if (type.flags & (TypeFlags.ObjectType | TypeFlags.Union)) {
let resolved = resolveObjectOrUnionTypeMembers(<ObjectType>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 typeHasCallOrConstructSignatures(type: Type): boolean {
let apparentType = getApparentType(type);
if (apparentType.flags & (TypeFlags.ObjectType | TypeFlags.Union)) {
let resolved = resolveObjectOrUnionTypeMembers(<ObjectType>type);
return resolved.callSignatures.length > 0
|| resolved.constructSignatures.length > 0;
}
return false;
}
function getIndexTypeOfObjectOrUnionType(type: Type, kind: IndexKind): Type {
if (type.flags & (TypeFlags.ObjectType | TypeFlags.Union)) {
let resolved = resolveObjectOrUnionTypeMembers(<ObjectType>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[] {
let result: TypeParameter[] = [];
forEach(typeParameterDeclarations, node => {
let tp = getDeclaredTypeOfTypeParameter(node.symbol);
if (!contains(result, tp)) {
result.push(tp);
}
});
return result;
}
function symbolsToArray(symbols: SymbolTable): Symbol[] {
let result: Symbol[] = [];
for (let id in symbols) {
if (!isReservedMemberName(id)) {
result.push(symbols[id]);
}
}
return result;
}
function getSignatureFromDeclaration(declaration: SignatureDeclaration): Signature {
let links = getNodeLinks(declaration);
if (!links.resolvedSignature) {
let classType = declaration.kind === SyntaxKind.Constructor ? getDeclaredTypeOfClassOrInterface((<ClassDeclaration>declaration.parent).symbol) : undefined;
let typeParameters = classType ? classType.localTypeParameters :
declaration.typeParameters ? getTypeParametersFromDeclaration(declaration.typeParameters) : undefined;
let parameters: Symbol[] = [];
let hasStringLiterals = false;
let minArgumentCount = -1;
for (let i = 0, n = declaration.parameters.length; i < n; i++) {
let param = declaration.parameters[i];
parameters.push(param.symbol);
if (param.type && param.type.kind === SyntaxKind.StringLiteral) {
hasStringLiterals = true;
}
if (minArgumentCount < 0) {
if (param.initializer || param.questionToken || param.dotDotDotToken) {
minArgumentCount = i;
}
}
}
if (minArgumentCount < 0) {
minArgumentCount = declaration.parameters.length;
}
let returnType: Type;
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)) {
let setter = <AccessorDeclaration>getDeclarationOfKind(declaration.symbol, SyntaxKind.SetAccessor);
returnType = getAnnotatedAccessorType(setter);
}
if (!returnType && nodeIsMissing((<FunctionLikeDeclaration>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;
let result: Signature[] = [];
for (let i = 0, len = symbol.declarations.length; i < len; i++) {
let node = symbol.declarations[i];
switch (node.kind) {
case SyntaxKind.FunctionType:
case SyntaxKind.ConstructorType:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
case SyntaxKind.Constructor:
case SyntaxKind.CallSignature:
case SyntaxKind.ConstructSignature:
case SyntaxKind.IndexSignature:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
// Don't include signature if node is the implementation of an overloaded function. A node is considered
// an implementation node if it has a body and the previous node is of the same kind and immediately
// precedes the implementation node (i.e. has the same parent and ends where the implementation starts).
if (i > 0 && (<FunctionLikeDeclaration>node).body) {
let previous = symbol.declarations[i - 1];
if (node.parent === previous.parent && node.kind === previous.kind && node.pos === previous.end) {
break;
}
}
result.push(getSignatureFromDeclaration(<SignatureDeclaration>node));
}
}
return result;
}
function getReturnTypeOfSignature(signature: Signature): Type {
if (!signature.resolvedReturnType) {
if (!pushTypeResolution(signature)) {
return unknownType;
}
let type: Type;
if (signature.target) {
type = instantiateType(getReturnTypeOfSignature(signature.target), signature.mapper);
}
else if (signature.unionSignatures) {
type = getUnionType(map(signature.unionSignatures, getReturnTypeOfSignature));
}
else {
type = getReturnTypeFromBody(<FunctionLikeDeclaration>signature.declaration);
}
if (!popTypeResolution()) {
type = anyType;
if (compilerOptions.noImplicitAny) {
let declaration = <Declaration>signature.declaration;
if (declaration.name) {
error(declaration.name, Diagnostics._0_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions, declarationNameToString(declaration.name));
}
else {
error(declaration, Diagnostics.Function_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions);
}
}
}
signature.resolvedReturnType = type;
}
return signature.resolvedReturnType;
}
function getRestTypeOfSignature(signature: Signature): Type {
if (signature.hasRestParameter) {
let type = getTypeOfSymbol(lastOrUndefined(signature.parameters));
if (type.flags & TypeFlags.Reference && (<TypeReference>type).target === globalArrayType) {
return (<TypeReference>type).typeArguments[0];
}
}
return anyType;
}
function getSignatureInstantiation(signature: Signature, typeArguments: Type[]): Signature {
return instantiateSignature(signature, createTypeMapper(signature.typeParameters, typeArguments), true);
}
function getErasedSignature(signature: Signature): Signature {
if (!signature.typeParameters) return signature;
if (!signature.erasedSignatureCache) {
if (signature.target) {
signature.erasedSignatureCache = instantiateSignature(getErasedSignature(signature.target), signature.mapper);
}
else {
signature.erasedSignatureCache = instantiateSignature(signature, createTypeEraser(signature.typeParameters), true);
}
}
return signature.erasedSignatureCache;
}
function getOrCreateTypeFromSignature(signature: Signature): ObjectType {
// There are two ways to declare a construct signature, one is by declaring a class constructor
// using the constructor keyword, and the other is declaring a bare construct signature in an
// object type literal or interface (using the new keyword). Each way of declaring a constructor
// will result in a different declaration kind.
if (!signature.isolatedSignatureType) {
let isConstructor = signature.declaration.kind === SyntaxKind.Constructor || signature.declaration.kind === SyntaxKind.ConstructSignature;
let type = <ResolvedType>createObjectType(TypeFlags.Anonymous | TypeFlags.FromSignature);
type.members = emptySymbols;
type.properties = emptyArray;
type.callSignatures = !isConstructor ? [signature] : emptyArray;
type.constructSignatures = isConstructor ? [signature] : emptyArray;
signature.isolatedSignatureType = type;
}
return signature.isolatedSignatureType;
}
function getIndexSymbol(symbol: Symbol): Symbol {
return symbol.members["__index"];
}
function getIndexDeclarationOfSymbol(symbol: Symbol, kind: IndexKind): SignatureDeclaration {
let syntaxKind = kind === IndexKind.Number ? SyntaxKind.NumberKeyword : SyntaxKind.StringKeyword;
let indexSymbol = getIndexSymbol(symbol);
if (indexSymbol) {
let len = indexSymbol.declarations.length;
for (let decl of indexSymbol.declarations) {
let node = <SignatureDeclaration>decl;
if (node.parameters.length === 1) {
let parameter = node.parameters[0];
if (parameter && parameter.type && parameter.type.kind === syntaxKind) {
return node;
}
}
}
}
return undefined;
}
function getIndexTypeOfSymbol(symbol: Symbol, kind: IndexKind): Type {
let declaration = getIndexDeclarationOfSymbol(symbol, kind);
return declaration
? declaration.type ? getTypeFromTypeNode(declaration.type) : anyType
: undefined;
}
function getConstraintOfTypeParameter(type: TypeParameter): Type {
if (!type.constraint) {
if (type.target) {
let targetConstraint = getConstraintOfTypeParameter(type.target);
type.constraint = targetConstraint ? instantiateType(targetConstraint, type.mapper) : noConstraintType;
}
else {
type.constraint = getTypeFromTypeNode((<TypeParameterDeclaration>getDeclarationOfKind(type.symbol, SyntaxKind.TypeParameter)).constraint);
}
}
return type.constraint === noConstraintType ? undefined : type.constraint;
}
function getParentSymbolOfTypeParameter(typeParameter: TypeParameter): Symbol {
return getSymbolOfNode(getDeclarationOfKind(typeParameter.symbol, SyntaxKind.TypeParameter).parent);
}
function getTypeListId(types: Type[]) {
switch (types.length) {
case 1:
return "" + types[0].id;
case 2:
return types[0].id + "," + types[1].id;
default:
let result = "";
for (let i = 0; i < types.length; i++) {
if (i > 0) {
result += ",";
}
result += types[i].id;
}
return result;
}
}
// This function is used to propagate widening flags when creating new object types references and union types.
// It is only necessary to do so if a constituent type might be the undefined type, the null type, or the type
// of an object literal (since those types have widening related information we need to track).
function getWideningFlagsOfTypes(types: Type[]): TypeFlags {
let result: TypeFlags = 0;
for (let type of types) {
result |= type.flags;
}
return result & TypeFlags.RequiresWidening;
}
function createTypeReference(target: GenericType, typeArguments: Type[]): TypeReference {
let id = getTypeListId(typeArguments);
let type = target.instantiations[id];
if (!type) {
let flags = TypeFlags.Reference | getWideningFlagsOfTypes(typeArguments);
type = target.instantiations[id] = <TypeReference>createObjectType(flags, target.symbol);
type.target = target;
type.typeArguments = typeArguments;
}
return type;
}
function isTypeParameterReferenceIllegalInConstraint(typeReferenceNode: TypeReferenceNode | ExpressionWithTypeArguments, typeParameterSymbol: Symbol): boolean {
let links = getNodeLinks(typeReferenceNode);
if (links.isIllegalTypeReferenceInConstraint !== undefined) {
return links.isIllegalTypeReferenceInConstraint;
}
// bubble up to the declaration
let currentNode: Node = typeReferenceNode;
// forEach === exists
while (!forEach(typeParameterSymbol.declarations, d => d.parent === currentNode.parent)) {
currentNode = currentNode.parent;
}
// if last step was made from the type parameter this means that path has started somewhere in constraint which is illegal
links.isIllegalTypeReferenceInConstraint = currentNode.kind === SyntaxKind.TypeParameter;
return links.isIllegalTypeReferenceInConstraint;
}
function checkTypeParameterHasIllegalReferencesInConstraint(typeParameter: TypeParameterDeclaration): void {
let typeParameterSymbol: Symbol;
function check(n: Node): void {
if (n.kind === SyntaxKind.TypeReference && (<TypeReferenceNode>n).typeName.kind === SyntaxKind.Identifier) {
let links = getNodeLinks(n);
if (links.isIllegalTypeReferenceInConstraint === undefined) {
let symbol = resolveName(typeParameter, (<Identifier>(<TypeReferenceNode>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 getTypeFromTypeReferenceOrExpressionWithTypeArguments(node: TypeReferenceNode | ExpressionWithTypeArguments): Type {
let links = getNodeLinks(node);
if (!links.resolvedType) {
let type: Type;
// We don't currently support heritage clauses with complex expressions in them.
// For these cases, we just set the type to be the unknownType.
if (node.kind !== SyntaxKind.ExpressionWithTypeArguments || isSupportedExpressionWithTypeArguments(<ExpressionWithTypeArguments>node)) {
let typeNameOrExpression = node.kind === SyntaxKind.TypeReference
? (<TypeReferenceNode>node).typeName
: (<ExpressionWithTypeArguments>node).expression;
let symbol = resolveEntityName(typeNameOrExpression, SymbolFlags.Type);
if (symbol) {
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) {
// In a type reference, the outer type parameters of the referenced class or interface are automatically
// supplied as type arguments and the type reference only specifies arguments for the local type parameters
// of the class or interface.
let localTypeParameters = (<InterfaceType>type).localTypeParameters;
let expectedTypeArgCount = localTypeParameters ? localTypeParameters.length : 0;
let typeArgCount = node.typeArguments ? node.typeArguments.length : 0;
if (typeArgCount === expectedTypeArgCount) {
// When no type arguments are expected we already have the right type because all outer type parameters
// have themselves as default type arguments.
if (typeArgCount) {
type = createTypeReference(<GenericType>type, concatenate((<InterfaceType>type).outerTypeParameters,
map(node.typeArguments, getTypeFromTypeNode)));
}
}
else {
error(node, Diagnostics.Generic_type_0_requires_1_type_argument_s, typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType), expectedTypeArgCount);
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 {
let links = getNodeLinks(node);
if (!links.resolvedType) {
// TypeScript 1.0 spec (April 2014): 3.6.3
// The expression is processed as an identifier expression (section 4.3)
// or property access expression(section 4.10),
// the widened type(section 3.9) of which becomes the result.
links.resolvedType = getWidenedType(checkExpressionOrQualifiedName(node.exprName));
}
return links.resolvedType;
}
function getTypeOfGlobalSymbol(symbol: Symbol, arity: number): ObjectType {
function getTypeDeclaration(symbol: Symbol): Declaration {
let declarations = symbol.declarations;
for (let declaration of declarations) {
switch (declaration.kind) {
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.EnumDeclaration:
return declaration;
}
}
}
if (!symbol) {
return arity ? emptyGenericType : emptyObjectType;
}
let type = getDeclaredTypeOfSymbol(symbol);
if (!(type.flags & TypeFlags.ObjectType)) {
error(getTypeDeclaration(symbol), Diagnostics.Global_type_0_must_be_a_class_or_interface_type, symbol.name);
return arity ? emptyGenericType : emptyObjectType;
}
if (((<InterfaceType>type).typeParameters ? (<InterfaceType>type).typeParameters.length : 0) !== arity) {
error(getTypeDeclaration(symbol), Diagnostics.Global_type_0_must_have_1_type_parameter_s, symbol.name, arity);
return arity ? emptyGenericType : emptyObjectType;
}
return <ObjectType>type;
}
function getGlobalValueSymbol(name: string): Symbol {
return getGlobalSymbol(name, SymbolFlags.Value, Diagnostics.Cannot_find_global_value_0);
}
function getGlobalTypeSymbol(name: string): Symbol {
return getGlobalSymbol(name, SymbolFlags.Type, Diagnostics.Cannot_find_global_type_0);
}
function getGlobalSymbol(name: string, meaning: SymbolFlags, diagnostic: DiagnosticMessage): Symbol {
return resolveName(undefined, name, meaning, diagnostic, name);
}
function getGlobalType(name: string, arity = 0): ObjectType {
return getTypeOfGlobalSymbol(getGlobalTypeSymbol(name), arity);
}
function getGlobalESSymbolConstructorSymbol() {
return globalESSymbolConstructorSymbol || (globalESSymbolConstructorSymbol = getGlobalValueSymbol("Symbol"));
}
/**
* Instantiates a global type that is generic with some element type, and returns that instantiation.
*/
function createTypeFromGenericGlobalType(genericGlobalType: GenericType, elementType: Type): Type {
return <ObjectType>genericGlobalType !== emptyGenericType ? createTypeReference(genericGlobalType, [elementType]) : emptyObjectType;
}
function createIterableType(elementType: Type): Type {
return createTypeFromGenericGlobalType(globalIterableType, elementType);
}
function createIterableIteratorType(elementType: Type): Type {
return createTypeFromGenericGlobalType(globalIterableIteratorType, elementType);
}
function createArrayType(elementType: Type): Type {
return createTypeFromGenericGlobalType(globalArrayType, elementType);
}
function getTypeFromArrayTypeNode(node: ArrayTypeNode): Type {
let links = getNodeLinks(node);
if (!links.resolvedType) {
links.resolvedType = createArrayType(getTypeFromTypeNode(node.elementType));
}
return links.resolvedType;
}
function createTupleType(elementTypes: Type[]) {
let id = getTypeListId(elementTypes);
let type = tupleTypes[id];
if (!type) {
type = tupleTypes[id] = <TupleType>createObjectType(TypeFlags.Tuple);
type.elementTypes = elementTypes;
}
return type;
}
function getTypeFromTupleTypeNode(node: TupleTypeNode): Type {
let links = getNodeLinks(node);
if (!links.resolvedType) {
links.resolvedType = createTupleType(map(node.elementTypes, getTypeFromTypeNode));
}
return links.resolvedType;
}
function addTypeToSortedSet(sortedSet: Type[], type: Type) {
if (type.flags & TypeFlags.Union) {
addTypesToSortedSet(sortedSet, (<UnionType>type).types);
}
else {
let i = 0;
let 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 (let type of types) {
addTypeToSortedSet(sortedTypes, type);
}
}
function isSubtypeOfAny(candidate: Type, types: Type[]): boolean {
for (let type of types) {
if (candidate !== type && isTypeSubtypeOf(candidate, type)) {
return true;
}
}
return false;
}
function removeSubtypes(types: Type[]) {
let i = types.length;
while (i > 0) {
i--;
if (isSubtypeOfAny(types[i], types)) {
types.splice(i, 1);
}
}
}
function containsAnyType(types: Type[]) {
for (let type of types) {
if (type.flags & TypeFlags.Any) {
return true;
}
}
return false;
}
function removeAllButLast(types: Type[], typeToRemove: Type) {
let i = types.length;
while (i > 0 && types.length > 1) {
i--;
if (types[i] === typeToRemove) {
types.splice(i, 1);
}
}
}
// The noSubtypeReduction flag is there because it isn't possible to always do subtype reduction. The flag
// is true when creating a union type from a type node and when instantiating a union type. In both of those
// cases subtype reduction has to be deferred to properly support recursive union types. For example, a
// type alias of the form "type Item = string | (() => Item)" cannot be reduced during its declaration.
function getUnionType(types: Type[], noSubtypeReduction?: boolean): Type {
if (types.length === 0) {
return emptyObjectType;
}
let 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];
}
let id = getTypeListId(sortedTypes);
let type = unionTypes[id];
if (!type) {
type = unionTypes[id] = <UnionType>createObjectType(TypeFlags.Union | getWideningFlagsOfTypes(sortedTypes));
type.types = sortedTypes;
type.reducedType = noSubtypeReduction ? undefined : type;
}
return type;
}
// Subtype reduction is basically an optimization we do to avoid excessively large union types, which take longer
// to process and look strange in quick info and error messages. Semantically there is no difference between the
// reduced type and the type itself. So, when we detect a circularity we simply say that the reduced type is the
// type itself.
function getReducedTypeOfUnionType(type: UnionType): Type {
if (!type.reducedType) {
type.reducedType = circularType;
let reducedType = getUnionType(type.types, /*noSubtypeReduction*/ false);
if (type.reducedType === circularType) {
type.reducedType = reducedType;
}
}
else if (type.reducedType === circularType) {
type.reducedType = type;
}
return type.reducedType;
}
function getTypeFromUnionTypeNode(node: UnionTypeNode): Type {
let links = getNodeLinks(node);
if (!links.resolvedType) {
links.resolvedType = getUnionType(map(node.types, getTypeFromTypeNode), /*noSubtypeReduction*/ true);
}
return links.resolvedType;
}
function getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node: Node): Type {
let links = getNodeLinks(node);
if (!links.resolvedType) {
// Deferred resolution of members is handled by resolveObjectTypeMembers
links.resolvedType = createObjectType(TypeFlags.Anonymous, node.symbol);
}
return links.resolvedType;
}
function getStringLiteralType(node: StringLiteral): StringLiteralType {
if (hasProperty(stringLiteralTypes, node.text)) {
return stringLiteralTypes[node.text];
}
let type = stringLiteralTypes[node.text] = <StringLiteralType>createType(TypeFlags.StringLiteral);
type.text = getTextOfNode(node);
return type;
}
function getTypeFromStringLiteral(node: StringLiteral): Type {
let links = getNodeLinks(node);
if (!links.resolvedType) {
links.resolvedType = getStringLiteralType(node);
}
return links.resolvedType;
}
function getTypeFromTypeNode(node: TypeNode): Type {
switch (node.kind) {
case SyntaxKind.AnyKeyword:
return anyType;
case SyntaxKind.StringKeyword:
return stringType;
case SyntaxKind.NumberKeyword:
return numberType;
case SyntaxKind.BooleanKeyword:
return booleanType;
case SyntaxKind.SymbolKeyword:
return esSymbolType;
case SyntaxKind.VoidKeyword:
return voidType;
case SyntaxKind.StringLiteral:
return getTypeFromStringLiteral(<StringLiteral>node);
case SyntaxKind.TypeReference:
return getTypeFromTypeReferenceOrExpressionWithTypeArguments(<TypeReferenceNode>node);
case SyntaxKind.ExpressionWithTypeArguments:
return getTypeFromTypeReferenceOrExpressionWithTypeArguments(<ExpressionWithTypeArguments>node);
case SyntaxKind.TypeQuery:
return getTypeFromTypeQueryNode(<TypeQueryNode>node);
case SyntaxKind.ArrayType:
return getTypeFromArrayTypeNode(<ArrayTypeNode>node);
case SyntaxKind.TupleType:
return getTypeFromTupleTypeNode(<TupleTypeNode>node);
case SyntaxKind.UnionType:
return getTypeFromUnionTypeNode(<UnionTypeNode>node);
case SyntaxKind.ParenthesizedType:
return getTypeFromTypeNode((<ParenthesizedTypeNode>node).type);
case SyntaxKind.FunctionType:
case SyntaxKind.ConstructorType:
case SyntaxKind.TypeLiteral:
return getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node);
// This function assumes that an identifier or qualified name is a type expression
// Callers should first ensure this by calling isTypeNode
case SyntaxKind.Identifier:
case SyntaxKind.QualifiedName:
let symbol = getSymbolInfo(node);
return symbol && getDeclaredTypeOfSymbol(symbol);
default:
return unknownType;
}
}
function instantiateList<T>(items: T[], mapper: TypeMapper, instantiator: (item: T, mapper: TypeMapper) => T): T[] {
if (items && items.length) {
let result: T[] = [];
for (let v of items) {
result.push(instantiator(v, mapper));
}
return result;
}
return items;
}
function createUnaryTypeMapper(source: Type, target: Type): TypeMapper {
return t => t === source ? target : t;
}
function createBinaryTypeMapper(source1: Type, target1: Type, source2: Type, target2: Type): TypeMapper {
return t => t === source1 ? target1 : t === source2 ? target2 : t;
}
function createTypeMapper(sources: Type[], targets: Type[]): TypeMapper {
switch (sources.length) {
case 1: return createUnaryTypeMapper(sources[0], targets[0]);
case 2: return createBinaryTypeMapper(sources[0], targets[0], sources[1], targets[1]);
}
return t => {
for (let i = 0; i < sources.length; i++) {
if (t === sources[i]) {
return targets[i];
}
}
return t;
};
}
function createUnaryTypeEraser(source: Type): TypeMapper {
return t => t === source ? anyType : t;
}
function createBinaryTypeEraser(source1: Type, source2: Type): TypeMapper {
return t => t === source1 || t === source2 ? anyType : t;
}
function createTypeEraser(sources: Type[]): TypeMapper {
switch (sources.length) {
case 1: return createUnaryTypeEraser(sources[0]);
case 2: return createBinaryTypeEraser(sources[0], sources[1]);
}
return t => {
for (let source of sources) {
if (t === source) {
return anyType;
}
}
return t;
};
}
function createInferenceMapper(context: InferenceContext): TypeMapper {
return t => {
for (let i = 0; i < context.typeParameters.length; i++) {
if (t === context.typeParameters[i]) {
context.inferences[i].isFixed = true;
return getInferredType(context, i);
}
}
return t;
}
}
function identityMapper(type: Type): Type {
return type;
}
function combineTypeMappers(mapper1: TypeMapper, mapper2: TypeMapper): TypeMapper {
return t => instantiateType(mapper1(t), mapper2);
}
function instantiateTypeParameter(typeParameter: TypeParameter, mapper: TypeMapper): TypeParameter {
let result = <TypeParameter>createType(TypeFlags.TypeParameter);
result.symbol = typeParameter.symbol;
if (typeParameter.constraint) {
result.constraint = instantiateType(typeParameter.constraint, mapper);
}
else {
result.target = typeParameter;
result.mapper = mapper;
}
return result;
}
function instantiateSignature(signature: Signature, mapper: TypeMapper, eraseTypeParameters?: boolean): Signature {
let freshTypeParameters: TypeParameter[];
if (signature.typeParameters && !eraseTypeParameters) {
freshTypeParameters = instantiateList(signature.typeParameters, mapper, instantiateTypeParameter);
mapper = combineTypeMappers(createTypeMapper(signature.typeParameters, freshTypeParameters), mapper);
}
let 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) {
let links = getSymbolLinks(symbol);
// If symbol being instantiated is itself a instantiation, fetch the original target and combine the
// type mappers. This ensures that original type identities are properly preserved and that aliases
// always reference a non-aliases.
symbol = links.target;
mapper = combineTypeMappers(links.mapper, mapper);
}
// Keep the flags from the symbol we're instantiating. Mark that is instantiated, and
// also transient so that we can just store data on it directly.
let result = <TransientSymbol>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 {
// If this type has already been instantiated using this mapper, returned the cached result. This guards against
// infinite instantiations of cyclic types, e.g. "var x: { a: T, b: typeof x };"
if (mapper.mappings) {
let cached = <ObjectType>mapper.mappings[type.id];
if (cached) {
return cached;
}
}
else {
mapper.mappings = {};
}
// Instantiate the given type using the given mapper and cache the result
let result = <ResolvedType>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);
let stringIndexType = getIndexTypeOfType(type, IndexKind.String);
let numberIndexType = getIndexTypeOfType(type, IndexKind.Number);
if (stringIndexType) result.stringIndexType = instantiateType(stringIndexType, mapper);
if (numberIndexType) result.numberIndexType = instantiateType(numberIndexType, mapper);
mapper.mappings[type.id] = result;
return result;
}
function instantiateType(type: Type, mapper: TypeMapper): Type {
if (mapper !== identityMapper) {
if (type.flags & TypeFlags.TypeParameter) {
return mapper(<TypeParameter>type);
}
if (type.flags & TypeFlags.Anonymous) {
return type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) ?
instantiateAnonymousType(<ObjectType>type, mapper) : type;
}
if (type.flags & TypeFlags.Reference) {
return createTypeReference((<TypeReference>type).target, instantiateList((<TypeReference>type).typeArguments, mapper, instantiateType));
}
if (type.flags & TypeFlags.Tuple) {
return createTupleType(instantiateList((<TupleType>type).elementTypes, mapper, instantiateType));
}
if (type.flags & TypeFlags.Union) {
return getUnionType(instantiateList((<UnionType>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(<FunctionExpression>node);
case SyntaxKind.ObjectLiteralExpression:
return forEach((<ObjectLiteralExpression>node).properties, isContextSensitive);
case SyntaxKind.ArrayLiteralExpression:
return forEach((<ArrayLiteralExpression>node).elements, isContextSensitive);
case SyntaxKind.ConditionalExpression:
return isContextSensitive((<ConditionalExpression>node).whenTrue) ||
isContextSensitive((<ConditionalExpression>node).whenFalse);
case SyntaxKind.BinaryExpression:
return (<BinaryExpression>node).operatorToken.kind === SyntaxKind.BarBarToken &&
(isContextSensitive((<BinaryExpression>node).left) || isContextSensitive((<BinaryExpression>node).right));
case SyntaxKind.PropertyAssignment:
return isContextSensitive((<PropertyAssignment>node).initializer);
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
return isContextSensitiveFunctionLikeDeclaration(<MethodDeclaration>node);
case SyntaxKind.ParenthesizedExpression:
return isContextSensitive((<ParenthesizedExpression>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) {
let resolved = resolveObjectOrUnionTypeMembers(<ObjectType>type);
if (resolved.constructSignatures.length) {
let result = <ResolvedType>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
let subtypeRelation: Map<RelationComparisonResult> = {};
let assignableRelation: Map<RelationComparisonResult> = {};
let identityRelation: Map<RelationComparisonResult> = {};
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 {
let sourceType = getOrCreateTypeFromSignature(source);
let targetType = getOrCreateTypeFromSignature(target);
return checkTypeRelatedTo(sourceType, targetType, assignableRelation, /*errorNode*/ undefined);
}
function checkTypeRelatedTo(
source: Type,
target: Type,
relation: Map<RelationComparisonResult>,
errorNode: Node,
headMessage?: DiagnosticMessage,
containingMessageChain?: DiagnosticMessageChain): boolean {
let errorInfo: DiagnosticMessageChain;
let sourceStack: ObjectType[];
let targetStack: ObjectType[];
let maybeStack: Map<RelationComparisonResult>[];
let expandingFlags: number;
let depth = 0;
let overflow = false;
let elaborateErrors = false;
Debug.assert(relation !== identityRelation || !errorNode, "no error reporting in identity checking");
let result = isRelatedTo(source, target, errorNode !== undefined, headMessage);
if (overflow) {
error(errorNode, Diagnostics.Excessive_stack_depth_comparing_types_0_and_1, typeToString(source), typeToString(target));
}
else if (errorInfo) {
// If we already computed this relation, but in a context where we didn't want to report errors (e.g. overload resolution),
// then we'll only have a top-level error (e.g. 'Class X does not implement interface Y') without any details. If this happened,
// request a recompuation to get a complete error message. This will be skipped if we've already done this computation in a context
// where errors were being reported.
if (errorInfo.next === undefined) {
errorInfo = undefined;
elaborateErrors = true;
isRelatedTo(source, target, errorNode !== undefined, headMessage);
}
if (containingMessageChain) {
errorInfo = concatenateDiagnosticMessageChains(containingMessageChain, errorInfo);
}
diagnostics.add(createDiagnosticForNodeFromMessageChain(errorNode, errorInfo));
}
return result !== Ternary.False;
function reportError(message: DiagnosticMessage, arg0?: string, arg1?: string, arg2?: string): void {
errorInfo = chainDiagnosticMessages(errorInfo, message, arg0, arg1, arg2);
}
// Compare two types and return
// Ternary.True if they are related with no assumptions,
// Ternary.Maybe if they are related with assumptions of other relationships, or
// Ternary.False if they are not related.
function isRelatedTo(source: Type, target: Type, reportErrors?: boolean, headMessage?: DiagnosticMessage): Ternary {
let result: Ternary;
// both types are the same - covers 'they are the same primitive type or both are Any' or the same type parameter cases
if (source === target) return Ternary.True;
if (relation !== identityRelation) {
if (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;
}
}
let saveErrorInfo = errorInfo;
if (source.flags & TypeFlags.Union || target.flags & TypeFlags.Union) {
if (relation === identityRelation) {
if (source.flags & TypeFlags.Union && target.flags & TypeFlags.Union) {
if (result = unionTypeRelatedToUnionType(<UnionType>source, <UnionType>target)) {
if (result &= unionTypeRelatedToUnionType(<UnionType>target, <UnionType>source)) {
return result;
}
}
}
else if (source.flags & TypeFlags.Union) {
if (result = unionTypeRelatedToType(<UnionType>source, target, reportErrors)) {
return result;
}
}
else {
if (result = unionTypeRelatedToType(<UnionType>target, source, reportErrors)) {
return result;
}
}
}
else {
if (source.flags & TypeFlags.Union) {
if (result = unionTypeRelatedToType(<UnionType>source, target, reportErrors)) {
return result;
}
}
else {
if (result = typeRelatedToUnionType(source, <UnionType>target, reportErrors)) {
return result;
}
}
}
}
else if (source.flags & TypeFlags.TypeParameter && target.flags & TypeFlags.TypeParameter) {
if (result = typeParameterRelatedTo(<TypeParameter>source, <TypeParameter>target, reportErrors)) {
return result;
}
}
else if (source.flags & TypeFlags.Reference && target.flags & TypeFlags.Reference && (<TypeReference>source).target === (<TypeReference>target).target) {
// We have type references to same target type, see if relationship holds for all type arguments
if (result = typesRelatedTo((<TypeReference>source).typeArguments, (<TypeReference>target).typeArguments, reportErrors)) {
return result;
}
}
// Even if relationship doesn't hold for unions, type parameters, or generic type references,
// it may hold in a structural comparison.
// Report structural errors only if we haven't reported any errors yet
let reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo;
// identity relation does not use apparent type
let sourceOrApparentType = relation === identityRelation ? source : getApparentType(source);
if (sourceOrApparentType.flags & TypeFlags.ObjectType && target.flags & TypeFlags.ObjectType) {
if (result = objectTypeRelatedTo(sourceOrApparentType, <ObjectType>target, reportStructuralErrors)) {
errorInfo = saveErrorInfo;
return result;
}
}
else if (source.flags & TypeFlags.TypeParameter && sourceOrApparentType.flags & TypeFlags.Union) {
// We clear the errors first because the following check often gives a better error than
// the union comparison above if it is applicable.
errorInfo = saveErrorInfo;
if (result = isRelatedTo(sourceOrApparentType, target, reportErrors)) {
return result;
}
}
if (reportErrors) {
headMessage = headMessage || Diagnostics.Type_0_is_not_assignable_to_type_1;
let sourceType = typeToString(source);
let 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 {
let result = Ternary.True;
let sourceTypes = source.types;
for (let sourceType of sourceTypes) {
let related = typeRelatedToUnionType(sourceType, target, false);
if (!related) {
return Ternary.False;
}
result &= related;
}
return result;
}
function typeRelatedToUnionType(source: Type, target: UnionType, reportErrors: boolean): Ternary {
let targetTypes = target.types;
for (let i = 0, len = targetTypes.length; i < len; i++) {
let 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 {
let result = Ternary.True;
let sourceTypes = source.types;
for (let sourceType of sourceTypes) {
let related = isRelatedTo(sourceType, target, reportErrors);
if (!related) {
return Ternary.False;
}
result &= related;
}
return result;
}
function typesRelatedTo(sources: Type[], targets: Type[], reportErrors: boolean): Ternary {
let result = Ternary.True;
for (let i = 0, len = sources.length; i < len; i++) {
let 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) {
let constraint = getConstraintOfTypeParameter(source);
if (constraint === target) return Ternary.True;
if (!(constraint && constraint.flags & TypeFlags.TypeParameter)) break;
source = <TypeParameter>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): Ternary {
if (overflow) {
return Ternary.False;
}
let id = relation !== identityRelation || source.id < target.id ? source.id + "," + target.id : target.id + "," + source.id;
let related = relation[id];
//let 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 (let 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++;
let saveExpandingFlags = expandingFlags;
if (!(expandingFlags & 1) && isDeeplyNestedGeneric(source, sourceStack)) expandingFlags |= 1;
if (!(expandingFlags & 2) && isDeeplyNestedGeneric(target, targetStack)) expandingFlags |= 2;
let result: Ternary;
if (expandingFlags === 3) {
result = Ternary.Maybe;
}
else {
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) {
let maybeCache = maybeStack[depth];
// If result is definitely true, copy assumptions to global cache, else copy to next level up
let 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) {
let target = (<TypeReference>type).target;
let count = 0;
for (let i = 0; i < depth; i++) {
let t = stack[i];
if (t.flags & TypeFlags.Reference && (<TypeReference>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);
}
let result = Ternary.True;
let properties = getPropertiesOfObjectType(target);
let requireOptionalProperties = relation === subtypeRelation && !(source.flags & TypeFlags.ObjectLiteral);
for (let targetProp of properties) {
let 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)) {
let sourceFlags = getDeclarationFlagsFromSymbol(sourceProp);
let 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) {
let sourceDeclaredInClass = sourceProp.parent && sourceProp.parent.flags & SymbolFlags.Class;
let sourceClass = sourceDeclaredInClass ? <InterfaceType>getDeclaredTypeOfSymbol(sourceProp.parent) : undefined;
let targetClass = <InterfaceType>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;
}
let 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 {
let sourceProperties = getPropertiesOfObjectType(source);
let targetProperties = getPropertiesOfObjectType(target);
if (sourceProperties.length !== targetProperties.length) {
return Ternary.False;
}
let result = Ternary.True;
for (let sourceProp of sourceProperties) {
let targetProp = getPropertyOfObjectType(target, sourceProp.name);
if (!targetProp) {
return Ternary.False;
}
let 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;
}
let sourceSignatures = getSignaturesOfType(source, kind);
let targetSignatures = getSignaturesOfType(target, kind);
let result = Ternary.True;
let saveErrorInfo = errorInfo;
outer: for (let t of targetSignatures) {
if (!t.hasStringLiterals || target.flags & TypeFlags.FromSignature) {
let localErrors = reportErrors;
for (let s of sourceSignatures) {
if (!s.hasStringLiterals || source.flags & TypeFlags.FromSignature) {
let 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;
}
let sourceMax = source.parameters.length;
let targetMax = target.parameters.length;
let 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);
let result = Ternary.True;
for (let i = 0; i < checkCount; i++) {
let s = i < sourceMax ? getTypeOfSymbol(source.parameters[i]) : getRestTypeOfSignature(source);
let t = i < targetMax ? getTypeOfSymbol(target.parameters[i]) : getRestTypeOfSignature(target);
let saveErrorInfo = errorInfo;
let 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;
}
let t = getReturnTypeOfSignature(target);
if (t === voidType) return result;
let s = getReturnTypeOfSignature(source);
return result & isRelatedTo(s, t, reportErrors);
}
function signaturesIdenticalTo(source: ObjectType, target: ObjectType, kind: SignatureKind): Ternary {
let sourceSignatures = getSignaturesOfType(source, kind);
let targetSignatures = getSignaturesOfType(target, kind);
if (sourceSignatures.length !== targetSignatures.length) {
return Ternary.False;
}
let result = Ternary.True;
for (let i = 0, len = sourceSignatures.length; i < len; ++i) {
let 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);
}
let targetType = getIndexTypeOfType(target, IndexKind.String);
if (targetType) {
let sourceType = getIndexTypeOfType(source, IndexKind.String);
if (!sourceType) {
if (reportErrors) {
reportError(Diagnostics.Index_signature_is_missing_in_type_0, typeToString(source));
}
return Ternary.False;
}
let 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);
}
let targetType = getIndexTypeOfType(target, IndexKind.Number);
if (targetType) {
let sourceStringType = getIndexTypeOfType(source, IndexKind.String);
let sourceNumberType = getIndexTypeOfType(source, IndexKind.Number);
if (!(sourceStringType || sourceNumberType)) {
if (reportErrors) {
reportError(Diagnostics.Index_signature_is_missing_in_type_0, typeToString(source));
}
return Ternary.False;
}
let related: Ternary;
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
related = isRelatedTo(sourceStringType, targetType, false) || isRelatedTo(sourceNumberType, targetType, reportErrors);
}
else {
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 {
let targetType = getIndexTypeOfType(target, indexKind);
let 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;
}
let sourcePropAccessibility = getDeclarationFlagsFromSymbol(sourceProp) & (NodeFlags.Private | NodeFlags.Protected);
let 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;
}
let result = Ternary.True;
if (source.typeParameters && target.typeParameters) {
if (source.typeParameters.length !== target.typeParameters.length) {
return Ternary.False;
}
for (let i = 0, len = source.typeParameters.length; i < len; ++i) {
let 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 (let i = 0, len = source.parameters.length; i < len; i++) {
let s = source.hasRestParameter && i === len - 1 ? getRestTypeOfSignature(source) : getTypeOfSymbol(source.parameters[i]);
let t = target.hasRestParameter && i === len - 1 ? getRestTypeOfSignature(target) : getTypeOfSymbol(target.parameters[i]);
let 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 (let type of types) {
if (candidate !== type && !isTypeSubtypeOf(type, 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 {
// The downfallType/bestSupertypeDownfallType is the first type that caused a particular candidate
// to not be the common supertype. So if it weren't for this one downfallType (and possibly others),
// the type in question could have been the common supertype.
let bestSupertype: Type;
let bestSupertypeDownfallType: Type;
let bestSupertypeScore = 0;
for (let i = 0; i < types.length; i++) {
let score = 0;
let downfallType: Type = undefined;
for (let j = 0; j < types.length; j++) {
if (isTypeSubtypeOf(types[j], types[i])) {
score++;
}
else if (!downfallType) {
downfallType = types[j];
}
}
Debug.assert(!!downfallType, "If there is no common supertype, each type should have a downfallType");
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 && (<TypeReference>type).target === globalArrayType;
}
function isArrayLikeType(type: Type): boolean {
// A type is array-like if it is not the undefined or null type and if it is assignable to any[]
return !(type.flags & (TypeFlags.Undefined | TypeFlags.Null)) && isTypeAssignableTo(type, anyArrayType);
}
function isTupleLikeType(type: Type): boolean {
return !!getPropertyOfType(type, "0");
}
/**
* Check if a Type was written as a tuple type literal.
* Prefer using isTupleLikeType() unless the use of `elementTypes` is required.
*/
function isTupleType(type: Type): boolean {
return (type.flags & TypeFlags.Tuple) && !!(<TupleType>type).elementTypes;
}
function getWidenedTypeOfObjectLiteral(type: Type): Type {
let properties = getPropertiesOfObjectType(type);
let members: SymbolTable = {};
forEach(properties, p => {
let propType = getTypeOfSymbol(p);
let widenedType = getWidenedType(propType);
if (propType !== widenedType) {
let symbol = <TransientSymbol>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;
});
let stringIndexType = getIndexTypeOfType(type, IndexKind.String);
let 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((<UnionType>type).types, getWidenedType));
}
if (isArrayType(type)) {
return createArrayType(getWidenedType((<TypeReference>type).typeArguments[0]));
}
}
return type;
}
function reportWideningErrorsInType(type: Type): boolean {
if (type.flags & TypeFlags.Union) {
let errorReported = false;
forEach((<UnionType>type).types, t => {
if (reportWideningErrorsInType(t)) {
errorReported = true;
}
});
return errorReported;
}
if (isArrayType(type)) {
return reportWideningErrorsInType((<TypeReference>type).typeArguments[0]);
}
if (type.flags & TypeFlags.ObjectLiteral) {
let errorReported = false;
forEach(getPropertiesOfObjectType(type), p => {
let 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) {
let typeAsString = typeToString(getWidenedType(type));
let diagnostic: DiagnosticMessage;
switch (declaration.kind) {
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.PropertySignature:
diagnostic = Diagnostics.Member_0_implicitly_has_an_1_type;
break;
case SyntaxKind.Parameter:
diagnostic = (<ParameterDeclaration>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;
}
diagnostic = Diagnostics._0_which_lacks_return_type_annotation_implicitly_has_an_1_return_type;
break;
default:
diagnostic = Diagnostics.Variable_0_implicitly_has_an_1_type;
}
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) {
let sourceMax = source.parameters.length;
let targetMax = target.parameters.length;
let 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 (let i = 0; i < count; i++) {
let s = i < sourceMax ? getTypeOfSymbol(source.parameters[i]) : getRestTypeOfSignature(source);
let t = i < targetMax ? getTypeOfSymbol(target.parameters[i]) : getRestTypeOfSignature(target);
callback(s, t);
}
}
function createInferenceContext(typeParameters: TypeParameter[], inferUnionTypes: boolean): InferenceContext {
let inferences: TypeInferences[] = [];
for (let unused of typeParameters) {
inferences.push({ primary: undefined, secondary: undefined, isFixed: false });
}
return {
typeParameters,
inferUnionTypes,
inferences,
inferredTypes: new Array(typeParameters.length),
};
}
function inferTypes(context: InferenceContext, source: Type, target: Type) {
let sourceStack: Type[];
let targetStack: Type[];
let depth = 0;
let inferiority = 0;
inferFromTypes(source, target);
function isInProcess(source: Type, target: Type) {
for (let 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) {
let target = (<TypeReference>type).target;
let count = 0;
for (let i = 0; i < depth; i++) {
let t = stack[i];
if (t.flags & TypeFlags.Reference && (<TypeReference>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
let typeParameters = context.typeParameters;
for (let i = 0; i < typeParameters.length; i++) {
if (target === typeParameters[i]) {
let inferences = context.inferences[i];
if (!inferences.isFixed) {
// Any inferences that are made to a type parameter in a union type are inferior
// to inferences made to a flat (non-union) type. This is because if we infer to
// T | string[], we really don't know if we should be inferring to T or not (because
// the correct constituent on the target side could be string[]). Therefore, we put
// such inferior inferences into a secondary bucket, and only use them if the primary
// bucket is empty.
let candidates = inferiority ?
inferences.secondary || (inferences.secondary = []) :
inferences.primary || (inferences.primary = []);
if (!contains(candidates, source)) {
candidates.push(source);
}
}
return;
}
}
}
else if (source.flags & TypeFlags.Reference && target.flags & TypeFlags.Reference && (<TypeReference>source).target === (<TypeReference>target).target) {
// If source and target are references to the same generic type, infer from type arguments
let sourceTypes = (<TypeReference>source).typeArguments;
let targetTypes = (<TypeReference>target).typeArguments;
for (let i = 0; i < sourceTypes.length; i++) {
inferFromTypes(sourceTypes[i], targetTypes[i]);
}
}
else if (target.flags & TypeFlags.Union) {
let targetTypes = (<UnionType>target).types;
let typeParameterCount = 0;
let typeParameter: TypeParameter;
// First infer to each type in union that isn't a type parameter
for (let t of targetTypes) {
if (t.flags & TypeFlags.TypeParameter && contains(context.typeParameters, t)) {
typeParameter = <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
let sourceTypes = (<UnionType>source).types;
for (let sourceType of sourceTypes) {
inferFromTypes(sourceType, 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) {
let properties = getPropertiesOfObjectType(target);
for (let targetProp of properties) {
let sourceProp = getPropertyOfObjectType(source, targetProp.name);
if (sourceProp) {
inferFromTypes(getTypeOfSymbol(sourceProp), getTypeOfSymbol(targetProp));
}
}
}
function inferFromSignatures(source: Type, target: Type, kind: SignatureKind) {
let sourceSignatures = getSignaturesOfType(source, kind);
let targetSignatures = getSignaturesOfType(target, kind);
let sourceLen = sourceSignatures.length;
let targetLen = targetSignatures.length;
let len = sourceLen < targetLen ? sourceLen : targetLen;
for (let 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) {
let targetIndexType = getIndexTypeOfType(target, targetKind);
if (targetIndexType) {
let sourceIndexType = getIndexTypeOfType(source, sourceKind);
if (sourceIndexType) {
inferFromTypes(sourceIndexType, targetIndexType);
}
}
}
}
function getInferenceCandidates(context: InferenceContext, index: number): Type[] {
let inferences = context.inferences[index];
return inferences.primary || inferences.secondary || emptyArray;
}
function getInferredType(context: InferenceContext, index: number): Type {
let inferredType = context.inferredTypes[index];
let inferenceSucceeded: boolean;
if (!inferredType) {
let inferences = getInferenceCandidates(context, index);
if (inferences.length) {
// Infer widened union or supertype, or the unknown type for no common supertype
let unionOrSuperType = context.inferUnionTypes ? getUnionType(inferences) : getCommonSupertype(inferences);
inferredType = unionOrSuperType ? getWidenedType(unionOrSuperType) : unknownType;
inferenceSucceeded = !!unionOrSuperType;
}
else {
// Infer the empty object type when no inferences were made. It is important to remember that
// in this case, inference still succeeds, meaning there is no error for not having inference
// candidates. An inference error only occurs when there are *conflicting* candidates, i.e.
// candidates with no common supertype.
inferredType = emptyObjectType;
inferenceSucceeded = true;
}
// Only do the constraint check if inference succeeded (to prevent cascading errors)
if (inferenceSucceeded) {
let constraint = getConstraintOfTypeParameter(context.typeParameters[index]);
inferredType = constraint && !isTypeAssignableTo(inferredType, constraint) ? constraint : inferredType;
}
else if (context.failedTypeParameterIndex === undefined || context.failedTypeParameterIndex > index) {
// If inference failed, it is necessary to record the index of the failed type parameter (the one we are on).
// It might be that inference has already failed on a later type parameter on a previous call to inferTypeArguments.
// So if this failure is on preceding type parameter, this type parameter is the new failure index.
context.failedTypeParameterIndex = index;
}
context.inferredTypes[index] = inferredType;
}
return inferredType;
}
function getInferredTypes(context: InferenceContext): Type[] {
for (let 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 {
let links = getNodeLinks(node);
if (!links.resolvedSymbol) {
links.resolvedSymbol = (!nodeIsMissing(node) && 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, allowEmptyUnionResult: boolean): Type {
if (type.flags & TypeFlags.Union) {
let types = (<UnionType>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
let narrowedType = getUnionType(filter(types, t => !(t.flags & typeKind) === isOfTypeKind));
if (allowEmptyUnionResult || narrowedType !== emptyObjectType) {
return narrowedType;
}
}
}
else if (allowEmptyUnionResult && !!(type.flags & typeKind) === isOfTypeKind) {
// Use getUnionType(emptyArray) instead of emptyObjectType in case the way empty union types
// are represented ever changes.
return getUnionType(emptyArray);
}
return type;
}
function hasInitializer(node: VariableLikeDeclaration): boolean {
return !!(node.initializer || isBindingPattern(node.parent) && hasInitializer(<VariableLikeDeclaration>node.parent.parent));
}
// Check if a given variable is assigned within a given syntax node
function isVariableAssignedWithin(symbol: Symbol, node: Node): boolean {
let links = getNodeLinks(node);
if (links.assignmentChecks) {
let 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.operatorToken.kind >= SyntaxKind.FirstAssignment && node.operatorToken.kind <= SyntaxKind.LastAssignment) {
let n = node.left;
while (n.kind === SyntaxKind.ParenthesizedExpression) {
n = (<ParenthesizedExpression>n).expression;
}
if (n.kind === SyntaxKind.Identifier && getResolvedSymbol(<Identifier>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(<BinaryExpression>node);
case SyntaxKind.VariableDeclaration:
case SyntaxKind.BindingElement:
return isAssignedInVariableDeclaration(<VariableLikeDeclaration>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.ForOfStatement:
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
let containerNodes: Node[] = [];
for (let parent = node.parent; parent; parent = parent.parent) {
if ((isExpression(parent) || isObjectLiteralMethod(node)) &&
isContextSensitive(<Expression>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) {
let 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) {
let child = node;
node = node.parent;
let narrowedType = type;
switch (node.kind) {
case SyntaxKind.IfStatement:
// In a branch of an if statement, narrow based on controlling expression
if (child !== (<IfStatement>node).expression) {
narrowedType = narrowType(type, (<IfStatement>node).expression, /*assumeTrue*/ child === (<IfStatement>node).thenStatement);
}
break;
case SyntaxKind.ConditionalExpression:
// In a branch of a conditional expression, narrow based on controlling condition
if (child !== (<ConditionalExpression>node).condition) {
narrowedType = narrowType(type, (<ConditionalExpression>node).condition, /*assumeTrue*/ child === (<ConditionalExpression>node).whenTrue);
}
break;
case SyntaxKind.BinaryExpression:
// In the right operand of an && or ||, narrow based on left operand
if (child === (<BinaryExpression>node).right) {
if ((<BinaryExpression>node).operatorToken.kind === SyntaxKind.AmpersandAmpersandToken) {
narrowedType = narrowType(type, (<BinaryExpression>node).left, /*assumeTrue*/ true);
}
else if ((<BinaryExpression>node).operatorToken.kind === SyntaxKind.BarBarToken) {
narrowedType = narrowType(type, (<BinaryExpression>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 <symbol>' on the left and string literal on the right
if (expr.left.kind !== SyntaxKind.TypeOfExpression || expr.right.kind !== SyntaxKind.StringLiteral) {
return type;
}
let left = <TypeOfExpression>expr.left;
let right = <LiteralExpression>expr.right;
if (left.expression.kind !== SyntaxKind.Identifier || getResolvedSymbol(<Identifier>left.expression) !== symbol) {
return type;
}
let typeInfo = primitiveTypeInfo[right.text];
if (expr.operatorToken.kind === 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, /*allowEmptyUnionResult*/ false);
}
// 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, /*allowEmptyUnionResult*/ 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, /*allowEmptyUnionResult*/ false);
}
// 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(<Identifier>expr.left) !== symbol) {
return type;
}
// Check that right operand is a function type with a prototype property
let rightType = checkExpression(expr.right);
if (!isTypeSubtypeOf(rightType, globalFunctionType)) {
return type;
}
let targetType: Type;
let prototypeProperty = getPropertyOfType(rightType, "prototype");
if (prototypeProperty) {
// Target type is type of the protoype property
let prototypePropertyType = getTypeOfSymbol(prototypeProperty);
if (prototypePropertyType !== anyType) {
targetType = prototypePropertyType;
}
}
if (!targetType) {
// Target type is type of construct signature
let constructSignatures: Signature[];
if (rightType.flags & TypeFlags.Interface) {
constructSignatures = resolveDeclaredMembers(<InterfaceType>rightType).declaredConstructSignatures;
}
else if (rightType.flags & TypeFlags.Anonymous) {
constructSignatures = getSignaturesOfType(rightType, SignatureKind.Construct);
}
if (constructSignatures && constructSignatures.length) {
targetType = getUnionType(map(constructSignatures, signature => getReturnTypeOfSignature(getErasedSignature(signature))));
}
}
if (targetType) {
// Narrow to the target type if it's a subtype of the current type
if (isTypeSubtypeOf(targetType, type)) {
return targetType;
}
// If the current type is a union type, remove all constituents that aren't subtypes of the target.
if (type.flags & TypeFlags.Union) {
return getUnionType(filter((<UnionType>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, (<ParenthesizedExpression>expr).expression, assumeTrue);
case SyntaxKind.BinaryExpression:
let operator = (<BinaryExpression>expr).operatorToken.kind;
if (operator === SyntaxKind.EqualsEqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken) {
return narrowTypeByEquality(type, <BinaryExpression>expr, assumeTrue);
}
else if (operator === SyntaxKind.AmpersandAmpersandToken) {
return narrowTypeByAnd(type, <BinaryExpression>expr, assumeTrue);
}
else if (operator === SyntaxKind.BarBarToken) {
return narrowTypeByOr(type, <BinaryExpression>expr, assumeTrue);
}
else if (operator === SyntaxKind.InstanceOfKeyword) {
return narrowTypeByInstanceof(type, <BinaryExpression>expr, assumeTrue);
}
break;
case SyntaxKind.PrefixUnaryExpression:
if ((<PrefixUnaryExpression>expr).operator === SyntaxKind.ExclamationToken) {
return narrowType(type, (<PrefixUnaryExpression>expr).operand, !assumeTrue);
}
break;
}
return type;
}
}
function checkIdentifier(node: Identifier): Type {
let symbol = getResolvedSymbol(node);
// As noted in ECMAScript 6 language spec, arrow functions never have an arguments objects.
// Although in down-level emit of arrow function, we emit it using function expression which means that
// arguments objects will be bound to the inner object; emitting arrow function natively in ES6, arguments objects
// will be bound to non-arrow function that contain this arrow function. This results in inconsistent behavior.
// To avoid that we will give an error to users if they use arguments objects in arrow function so that they
// can explicitly bound arguments objects
if (symbol === argumentsSymbol && getContainingFunction(node).kind === SyntaxKind.ArrowFunction && languageVersion < ScriptTarget.ES6) {
error(node, Diagnostics.The_arguments_object_cannot_be_referenced_in_an_arrow_function_in_ES3_and_ES5_Consider_using_a_standard_function_expression);
}
if (symbol.flags & SymbolFlags.Alias && !isInTypeQuery(node) && !isConstEnumOrConstEnumOnlyModule(resolveAlias(symbol))) {
markAliasSymbolAsReferenced(symbol);
}
checkCollisionWithCapturedSuperVariable(node, node);
checkCollisionWithCapturedThisVariable(node, node);
checkBlockScopedBindingCapturedInLoop(node, symbol);
return getNarrowedTypeOfSymbol(getExportSymbolOfValueSymbolIfExported(symbol), node);
}
function isInsideFunction(node: Node, threshold: Node): boolean {
let current = node;
while (current && current !== threshold) {
if (isFunctionLike(current)) {
return true;
}
current = current.parent;
}
return false;
}
function checkBlockScopedBindingCapturedInLoop(node: Identifier, symbol: Symbol): void {
if (languageVersion >= ScriptTarget.ES6 ||
(symbol.flags & SymbolFlags.BlockScopedVariable) === 0 ||
symbol.valueDeclaration.parent.kind === SyntaxKind.CatchClause) {
return;
}
// - check if binding is used in some function
// (stop the walk when reaching container of binding declaration)
// - if first check succeeded - check if variable is declared inside the loop
// nesting structure:
// (variable declaration or binding element) -> variable declaration list -> container
let container: Node = symbol.valueDeclaration;
while (container.kind !== SyntaxKind.VariableDeclarationList) {
container = container.parent;
}
// get the parent of variable declaration list
container = container.parent;
if (container.kind === SyntaxKind.VariableStatement) {
// if parent is variable statement - get its parent
container = container.parent;
}
let inFunction = isInsideFunction(node.parent, container);
let current = container;
while (current && !nodeStartsNewLexicalEnvironment(current)) {
if (isIterationStatement(current, /*lookInLabeledStatements*/ false)) {
if (inFunction) {
grammarErrorOnFirstToken(current, Diagnostics.Loop_contains_block_scoped_variable_0_referenced_by_a_function_in_the_loop_This_is_only_supported_in_ECMAScript_6_or_higher, declarationNameToString(node));
}
// mark value declaration so during emit they can have a special handling
getNodeLinks(<VariableDeclaration>symbol.valueDeclaration).flags |= NodeCheckFlags.BlockScopedBindingInLoop;
break;
}
current = current.parent;
}
}
function captureLexicalThis(node: Node, container: Node): void {
let classNode = container.parent && container.parent.kind === SyntaxKind.ClassDeclaration ? container.parent : undefined;
getNodeLinks(node).flags |= NodeCheckFlags.LexicalThis;
if (container.kind === SyntaxKind.PropertyDeclaration || container.kind === SyntaxKind.Constructor) {
getNodeLinks(classNode).flags |= NodeCheckFlags.CaptureThis;
}
else {
getNodeLinks(container).flags |= NodeCheckFlags.CaptureThis;
}
}
function checkThisExpression(node: Node): Type {
// Stop at the first arrow function so that we can
// tell whether 'this' needs to be captured.
let container = getThisContainer(node, /* includeArrowFunctions */ true);
let needToCaptureLexicalThis = false;
// Now skip arrow functions to get the "real" owner of 'this'.
if (container.kind === SyntaxKind.ArrowFunction) {
container = getThisContainer(container, /* includeArrowFunctions */ false);
// When targeting es6, arrow function lexically bind "this" so we do not need to do the work of binding "this" in emitted code
needToCaptureLexicalThis = (languageVersion < ScriptTarget.ES6);
}
switch (container.kind) {
case SyntaxKind.ModuleDeclaration:
error(node, Diagnostics.this_cannot_be_referenced_in_a_module_or_namespace_body);
// do not return here so in case if lexical this is captured - it will be reflected in flags on NodeLinks
break;
case SyntaxKind.EnumDeclaration:
error(node, Diagnostics.this_cannot_be_referenced_in_current_location);
// do not return here so in case if lexical this is captured - it will be reflected in flags on NodeLinks
break;
case SyntaxKind.Constructor:
if (isInConstructorArgumentInitializer(node, container)) {
error(node, Diagnostics.this_cannot_be_referenced_in_constructor_arguments);
// do not return here so in case if lexical this is captured - it will be reflected in flags on NodeLinks
}
break;
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.PropertySignature:
if (container.flags & NodeFlags.Static) {
error(node, Diagnostics.this_cannot_be_referenced_in_a_static_property_initializer);
// do not return here so in case if lexical this is captured - it will be reflected in flags on NodeLinks
}
break;
case SyntaxKind.ComputedPropertyName:
error(node, Diagnostics.this_cannot_be_referenced_in_a_computed_property_name);
break;
}
if (needToCaptureLexicalThis) {
captureLexicalThis(node, container);
}
let classNode = container.parent && container.parent.kind === SyntaxKind.ClassDeclaration ? container.parent : undefined;
if (classNode) {
let symbol = getSymbolOfNode(classNode);
return container.flags & NodeFlags.Static ? getTypeOfSymbol(symbol) : getDeclaredTypeOfSymbol(symbol);
}
return anyType;
}
function isInConstructorArgumentInitializer(node: Node, constructorDecl: Node): boolean {
for (let n = node; n && n !== constructorDecl; n = n.parent) {
if (n.kind === SyntaxKind.Parameter) {
return true;
}
}
return false;
}
function checkSuperExpression(node: Node): Type {
let isCallExpression = node.parent.kind === SyntaxKind.CallExpression && (<CallExpression>node.parent).expression === node;
let enclosingClass = <ClassDeclaration>getAncestor(node, SyntaxKind.ClassDeclaration);
let baseClass: Type;
if (enclosingClass && getClassExtendsHeritageClauseElement(enclosingClass)) {
let classType = <InterfaceType>getDeclaredTypeOfSymbol(getSymbolOfNode(enclosingClass));
let baseTypes = getBaseTypes(classType);
baseClass = baseTypes.length && baseTypes[0];
}
if (!baseClass) {
error(node, Diagnostics.super_can_only_be_referenced_in_a_derived_class);
return unknownType;
}
let container = getSuperContainer(node, /*includeFunctions*/ true);
if (container) {
let canUseSuperExpression = false;
let needToCaptureLexicalThis: boolean;
if (isCallExpression) {
// TS 1.0 SPEC (April 2014): 4.8.1
// Super calls are only permitted in constructors of derived classes
canUseSuperExpression = container.kind === SyntaxKind.Constructor;
}
else {
// TS 1.0 SPEC (April 2014)
// 'super' property access is allowed
// - In a constructor, instance member function, instance member accessor, or instance member variable initializer where this references a derived class instance
// - In a static member function or static member accessor
// super property access might appear in arrow functions with arbitrary deep nesting
needToCaptureLexicalThis = false;
while (container && container.kind === SyntaxKind.ArrowFunction) {
container = getSuperContainer(container, /*includeFunctions*/ true);
needToCaptureLexicalThis = languageVersion < ScriptTarget.ES6;
}
// topmost container must be something that is directly nested in the class declaration
if (container && container.parent && container.parent.kind === SyntaxKind.ClassDeclaration) {
if (container.flags & NodeFlags.Static) {
canUseSuperExpression =
container.kind === SyntaxKind.MethodDeclaration ||
container.kind === SyntaxKind.MethodSignature ||
container.kind === SyntaxKind.GetAccessor ||
container.kind === SyntaxKind.SetAccessor;
}
else {
canUseSuperExpression =
container.kind === SyntaxKind.MethodDeclaration ||
container.kind === SyntaxKind.MethodSignature ||
container.kind === SyntaxKind.GetAccessor ||
container.kind === SyntaxKind.SetAccessor ||
container.kind === SyntaxKind.PropertyDeclaration ||
container.kind === SyntaxKind.PropertySignature ||
container.kind === SyntaxKind.Constructor;
}
}
}
if (canUseSuperExpression) {
let returnType: Type;
if ((container.flags & NodeFlags.Static) || isCallExpression) {
getNodeLinks(node).flags |= NodeCheckFlags.SuperStatic;
returnType = getTypeOfSymbol(baseClass.symbol);
}
else {
getNodeLinks(node).flags |= NodeCheckFlags.SuperInstance;
returnType = baseClass;
}
if (container.kind === SyntaxKind.Constructor && isInConstructorArgumentInitializer(node, container)) {
// issue custom error message for super property access in constructor arguments (to be aligned with old compiler)
error(node, Diagnostics.super_cannot_be_referenced_in_constructor_arguments);
returnType = unknownType;
}
if (!isCallExpression && needToCaptureLexicalThis) {
// call expressions are allowed only in constructors so they should always capture correct 'this'
// super property access expressions can also appear in arrow functions -
// in this case they should also use correct lexical this
captureLexicalThis(node.parent, container);
}
return returnType;
}
}
if (container && container.kind === SyntaxKind.ComputedPropertyName) {
error(node, Diagnostics.super_cannot_be_referenced_in_a_computed_property_name);
}
else if (isCallExpression) {
error(node, Diagnostics.Super_calls_are_not_permitted_outside_constructors_or_in_nested_functions_inside_constructors);
}
else {
error(node, Diagnostics.super_property_access_is_permitted_only_in_a_constructor_member_function_or_member_accessor_of_a_derived_class);
}
return unknownType;
}
// Return contextual type of parameter or undefined if no contextual type is available
function getContextuallyTypedParameterType(parameter: ParameterDeclaration): Type {
if (isFunctionExpressionOrArrowFunction(parameter.parent)) {
let func = <FunctionExpression>parameter.parent;
if (isContextSensitive(func)) {
let contextualSignature = getContextualSignature(func);
if (contextualSignature) {
let funcHasRestParameters = hasRestParameters(func);
let len = func.parameters.length - (funcHasRestParameters ? 1 : 0);
let indexOfParameter = indexOf(func.parameters, parameter);
if (indexOfParameter < len) {
return getTypeAtPosition(contextualSignature, indexOfParameter);
}
// If last parameter is contextually rest parameter get its type
if (indexOfParameter === (func.parameters.length - 1) &&
funcHasRestParameters && contextualSignature.hasRestParameter && func.parameters.length >= contextualSignature.parameters.length) {
return getTypeOfSymbol(lastOrUndefined(contextualSignature.parameters));
}
}
}
}
return undefined;
}
// In a variable, parameter or property declaration with a type annotation, the contextual type of an initializer
// expression is the type of the variable, parameter or property. Otherwise, in a parameter declaration of a
// contextually typed function expression, the contextual type of an initializer expression is the contextual type
// of the parameter. Otherwise, in a variable or parameter declaration with a binding pattern name, the contextual
// type of an initializer expression is the type implied by the binding pattern.
function getContextualTypeForInitializerExpression(node: Expression): Type {
let declaration = <VariableLikeDeclaration>node.parent;
if (node === declaration.initializer) {
if (declaration.type) {
return getTypeFromTypeNode(declaration.type);
}
if (declaration.kind === SyntaxKind.Parameter) {
let type = getContextuallyTypedParameterType(<ParameterDeclaration>declaration);
if (type) {
return type;
}
}
if (isBindingPattern(declaration.name)) {
return getTypeFromBindingPattern(<BindingPattern>declaration.name);
}
}
return undefined;
}
function getContextualTypeForReturnExpression(node: Expression): Type {
let func = getContainingFunction(node);
if (func && !func.asteriskToken) {
return getContextualReturnType(func);
}
return undefined;
}
function getContextualTypeForYieldOperand(node: YieldExpression): Type {
let func = getContainingFunction(node);
if (func) {
let contextualReturnType = getContextualReturnType(func);
if (contextualReturnType) {
return node.asteriskToken
? contextualReturnType
: getElementTypeOfIterableIterator(contextualReturnType);
}
}
return undefined;
}
function getContextualReturnType(functionDecl: FunctionLikeDeclaration): Type {
// If the containing function has a return type annotation, is a constructor, or is a get accessor whose
// corresponding set accessor has a type annotation, return statements in the function are contextually typed
if (functionDecl.type ||
functionDecl.kind === SyntaxKind.Constructor ||
functionDecl.kind === SyntaxKind.GetAccessor && getSetAccessorTypeAnnotationNode(<AccessorDeclaration>getDeclarationOfKind(functionDecl.symbol, SyntaxKind.SetAccessor))) {
return getReturnTypeOfSignature(getSignatureFromDeclaration(functionDecl));
}
// Otherwise, if the containing function is contextually typed by a function type with exactly one call signature
// and that call signature is non-generic, return statements are contextually typed by the return type of the signature
let signature = getContextualSignatureForFunctionLikeDeclaration(<FunctionExpression>functionDecl);
if (signature) {
return getReturnTypeOfSignature(signature);
}
return undefined;
}
// In a typed function call, an argument or substitution expression is contextually typed by the type of the corresponding parameter.
function getContextualTypeForArgument(callTarget: CallLikeExpression, arg: Expression): Type {
let args = getEffectiveCallArguments(callTarget);
let argIndex = indexOf(args, arg);
if (argIndex >= 0) {
let signature = getResolvedSignature(callTarget);
return getTypeAtPosition(signature, argIndex);
}
return undefined;
}
function getContextualTypeForSubstitutionExpression(template: TemplateExpression, substitutionExpression: Expression) {
if (template.parent.kind === SyntaxKind.TaggedTemplateExpression) {
return getContextualTypeForArgument(<TaggedTemplateExpression>template.parent, substitutionExpression);
}
return undefined;
}
function getContextualTypeForBinaryOperand(node: Expression): Type {
let binaryExpression = <BinaryExpression>node.parent;
let operator = binaryExpression.operatorToken.kind;
if (operator >= SyntaxKind.FirstAssignment && operator <= SyntaxKind.LastAssignment) {
// In an assignment expression, the right operand is contextually typed by the type of the left operand.
if (node === binaryExpression.right) {
return checkExpression(binaryExpression.left);
}
}
else if (operator === SyntaxKind.BarBarToken) {
// When an || expression has a contextual type, the operands are contextually typed by that type. When an ||
// expression has no contextual type, the right operand is contextually typed by the type of the left operand.
let type = getContextualType(binaryExpression);
if (!type && node === binaryExpression.right) {
type = checkExpression(binaryExpression.left);
}
return type;
}
return undefined;
}
// Apply a mapping function to a contextual type and return the resulting type. If the contextual type
// is a union type, the mapping function is applied to each constituent type and a union of the resulting
// types is returned.
function applyToContextualType(type: Type, mapper: (t: Type) => Type): Type {
if (!(type.flags & TypeFlags.Union)) {
return mapper(type);
}
let types = (<UnionType>type).types;
let mappedType: Type;
let mappedTypes: Type[];
for (let current of types) {
let t = mapper(current);
if (t) {
if (!mappedType) {
mappedType = t;
}
else if (!mappedTypes) {
mappedTypes = [mappedType, t];
}
else {
mappedTypes.push(t);
}
}
}
return mappedTypes ? getUnionType(mappedTypes) : mappedType;
}
function getTypeOfPropertyOfContextualType(type: Type, name: string) {
return applyToContextualType(type, t => {
let prop = getPropertyOfObjectType(t, name);
return prop ? getTypeOfSymbol(prop) : undefined;
});
}
function getIndexTypeOfContextualType(type: Type, kind: IndexKind) {
return applyToContextualType(type, t => getIndexTypeOfObjectOrUnionType(t, kind));
}
// Return true if the given contextual type is a tuple-like type
function contextualTypeIsTupleLikeType(type: Type): boolean {
return !!(type.flags & TypeFlags.Union ? forEach((<UnionType>type).types, isTupleLikeType) : isTupleLikeType(type));
}
// Return true if the given contextual type provides an index signature of the given kind
function contextualTypeHasIndexSignature(type: Type, kind: IndexKind): boolean {
return !!(type.flags & TypeFlags.Union ? forEach((<UnionType>type).types, t => getIndexTypeOfObjectOrUnionType(t, kind)) : getIndexTypeOfObjectOrUnionType(type, kind));
}
// In an object literal contextually typed by a type T, the contextual type of a property assignment is the type of
// the matching property in T, if one exists. Otherwise, it is the type of the numeric index signature in T, if one
// exists. Otherwise, it is the type of the string index signature in T, if one exists.
function getContextualTypeForObjectLiteralMethod(node: MethodDeclaration): Type {
Debug.assert(isObjectLiteralMethod(node));
if (isInsideWithStatementBody(node)) {
// We cannot answer semantic questions within a with block, do not proceed any further
return undefined;
}
return getContextualTypeForObjectLiteralElement(node);
}
function getContextualTypeForObjectLiteralElement(element: ObjectLiteralElement) {
let objectLiteral = <ObjectLiteralExpression>element.parent;
let type = getContextualType(objectLiteral);
if (type) {
if (!hasDynamicName(element)) {
// For a (non-symbol) computed property, there is no reason to look up the name
// in the type. It will just be "__computed", which does not appear in any
// SymbolTable.
let symbolName = getSymbolOfNode(element).name;
let propertyType = getTypeOfPropertyOfContextualType(type, symbolName);
if (propertyType) {
return propertyType;
}
}
return isNumericName(element.name) && getIndexTypeOfContextualType(type, IndexKind.Number) ||
getIndexTypeOfContextualType(type, IndexKind.String);
}
return undefined;
}
// In an array literal contextually typed by a type T, the contextual type of an element expression at index N is
// the type of the property with the numeric name N in T, if one exists. Otherwise, if T has a numeric index signature,
// it is the type of the numeric index signature in T. Otherwise, in ES6 and higher, the contextual type is the iterated
// type of T.
function getContextualTypeForElementExpression(node: Expression): Type {
let arrayLiteral = <ArrayLiteralExpression>node.parent;
let type = getContextualType(arrayLiteral);
if (type) {
let index = indexOf(arrayLiteral.elements, node);
return getTypeOfPropertyOfContextualType(type, "" + index)
|| getIndexTypeOfContextualType(type, IndexKind.Number)
|| (languageVersion >= ScriptTarget.ES6 ? getElementTypeOfIterable(type, /*errorNode*/ undefined) : undefined);
}
return undefined;
}
// In a contextually typed conditional expression, the true/false expressions are contextually typed by the same type.
function getContextualTypeForConditionalOperand(node: Expression): Type {
let conditional = <ConditionalExpression>node.parent;
return node === conditional.whenTrue || node === conditional.whenFalse ? getContextualType(conditional) : undefined;
}
// Return the contextual type for a given expression node. During overload resolution, a contextual type may temporarily
// be "pushed" onto a node using the contextualType property.
function getContextualType(node: Expression): Type {
if (isInsideWithStatementBody(node)) {
// We cannot answer semantic questions within a with block, do not proceed any further
return undefined;
}
if (node.contextualType) {
return node.contextualType;
}
let parent = node.parent;
switch (parent.kind) {
case SyntaxKind.VariableDeclaration:
case SyntaxKind.Parameter:
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.PropertySignature:
case SyntaxKind.BindingElement:
return getContextualTypeForInitializerExpression(node);
case SyntaxKind.ArrowFunction:
case SyntaxKind.ReturnStatement:
return getContextualTypeForReturnExpression(node);
case SyntaxKind.YieldExpression:
return getContextualTypeForYieldOperand(<YieldExpression>parent);
case SyntaxKind.CallExpression:
case SyntaxKind.NewExpression:
return getContextualTypeForArgument(<CallExpression>parent, node);
case SyntaxKind.TypeAssertionExpression:
return getTypeFromTypeNode((<TypeAssertion>parent).type);
case SyntaxKind.BinaryExpression:
return getContextualTypeForBinaryOperand(node);
case SyntaxKind.PropertyAssignment:
return getContextualTypeForObjectLiteralElement(<ObjectLiteralElement>parent);
case SyntaxKind.ArrayLiteralExpression:
return getContextualTypeForElementExpression(node);
case SyntaxKind.ConditionalExpression:
return getContextualTypeForConditionalOperand(node);
case SyntaxKind.TemplateSpan:
Debug.assert(parent.parent.kind === SyntaxKind.TemplateExpression);
return getContextualTypeForSubstitutionExpression(<TemplateExpression>parent.parent, node);
case SyntaxKind.ParenthesizedExpression:
return getContextualType(<ParenthesizedExpression>parent);
}
return undefined;
}
// If the given type is an object or union type, if that type has a single signature, and if
// that signature is non-generic, return the signature. Otherwise return undefined.
function getNonGenericSignature(type: Type): Signature {
let signatures = getSignaturesOfObjectOrUnionType(type, SignatureKind.Call);
if (signatures.length === 1) {
let signature = signatures[0];
if (!signature.typeParameters) {
return signature;
}
}
}
function isFunctionExpressionOrArrowFunction(node: Node): boolean {
return node.kind === SyntaxKind.FunctionExpression || node.kind === SyntaxKind.ArrowFunction;
}
function getContextualSignatureForFunctionLikeDeclaration(node: FunctionLikeDeclaration): Signature {
// Only function expressions, arrow functions, and object literal methods are contextually typed.
return isFunctionExpressionOrArrowFunction(node) || isObjectLiteralMethod(node)
? getContextualSignature(<FunctionExpression>node)
: undefined;
}
// Return the contextual signature for a given expression node. A contextual type provides a
// contextual signature if it has a single call signature and if that call signature is non-generic.
// If the contextual type is a union type, get the signature from each type possible and if they are
// all identical ignoring their return type, the result is same signature but with return type as
// union type of return types from these signatures
function getContextualSignature(node: FunctionExpression | MethodDeclaration): Signature {
Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isObjectLiteralMethod(node));
let type = isObjectLiteralMethod(node)
? getContextualTypeForObjectLiteralMethod(<MethodDeclaration>node)
: getContextualType(<FunctionExpression>node);
if (!type) {
return undefined;
}
if (!(type.flags & TypeFlags.Union)) {
return getNonGenericSignature(type);
}
let signatureList: Signature[];
let types = (<UnionType>type).types;
for (let current of types) {
// The signature set of all constituent type with call signatures should match
// So number of signatures allowed is either 0 or 1
if (signatureList &&
getSignaturesOfObjectOrUnionType(current, SignatureKind.Call).length > 1) {
return undefined;
}
let signature = getNonGenericSignature(current);
if (signature) {
if (!signatureList) {
// This signature will contribute to contextual union signature
signatureList = [signature];
}
else if (!compareSignatures(signatureList[0], signature, /*compareReturnTypes*/ false, compareTypes)) {
// Signatures aren't identical, do not use
return undefined;
}
else {
// Use this signature for contextual union signature
signatureList.push(signature);
}
}
}
// Result is union of signatures collected (return type is union of return types of this signature set)
let result: Signature;
if (signatureList) {
result = cloneSignature(signatureList[0]);
// Clear resolved return type we possibly got from cloneSignature
result.resolvedReturnType = undefined;
result.unionSignatures = signatureList;
}
return result;
}
// Presence of a contextual type mapper indicates inferential typing, except the identityMapper object is
// used as a special marker for other purposes.
function isInferentialContext(mapper: TypeMapper) {
return mapper && mapper !== identityMapper;
}
// A node is an assignment target if it is on the left hand side of an '=' token, if it is parented by a property
// assignment in an object literal that is an assignment target, or if it is parented by an array literal that is
// an assignment target. Examples include 'a = xxx', '{ p: a } = xxx', '[{ p: a}] = xxx'.
function isAssignmentTarget(node: Node): boolean {
let parent = node.parent;
if (parent.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>parent).operatorToken.kind === SyntaxKind.EqualsToken && (<BinaryExpression>parent).left === node) {
return true;
}
if (parent.kind === SyntaxKind.PropertyAssignment) {
return isAssignmentTarget(parent.parent);
}
if (parent.kind === SyntaxKind.ArrayLiteralExpression) {
return isAssignmentTarget(parent);
}
return false;
}
function checkSpreadElementExpression(node: SpreadElementExpression, contextualMapper?: TypeMapper): Type {
// It is usually not safe to call checkExpressionCached if we can be contextually typing.
// You can tell that we are contextually typing because of the contextualMapper parameter.
// While it is true that a spread element can have a contextual type, it does not do anything
// with this type. It is neither affected by it, nor does it propagate it to its operand.
// So the fact that contextualMapper is passed is not important, because the operand of a spread
// element is not contextually typed.
let arrayOrIterableType = checkExpressionCached(node.expression, contextualMapper);
return checkIteratedTypeOrElementType(arrayOrIterableType, node.expression, /*allowStringInput*/ false);
}
function checkArrayLiteral(node: ArrayLiteralExpression, contextualMapper?: TypeMapper): Type {
let elements = node.elements;
if (!elements.length) {
return createArrayType(undefinedType);
}
let hasSpreadElement = false;
let elementTypes: Type[] = [];
let inDestructuringPattern = isAssignmentTarget(node);
for (let e of elements) {
if (inDestructuringPattern && e.kind === SyntaxKind.SpreadElementExpression) {
// Given the following situation:
// var c: {};
// [...c] = ["", 0];
//
// c is represented in the tree as a spread element in an array literal.
// But c really functions as a rest element, and its purpose is to provide
// a contextual type for the right hand side of the assignment. Therefore,
// instead of calling checkExpression on "...c", which will give an error
// if c is not iterable/array-like, we need to act as if we are trying to
// get the contextual element type from it. So we do something similar to
// getContextualTypeForElementExpression, which will crucially not error
// if there is no index type / iterated type.
let restArrayType = checkExpression((<SpreadElementExpression>e).expression, contextualMapper);
let restElementType = getIndexTypeOfType(restArrayType, IndexKind.Number) ||
(languageVersion >= ScriptTarget.ES6 ? getElementTypeOfIterable(restArrayType, /*errorNode*/ undefined) : undefined);
if (restElementType) {
elementTypes.push(restElementType);
}
}
else {
let type = checkExpression(e, contextualMapper);
elementTypes.push(type);
}
hasSpreadElement = hasSpreadElement || e.kind === SyntaxKind.SpreadElementExpression;
}
if (!hasSpreadElement) {
let contextualType = getContextualType(node);
if (contextualType && contextualTypeIsTupleLikeType(contextualType) || inDestructuringPattern) {
return createTupleType(elementTypes);
}
}
return createArrayType(getUnionType(elementTypes));
}
function isNumericName(name: DeclarationName): boolean {
return name.kind === SyntaxKind.ComputedPropertyName ? isNumericComputedName(<ComputedPropertyName>name) : isNumericLiteralName((<Identifier>name).text);
}
function isNumericComputedName(name: ComputedPropertyName): boolean {
// It seems odd to consider an expression of type Any to result in a numeric name,
// but this behavior is consistent with checkIndexedAccess
return allConstituentTypesHaveKind(checkComputedPropertyName(name), TypeFlags.Any | TypeFlags.NumberLike);
}
function isNumericLiteralName(name: string) {
// The intent of numeric names is that
// - they are names with text in a numeric form, and that
// - setting properties/indexing with them is always equivalent to doing so with the numeric literal 'numLit',
// acquired by applying the abstract 'ToNumber' operation on the name's text.
//
// The subtlety is in the latter portion, as we cannot reliably say that anything that looks like a numeric literal is a numeric name.
// In fact, it is the case that the text of the name must be equal to 'ToString(numLit)' for this to hold.
//
// Consider the property name '"0xF00D"'. When one indexes with '0xF00D', they are actually indexing with the value of 'ToString(0xF00D)'
// according to the ECMAScript specification, so it is actually as if the user indexed with the string '"61453"'.
// Thus, the text of all numeric literals equivalent to '61543' such as '0xF00D', '0xf00D', '0170015', etc. are not valid numeric names
// because their 'ToString' representation is not equal to their original text.
// This is motivated by ECMA-262 sections 9.3.1, 9.8.1, 11.1.5, and 11.2.1.
//
// Here, we test whether 'ToString(ToNumber(name))' is exactly equal to 'name'.
// The '+' prefix operator is equivalent here to applying the abstract ToNumber operation.
// Applying the 'toString()' method on a number gives us the abstract ToString operation on a number.
//
// Note that this accepts the values 'Infinity', '-Infinity', and 'NaN', and that this is intentional.
// This is desired behavior, because when indexing with them as numeric entities, you are indexing
// with the strings '"Infinity"', '"-Infinity"', and '"NaN"' respectively.
return (+name).toString() === name;
}
function checkComputedPropertyName(node: ComputedPropertyName): Type {
let links = getNodeLinks(node.expression);
if (!links.resolvedType) {
links.resolvedType = checkExpression(node.expression);
// This will allow types number, string, symbol or any. It will also allow enums, the unknown
// type, and any union of these types (like string | number).
if (!allConstituentTypesHaveKind(links.resolvedType, TypeFlags.Any | TypeFlags.NumberLike | TypeFlags.StringLike | TypeFlags.ESSymbol)) {
error(node, Diagnostics.A_computed_property_name_must_be_of_type_string_number_symbol_or_any);
}
else {
checkThatExpressionIsProperSymbolReference(node.expression, links.resolvedType, /*reportError*/ true);
}
}
return links.resolvedType;
}
function checkObjectLiteral(node: ObjectLiteralExpression, contextualMapper?: TypeMapper): Type {
// Grammar checking
checkGrammarObjectLiteralExpression(node);
let propertiesTable: SymbolTable = {};
let propertiesArray: Symbol[] = [];
let contextualType = getContextualType(node);
let typeFlags: TypeFlags;
for (let memberDecl of node.properties) {
let member = memberDecl.symbol;
if (memberDecl.kind === SyntaxKind.PropertyAssignment ||
memberDecl.kind === SyntaxKind.ShorthandPropertyAssignment ||
isObjectLiteralMethod(memberDecl)) {
let type: Type;
if (memberDecl.kind === SyntaxKind.PropertyAssignment) {
type = checkPropertyAssignment(<PropertyAssignment>memberDecl, contextualMapper);
}
else if (memberDecl.kind === SyntaxKind.MethodDeclaration) {
type = checkObjectLiteralMethod(<MethodDeclaration>memberDecl, contextualMapper);
}
else {
Debug.assert(memberDecl.kind === SyntaxKind.ShorthandPropertyAssignment);
type = checkExpression((<ShorthandPropertyAssignment>memberDecl).name, contextualMapper);
}
typeFlags |= type.flags;
let prop = <TransientSymbol>createSymbol(SymbolFlags.Property | SymbolFlags.Transient | member.flags, member.name);
prop.declarations = member.declarations;
prop.parent = member.parent;
if (member.valueDeclaration) {
prop.valueDeclaration = member.valueDeclaration;
}
prop.type = type;
prop.target = member;
member = prop;
}
else {
// TypeScript 1.0 spec (April 2014)
// A get accessor declaration is processed in the same manner as
// an ordinary function declaration(section 6.1) with no parameters.
// A set accessor declaration is processed in the same manner
// as an ordinary function declaration with a single parameter and a Void return type.
Debug.assert(memberDecl.kind === SyntaxKind.GetAccessor || memberDecl.kind === SyntaxKind.SetAccessor);
checkAccessorDeclaration(<AccessorDeclaration>memberDecl);
}
if (!hasDynamicName(memberDecl)) {
propertiesTable[member.name] = member;
}
propertiesArray.push(member);
}
let stringIndexType = getIndexType(IndexKind.String);
let numberIndexType = getIndexType(IndexKind.Number);
let result = createAnonymousType(node.symbol, propertiesTable, emptyArray, emptyArray, stringIndexType, numberIndexType);
result.flags |= TypeFlags.ObjectLiteral | TypeFlags.ContainsObjectLiteral | (typeFlags & TypeFlags.ContainsUndefinedOrNull);
return result;
function getIndexType(kind: IndexKind) {
if (contextualType && contextualTypeHasIndexSignature(contextualType, kind)) {
let propTypes: Type[] = [];
for (let i = 0; i < propertiesArray.length; i++) {
let propertyDecl = node.properties[i];
if (kind === IndexKind.String || isNumericName(propertyDecl.name)) {
// Do not call getSymbolOfNode(propertyDecl), as that will get the
// original symbol for the node. We actually want to get the symbol
// created by checkObjectLiteral, since that will be appropriately
// contextually typed and resolved.
let type = getTypeOfSymbol(propertiesArray[i]);
if (!contains(propTypes, type)) {
propTypes.push(type);
}
}
}
let result = propTypes.length ? getUnionType(propTypes) : undefinedType;
typeFlags |= result.flags;
return result;
}
return undefined;
}
}
// If a symbol is a synthesized symbol with no value declaration, we assume it is a property. Example of this are the synthesized
// '.prototype' property as well as synthesized tuple index properties.
function getDeclarationKindFromSymbol(s: Symbol) {
return s.valueDeclaration ? s.valueDeclaration.kind : SyntaxKind.PropertyDeclaration;
}
function getDeclarationFlagsFromSymbol(s: Symbol) {
return s.valueDeclaration ? getCombinedNodeFlags(s.valueDeclaration) : s.flags & SymbolFlags.Prototype ? NodeFlags.Public | NodeFlags.Static : 0;
}
function checkClassPropertyAccess(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, type: Type, prop: Symbol) {
let flags = getDeclarationFlagsFromSymbol(prop);
// Public properties are always accessible
if (!(flags & (NodeFlags.Private | NodeFlags.Protected))) {
return;
}
// Property is known to be private or protected at this point
// Get the declaring and enclosing class instance types
let enclosingClassDeclaration = getAncestor(node, SyntaxKind.ClassDeclaration);
let enclosingClass = enclosingClassDeclaration ? <InterfaceType>getDeclaredTypeOfSymbol(getSymbolOfNode(enclosingClassDeclaration)) : undefined;
let declaringClass = <InterfaceType>getDeclaredTypeOfSymbol(prop.parent);
// Private property is accessible if declaring and enclosing class are the same
if (flags & NodeFlags.Private) {
if (declaringClass !== enclosingClass) {
error(node, Diagnostics.Property_0_is_private_and_only_accessible_within_class_1, symbolToString(prop), typeToString(declaringClass));
}
return;
}
// Property is known to be protected at this point
// All protected properties of a supertype are accessible in a super access
if (left.kind === SyntaxKind.SuperKeyword) {
return;
}
// A protected property is accessible in the declaring class and classes derived from it
if (!enclosingClass || !hasBaseType(enclosingClass, declaringClass)) {
error(node, Diagnostics.Property_0_is_protected_and_only_accessible_within_class_1_and_its_subclasses, symbolToString(prop), typeToString(declaringClass));
return;
}
// No further restrictions for static properties
if (flags & NodeFlags.Static) {
return;
}
// An instance property must be accessed through an instance of the enclosing class
if (!(getTargetType(type).flags & (TypeFlags.Class | TypeFlags.Interface) && hasBaseType(<InterfaceType>type, enclosingClass))) {
error(node, Diagnostics.Property_0_is_protected_and_only_accessible_through_an_instance_of_class_1, symbolToString(prop), typeToString(enclosingClass));
}
}
function checkPropertyAccessExpression(node: PropertyAccessExpression) {
return checkPropertyAccessExpressionOrQualifiedName(node, node.expression, node.name);
}
function checkQualifiedName(node: QualifiedName) {
return checkPropertyAccessExpressionOrQualifiedName(node, node.left, node.right);
}
function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, right: Identifier) {
let type = checkExpressionOrQualifiedName(left);
if (type === unknownType) return type;
if (type !== anyType) {
let apparentType = getApparentType(getWidenedType(type));
if (apparentType === unknownType) {
// handle cases when type is Type parameter with invalid constraint
return unknownType;
}
let prop = getPropertyOfType(apparentType, right.text);
if (!prop) {
if (right.text) {
error(right, Diagnostics.Property_0_does_not_exist_on_type_1, declarationNameToString(right), typeToString(type));
}
return unknownType;
}
getNodeLinks(node).resolvedSymbol = prop;
if (prop.parent && prop.parent.flags & SymbolFlags.Class) {
// TS 1.0 spec (April 2014): 4.8.2
// - In a constructor, instance member function, instance member accessor, or
// instance member variable initializer where this references a derived class instance,
// a super property access is permitted and must specify a public instance member function of the base class.
// - In a static member function or static member accessor
// where this references the constructor function object of a derived class,
// a super property access is permitted and must specify a public static member function of the base class.
if (left.kind === SyntaxKind.SuperKeyword && getDeclarationKindFromSymbol(prop) !== SyntaxKind.MethodDeclaration) {
error(right, Diagnostics.Only_public_and_protected_methods_of_the_base_class_are_accessible_via_the_super_keyword);
}
else {
checkClassPropertyAccess(node, left, type, prop);
}
}
return getTypeOfSymbol(prop);
}
return anyType;
}
function isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean {
let left = node.kind === SyntaxKind.PropertyAccessExpression
? (<PropertyAccessExpression>node).expression
: (<QualifiedName>node).left;
let type = checkExpressionOrQualifiedName(left);
if (type !== unknownType && type !== anyType) {
let prop = getPropertyOfType(getWidenedType(type), propertyName);
if (prop && prop.parent && prop.parent.flags & SymbolFlags.Class) {
if (left.kind === SyntaxKind.SuperKeyword && getDeclarationKindFromSymbol(prop) !== SyntaxKind.MethodDeclaration) {
return false;
}
else {
let modificationCount = diagnostics.getModificationCount();
checkClassPropertyAccess(node, left, type, prop);
return diagnostics.getModificationCount() === modificationCount;
}
}
}
return true;
}
function checkIndexedAccess(node: ElementAccessExpression): Type {
// Grammar checking
if (!node.argumentExpression) {
let sourceFile = getSourceFile(node);
if (node.parent.kind === SyntaxKind.NewExpression && (<NewExpression>node.parent).expression === node) {
let start = skipTrivia(sourceFile.text, node.expression.end);
let end = node.end;
grammarErrorAtPos(sourceFile, start, end - start, Diagnostics.new_T_cannot_be_used_to_create_an_array_Use_new_Array_T_instead);
}
else {
let start = node.end - "]".length;
let end = node.end;
grammarErrorAtPos(sourceFile, start, end - start, Diagnostics.Expression_expected);
}
}
// Obtain base constraint such that we can bail out if the constraint is an unknown type
let objectType = getApparentType(checkExpression(node.expression));
let indexType = node.argumentExpression ? checkExpression(node.argumentExpression) : unknownType;
if (objectType === unknownType) {
return unknownType;
}
let isConstEnum = isConstEnumObjectType(objectType);
if (isConstEnum &&
(!node.argumentExpression || node.argumentExpression.kind !== SyntaxKind.StringLiteral)) {
error(node.argumentExpression, Diagnostics.A_const_enum_member_can_only_be_accessed_using_a_string_literal);
return unknownType;
}
// TypeScript 1.0 spec (April 2014): 4.10 Property Access
// - If IndexExpr is a string literal or a numeric literal and ObjExpr's apparent type has a property with the name
// given by that literal(converted to its string representation in the case of a numeric literal), the property access is of the type of that property.
// - Otherwise, if ObjExpr's apparent type has a numeric index signature and IndexExpr is of type Any, the Number primitive type, or an enum type,
// the property access is of the type of that index signature.
// - Otherwise, if ObjExpr's apparent type has a string index signature and IndexExpr is of type Any, the String or Number primitive type, or an enum type,
// the property access is of the type of that index signature.
// - Otherwise, if IndexExpr is of type Any, the String or Number primitive type, or an enum type, the property access is of type Any.
// See if we can index as a property.
if (node.argumentExpression) {
let name = getPropertyNameForIndexedAccess(node.argumentExpression, indexType);
if (name !== undefined) {
let prop = getPropertyOfType(objectType, name);
if (prop) {
getNodeLinks(node).resolvedSymbol = prop;
return getTypeOfSymbol(prop);
}
else if (isConstEnum) {
error(node.argumentExpression, Diagnostics.Property_0_does_not_exist_on_const_enum_1, name, symbolToString(objectType.symbol));
return unknownType;
}
}
}
// Check for compatible indexer types.
if (allConstituentTypesHaveKind(indexType, TypeFlags.Any | TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbol)) {
// Try to use a number indexer.
if (allConstituentTypesHaveKind(indexType, TypeFlags.Any | TypeFlags.NumberLike)) {
let numberIndexType = getIndexTypeOfType(objectType, IndexKind.Number);
if (numberIndexType) {
return numberIndexType;
}
}
// Try to use string indexing.
let stringIndexType = getIndexTypeOfType(objectType, IndexKind.String);
if (stringIndexType) {
return stringIndexType;
}
// Fall back to any.
if (compilerOptions.noImplicitAny && !compilerOptions.suppressImplicitAnyIndexErrors && objectType !== anyType) {
error(node, Diagnostics.Index_signature_of_object_type_implicitly_has_an_any_type);
}
return anyType;
}
// REVIEW: Users should know the type that was actually used.
error(node, Diagnostics.An_index_expression_argument_must_be_of_type_string_number_symbol_or_any);
return unknownType;
}
/**
* If indexArgumentExpression is a string literal or number literal, returns its text.
* If indexArgumentExpression is a well known symbol, returns the property name corresponding
* to this symbol, as long as it is a proper symbol reference.
* Otherwise, returns undefined.
*/
function getPropertyNameForIndexedAccess(indexArgumentExpression: Expression, indexArgumentType: Type): string {
if (indexArgumentExpression.kind === SyntaxKind.StringLiteral || indexArgumentExpression.kind === SyntaxKind.NumericLiteral) {
return (<LiteralExpression>indexArgumentExpression).text;
}
if (checkThatExpressionIsProperSymbolReference(indexArgumentExpression, indexArgumentType, /*reportError*/ false)) {
let rightHandSideName = (<Identifier>(<PropertyAccessExpression>indexArgumentExpression).name).text;
return getPropertyNameForKnownSymbolName(rightHandSideName);
}
return undefined;
}
/**
* A proper symbol reference requires the following:
* 1. The property access denotes a property that exists
* 2. The expression is of the form Symbol.<identifier>
* 3. The property access is of the primitive type symbol.
* 4. Symbol in this context resolves to the global Symbol object
*/
function checkThatExpressionIsProperSymbolReference(expression: Expression, expressionType: Type, reportError: boolean): boolean {
if (expressionType === unknownType) {
// There is already an error, so no need to report one.
return false;
}
if (!isWellKnownSymbolSyntactically(expression)) {
return false;
}
// Make sure the property type is the primitive symbol type
if ((expressionType.flags & TypeFlags.ESSymbol) === 0) {
if (reportError) {
error(expression, Diagnostics.A_computed_property_name_of_the_form_0_must_be_of_type_symbol, getTextOfNode(expression));
}
return false;
}
// The name is Symbol.<someName>, so make sure Symbol actually resolves to the
// global Symbol object
let leftHandSide = <Identifier>(<PropertyAccessExpression>expression).expression;
let leftHandSideSymbol = getResolvedSymbol(leftHandSide);
if (!leftHandSideSymbol) {
return false;
}
let globalESSymbol = getGlobalESSymbolConstructorSymbol();
if (!globalESSymbol) {
// Already errored when we tried to look up the symbol
return false;
}
if (leftHandSideSymbol !== globalESSymbol) {
if (reportError) {
error(leftHandSide, Diagnostics.Symbol_reference_does_not_refer_to_the_global_Symbol_constructor_object);
}
return false;
}
return true;
}
function resolveUntypedCall(node: CallLikeExpression): Signature {
if (node.kind === SyntaxKind.TaggedTemplateExpression) {
checkExpression((<TaggedTemplateExpression>node).template);
}
else {
forEach((<CallExpression>node).arguments, argument => {
checkExpression(argument);
});
}
return anySignature;
}
function resolveErrorCall(node: CallLikeExpression): Signature {
resolveUntypedCall(node);
return unknownSignature;
}
// Re-order candidate signatures into the result array. Assumes the result array to be empty.
// The candidate list orders groups in reverse, but within a group signatures are kept in declaration order
// A nit here is that we reorder only signatures that belong to the same symbol,
// so order how inherited signatures are processed is still preserved.
// interface A { (x: string): void }
// interface B extends A { (x: 'foo'): string }
// let b: B;
// b('foo') // <- here overloads should be processed as [(x:'foo'): string, (x: string): void]
function reorderCandidates(signatures: Signature[], result: Signature[]): void {
let lastParent: Node;
let lastSymbol: Symbol;
let cutoffIndex: number = 0;
let index: number;
let specializedIndex: number = -1;
let spliceIndex: number;
Debug.assert(!result.length);
for (let signature of signatures) {
let symbol = signature.declaration && getSymbolOfNode(signature.declaration);
let parent = signature.declaration && signature.declaration.parent;
if (!lastSymbol || symbol === lastSymbol) {
if (lastParent && parent === lastParent) {
index++;
}
else {
lastParent = parent;
index = cutoffIndex;
}
}
else {
// current declaration belongs to a different symbol
// set cutoffIndex so re-orderings in the future won't change result set from 0 to cutoffIndex
index = cutoffIndex = result.length;
lastParent = parent;
}
lastSymbol = symbol;
// specialized signatures always need to be placed before non-specialized signatures regardless
// of the cutoff position; see GH#1133
if (signature.hasStringLiterals) {
specializedIndex++;
spliceIndex = specializedIndex;
// The cutoff index always needs to be greater than or equal to the specialized signature index
// in order to prevent non-specialized signatures from being added before a specialized
// signature.
cutoffIndex++;
}
else {
spliceIndex = index;
}
result.splice(spliceIndex, 0, signature);
}
}
function getSpreadArgumentIndex(args: Expression[]): number {
for (let i = 0; i < args.length; i++) {
if (args[i].kind === SyntaxKind.SpreadElementExpression) {
return i;
}
}
return -1;
}
function hasCorrectArity(node: CallLikeExpression, args: Expression[], signature: Signature) {
let adjustedArgCount: number; // Apparent number of arguments we will have in this call
let typeArguments: NodeArray<TypeNode>; // Type arguments (undefined if none)
let callIsIncomplete: boolean; // In incomplete call we want to be lenient when we have too few arguments
if (node.kind === SyntaxKind.TaggedTemplateExpression) {
let tagExpression = <TaggedTemplateExpression>node;
// Even if the call is incomplete, we'll have a missing expression as our last argument,
// so we can say the count is just the arg list length
adjustedArgCount = args.length;
typeArguments = undefined;
if (tagExpression.template.kind === SyntaxKind.TemplateExpression) {
// If a tagged template expression lacks a tail literal, the call is incomplete.
// Specifically, a template only can end in a TemplateTail or a Missing literal.
let templateExpression = <TemplateExpression>tagExpression.template;
let lastSpan = lastOrUndefined(templateExpression.templateSpans);
Debug.assert(lastSpan !== undefined); // we should always have at least one span.
callIsIncomplete = nodeIsMissing(lastSpan.literal) || !!lastSpan.literal.isUnterminated;
}
else {
// If the template didn't end in a backtick, or its beginning occurred right prior to EOF,
// then this might actually turn out to be a TemplateHead in the future;
// so we consider the call to be incomplete.
let templateLiteral = <LiteralExpression>tagExpression.template;
Debug.assert(templateLiteral.kind === SyntaxKind.NoSubstitutionTemplateLiteral);
callIsIncomplete = !!templateLiteral.isUnterminated;
}
}
else {
let callExpression = <CallExpression>node;
if (!callExpression.arguments) {
// This only happens when we have something of the form: 'new C'
Debug.assert(callExpression.kind === SyntaxKind.NewExpression);
return signature.minArgumentCount === 0;
}
// For IDE scenarios we may have an incomplete call, so a trailing comma is tantamount to adding another argument.
adjustedArgCount = callExpression.arguments.hasTrailingComma ? args.length + 1 : args.length;
// If we are missing the close paren, the call is incomplete.
callIsIncomplete = (<CallExpression>callExpression).arguments.end === callExpression.end;
typeArguments = callExpression.typeArguments;
}
// If the user supplied type arguments, but the number of type arguments does not match
// the declared number of type parameters, the call has an incorrect arity.
let hasRightNumberOfTypeArgs = !typeArguments ||
(signature.typeParameters && typeArguments.length === signature.typeParameters.length);
if (!hasRightNumberOfTypeArgs) {
return false;
}
// If spread arguments are present, check that they correspond to a rest parameter. If so, no
// further checking is necessary.
let spreadArgIndex = getSpreadArgumentIndex(args);
if (spreadArgIndex >= 0) {
return signature.hasRestParameter && spreadArgIndex >= signature.parameters.length - 1;
}
// Too many arguments implies incorrect arity.
if (!signature.hasRestParameter && adjustedArgCount > signature.parameters.length) {
return false;
}
// If the call is incomplete, we should skip the lower bound check.
let hasEnoughArguments = adjustedArgCount >= signature.minArgumentCount;
return callIsIncomplete || hasEnoughArguments;
}
// If type has a single call signature and no other members, return that signature. Otherwise, return undefined.
function getSingleCallSignature(type: Type): Signature {
if (type.flags & TypeFlags.ObjectType) {
let resolved = resolveObjectOrUnionTypeMembers(<ObjectType>type);
if (resolved.callSignatures.length === 1 && resolved.constructSignatures.length === 0 &&
resolved.properties.length === 0 && !resolved.stringIndexType && !resolved.numberIndexType) {
return resolved.callSignatures[0];
}
}
return undefined;
}
// Instantiate a generic signature in the context of a non-generic signature (section 3.8.5 in TypeScript spec)
function instantiateSignatureInContextOf(signature: Signature, contextualSignature: Signature, contextualMapper: TypeMapper): Signature {
let context = createInferenceContext(signature.typeParameters, /*inferUnionTypes*/ true);
forEachMatchingParameterType(contextualSignature, signature, (source, target) => {
// Type parameters from outer context referenced by source type are fixed by instantiation of the source type
inferTypes(context, instantiateType(source, contextualMapper), target);
});
return getSignatureInstantiation(signature, getInferredTypes(context));
}
function inferTypeArguments(signature: Signature, args: Expression[], excludeArgument: boolean[], context: InferenceContext): void {
let typeParameters = signature.typeParameters;
let inferenceMapper = createInferenceMapper(context);
// Clear out all the inference results from the last time inferTypeArguments was called on this context
for (let i = 0; i < typeParameters.length; i++) {
// As an optimization, we don't have to clear (and later recompute) inferred types
// for type parameters that have already been fixed on the previous call to inferTypeArguments.
// It would be just as correct to reset all of them. But then we'd be repeating the same work
// for the type parameters that were fixed, namely the work done by getInferredType.
if (!context.inferences[i].isFixed) {
context.inferredTypes[i] = undefined;
}
}
// On this call to inferTypeArguments, we may get more inferences for certain type parameters that were not
// fixed last time. This means that a type parameter that failed inference last time may succeed this time,
// or vice versa. Therefore, the failedTypeParameterIndex is useless if it points to an unfixed type parameter,
// because it may change. So here we reset it. However, getInferredType will not revisit any type parameters
// that were previously fixed. So if a fixed type parameter failed previously, it will fail again because
// it will contain the exact same set of inferences. So if we reset the index from a fixed type parameter,
// we will lose information that we won't recover this time around.
if (context.failedTypeParameterIndex !== undefined && !context.inferences[context.failedTypeParameterIndex].isFixed) {
context.failedTypeParameterIndex = undefined;
}
// We perform two passes over the arguments. In the first pass we infer from all arguments, but use
// wildcards for all context sensitive function expressions.
for (let i = 0; i < args.length; i++) {
let arg = args[i];
if (arg.kind !== SyntaxKind.OmittedExpression) {
let paramType = getTypeAtPosition(signature, i);
let argType: Type;
if (i === 0 && args[i].parent.kind === SyntaxKind.TaggedTemplateExpression) {
argType = globalTemplateStringsArrayType;
}
else {
// For context sensitive arguments we pass the identityMapper, which is a signal to treat all
// context sensitive function expressions as wildcards
let mapper = excludeArgument && excludeArgument[i] !== undefined ? identityMapper : inferenceMapper;
argType = checkExpressionWithContextualType(arg, paramType, mapper);
}
inferTypes(context, argType, paramType);
}
}
// In the second pass we visit only context sensitive arguments, and only those that aren't excluded, this
// time treating function expressions normally (which may cause previously inferred type arguments to be fixed
// as we construct types for contextually typed parameters)
if (excludeArgument) {
for (let i = 0; i < args.length; i++) {
// No need to check for omitted args and template expressions, their exlusion value is always undefined
if (excludeArgument[i] === false) {
let arg = args[i];
let paramType = getTypeAtPosition(signature, i);
inferTypes(context, checkExpressionWithContextualType(arg, paramType, inferenceMapper), paramType);
}
}
}
getInferredTypes(context);
}
function checkTypeArguments(signature: Signature, typeArguments: TypeNode[], typeArgumentResultTypes: Type[], reportErrors: boolean): boolean {
let typeParameters = signature.typeParameters;
let typeArgumentsAreAssignable = true;
for (let i = 0; i < typeParameters.length; i++) {
let typeArgNode = typeArguments[i];
let typeArgument = getTypeFromTypeNode(typeArgNode);
// Do not push on this array! It has a preallocated length
typeArgumentResultTypes[i] = typeArgument;
if (typeArgumentsAreAssignable /* so far */) {
let constraint = getConstraintOfTypeParameter(typeParameters[i]);
if (constraint) {
typeArgumentsAreAssignable = checkTypeAssignableTo(typeArgument, constraint, reportErrors ? typeArgNode : undefined,
Diagnostics.Type_0_does_not_satisfy_the_constraint_1);
}
}
}
return typeArgumentsAreAssignable;
}
function checkApplicableSignature(node: CallLikeExpression, args: Expression[], signature: Signature, relation: Map<RelationComparisonResult>, excludeArgument: boolean[], reportErrors: boolean) {
for (let i = 0; i < args.length; i++) {
let arg = args[i];
if (arg.kind !== SyntaxKind.OmittedExpression) {
// Check spread elements against rest type (from arity check we know spread argument corresponds to a rest parameter)
let paramType = getTypeAtPosition(signature, i);
// A tagged template expression provides a special first argument, and string literals get string literal types
// unless we're reporting errors
let argType = i === 0 && node.kind === SyntaxKind.TaggedTemplateExpression
? globalTemplateStringsArrayType
: arg.kind === SyntaxKind.StringLiteral && !reportErrors
? getStringLiteralType(<StringLiteral>arg)
: checkExpressionWithContextualType(arg, paramType, excludeArgument && excludeArgument[i] ? identityMapper : undefined);
// Use argument expression as error location when reporting errors
if (!checkTypeRelatedTo(argType, paramType, relation, reportErrors ? arg : undefined,
Diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1)) {
return false;
}
}
}
return true;
}
/**
* Returns the effective arguments for an expression that works like a function invocation.
*
* If 'node' is a CallExpression or a NewExpression, then its argument list is returned.
* If 'node' is a TaggedTemplateExpression, a new argument list is constructed from the substitution
* expressions, where the first element of the list is the template for error reporting purposes.
*/
function getEffectiveCallArguments(node: CallLikeExpression): Expression[] {
let args: Expression[];
if (node.kind === SyntaxKind.TaggedTemplateExpression) {
let template = (<TaggedTemplateExpression>node).template;
args = [template];
if (template.kind === SyntaxKind.TemplateExpression) {
forEach((<TemplateExpression>template).templateSpans, span => {
args.push(span.expression);
});
}
}
else {
args = (<CallExpression>node).arguments || emptyArray;
}
return args;
}
/**
* In a 'super' call, type arguments are not provided within the CallExpression node itself.
* Instead, they must be fetched from the class declaration's base type node.
*
* If 'node' is a 'super' call (e.g. super(...), new super(...)), then we attempt to fetch
* the type arguments off the containing class's first heritage clause (if one exists). Note that if
* type arguments are supplied on the 'super' call, they are ignored (though this is syntactically incorrect).
*
* In all other cases, the call's explicit type arguments are returned.
*/
function getEffectiveTypeArguments(callExpression: CallExpression): TypeNode[] {
if (callExpression.expression.kind === SyntaxKind.SuperKeyword) {
let containingClass = <ClassDeclaration>getAncestor(callExpression, SyntaxKind.ClassDeclaration);
let baseClassTypeNode = containingClass && getClassExtendsHeritageClauseElement(containingClass);
return baseClassTypeNode && baseClassTypeNode.typeArguments;
}
else {
// Ordinary case - simple function invocation.
return callExpression.typeArguments;
}
}
function resolveCall(node: CallLikeExpression, signatures: Signature[], candidatesOutArray: Signature[]): Signature {
let isTaggedTemplate = node.kind === SyntaxKind.TaggedTemplateExpression;
let typeArguments: TypeNode[];
if (!isTaggedTemplate) {
typeArguments = getEffectiveTypeArguments(<CallExpression>node);
// We already perform checking on the type arguments on the class declaration itself.
if ((<CallExpression>node).expression.kind !== SyntaxKind.SuperKeyword) {
forEach(typeArguments, checkSourceElement);
}
}
let candidates = candidatesOutArray || [];
// reorderCandidates fills up the candidates array directly
reorderCandidates(signatures, candidates);
if (!candidates.length) {
error(node, Diagnostics.Supplied_parameters_do_not_match_any_signature_of_call_target);
return resolveErrorCall(node);
}
let args = getEffectiveCallArguments(node);
// The following applies to any value of 'excludeArgument[i]':
// - true: the argument at 'i' is susceptible to a one-time permanent contextual typing.
// - undefined: the argument at 'i' is *not* susceptible to permanent contextual typing.
// - false: the argument at 'i' *was* and *has been* permanently contextually typed.
//
// The idea is that we will perform type argument inference & assignability checking once
// without using the susceptible parameters that are functions, and once more for each of those
// parameters, contextually typing each as we go along.
//
// For a tagged template, then the first argument be 'undefined' if necessary
// because it represents a TemplateStringsArray.
let excludeArgument: boolean[];
for (let i = isTaggedTemplate ? 1 : 0; i < args.length; i++) {
if (isContextSensitive(args[i])) {
if (!excludeArgument) {
excludeArgument = new Array(args.length);
}
excludeArgument[i] = true;
}
}
// The following variables are captured and modified by calls to chooseOverload.
// If overload resolution or type argument inference fails, we want to report the
// best error possible. The best error is one which says that an argument was not
// assignable to a parameter. This implies that everything else about the overload
// was fine. So if there is any overload that is only incorrect because of an
// argument, we will report an error on that one.
//
// function foo(s: string) {}
// function foo(n: number) {} // Report argument error on this overload
// function foo() {}
// foo(true);
//
// If none of the overloads even made it that far, there are two possibilities.
// There was a problem with type arguments for some overload, in which case
// report an error on that. Or none of the overloads even had correct arity,
// in which case give an arity error.
//
// function foo<T>(x: T, y: T) {} // Report type argument inference error
// function foo() {}
// foo(0, true);
//
let candidateForArgumentError: Signature;
let candidateForTypeArgumentError: Signature;
let resultOfFailedInference: InferenceContext;
let result: Signature;
// Section 4.12.1:
// if the candidate list contains one or more signatures for which the type of each argument
// expression is a subtype of each corresponding parameter type, the return type of the first
// of those signatures becomes the return type of the function call.
// Otherwise, the return type of the first signature in the candidate list becomes the return
// type of the function call.
//
// Whether the call is an error is determined by assignability of the arguments. The subtype pass
// is just important for choosing the best signature. So in the case where there is only one
// signature, the subtype pass is useless. So skipping it is an optimization.
if (candidates.length > 1) {
result = chooseOverload(candidates, subtypeRelation);
}
if (!result) {
// Reinitialize these pointers for round two
candidateForArgumentError = undefined;
candidateForTypeArgumentError = undefined;
resultOfFailedInference = undefined;
result = chooseOverload(candidates, assignableRelation);
}
if (result) {
return result;
}
// No signatures were applicable. Now report errors based on the last applicable signature with
// no arguments excluded from assignability checks.
// If candidate is undefined, it means that no candidates had a suitable arity. In that case,
// skip the checkApplicableSignature check.
if (candidateForArgumentError) {
// excludeArgument is undefined, in this case also equivalent to [undefined, undefined, ...]
// The importance of excludeArgument is to prevent us from typing function expression parameters
// in arguments too early. If possible, we'd like to only type them once we know the correct
// overload. However, this matters for the case where the call is correct. When the call is
// an error, we don't need to exclude any arguments, although it would cause no harm to do so.
checkApplicableSignature(node, args, candidateForArgumentError, assignableRelation, /*excludeArgument*/ undefined, /*reportErrors*/ true);
}
else if (candidateForTypeArgumentError) {
if (!isTaggedTemplate && (<CallExpression>node).typeArguments) {
checkTypeArguments(candidateForTypeArgumentError, (<CallExpression>node).typeArguments, [], /*reportErrors*/ true)
}
else {
Debug.assert(resultOfFailedInference.failedTypeParameterIndex >= 0);
let failedTypeParameter = candidateForTypeArgumentError.typeParameters[resultOfFailedInference.failedTypeParameterIndex];
let inferenceCandidates = getInferenceCandidates(resultOfFailedInference, resultOfFailedInference.failedTypeParameterIndex);
let diagnosticChainHead = chainDiagnosticMessages(/*details*/ undefined, // details will be provided by call to reportNoCommonSupertypeError
Diagnostics.The_type_argument_for_type_parameter_0_cannot_be_inferred_from_the_usage_Consider_specifying_the_type_arguments_explicitly,
typeToString(failedTypeParameter));
reportNoCommonSupertypeError(inferenceCandidates, (<CallExpression>node).expression || (<TaggedTemplateExpression>node).tag, diagnosticChainHead);
}
}
else {
error(node, Diagnostics.Supplied_parameters_do_not_match_any_signature_of_call_target);
}
// No signature was applicable. We have already reported the errors for the invalid signature.
// If this is a type resolution session, e.g. Language Service, try to get better information that anySignature.
// Pick the first candidate that matches the arity. This way we can get a contextual type for cases like:
// declare function f(a: { xa: number; xb: number; });
// f({ |
if (!produceDiagnostics) {
for (let candidate of candidates) {
if (hasCorrectArity(node, args, candidate)) {
return candidate;
}
}
}
return resolveErrorCall(node);
function chooseOverload(candidates: Signature[], relation: Map<RelationComparisonResult>) {
for (let originalCandidate of candidates) {
if (!hasCorrectArity(node, args, originalCandidate)) {
continue;
}
let candidate: Signature;
let typeArgumentsAreValid: boolean;
let inferenceContext = originalCandidate.typeParameters
? createInferenceContext(originalCandidate.typeParameters, /*inferUnionTypes*/ false)
: undefined;
while (true) {
candidate = originalCandidate;
if (candidate.typeParameters) {
let typeArgumentTypes: Type[];
if (typeArguments) {
typeArgumentTypes = new Array<Type>(candidate.typeParameters.length);
typeArgumentsAreValid = checkTypeArguments(candidate, typeArguments, typeArgumentTypes, /*reportErrors*/ false)
}
else {
inferTypeArguments(candidate, args, excludeArgument, inferenceContext);
typeArgumentsAreValid = inferenceContext.failedTypeParameterIndex === undefined;
typeArgumentTypes = inferenceContext.inferredTypes;
}
if (!typeArgumentsAreValid) {
break;
}
candidate = getSignatureInstantiation(candidate, typeArgumentTypes);
}
if (!checkApplicableSignature(node, args, candidate, relation, excludeArgument, /*reportErrors*/ false)) {
break;
}
let index = excludeArgument ? indexOf(excludeArgument, true) : -1;
if (index < 0) {
return candidate;
}
excludeArgument[index] = false;
}
// A post-mortem of this iteration of the loop. The signature was not applicable,
// so we want to track it as a candidate for reporting an error. If the candidate
// had no type parameters, or had no issues related to type arguments, we can
// report an error based on the arguments. If there was an issue with type
// arguments, then we can only report an error based on the type arguments.
if (originalCandidate.typeParameters) {
let instantiatedCandidate = candidate;
if (typeArgumentsAreValid) {
candidateForArgumentError = instantiatedCandidate;
}
else {
candidateForTypeArgumentError = originalCandidate;
if (!typeArguments) {
resultOfFailedInference = inferenceContext;
}
}
}
else {
Debug.assert(originalCandidate === candidate);
candidateForArgumentError = originalCandidate;
}
}
return undefined;
}
}
function resolveCallExpression(node: CallExpression, candidatesOutArray: Signature[]): Signature {
if (node.expression.kind === SyntaxKind.SuperKeyword) {
let superType = checkSuperExpression(node.expression);
if (superType !== unknownType) {
return resolveCall(node, getSignaturesOfType(superType, SignatureKind.Construct), candidatesOutArray);
}
return resolveUntypedCall(node);
}
let funcType = checkExpression(node.expression);
let apparentType = getApparentType(funcType);
if (apparentType === unknownType) {
// Another error has already been reported
return resolveErrorCall(node);
}
// Technically, this signatures list may be incomplete. We are taking the apparent type,
// but we are not including call signatures that may have been added to the Object or
// Function interface, since they have none by default. This is a bit of a leap of faith
// that the user will not add any.
let callSignatures = getSignaturesOfType(apparentType, SignatureKind.Call);
let constructSignatures = getSignaturesOfType(apparentType, SignatureKind.Construct);
// TS 1.0 spec: 4.12
// If FuncExpr is of type Any, or of an object type that has no call or construct signatures
// but is a subtype of the Function interface, the call is an untyped function call. In an
// untyped function call no TypeArgs are permitted, Args can be any argument list, no contextual
// types are provided for the argument expressions, and the result is always of type Any.
// We exclude union types because we may have a union of function types that happen to have
// no common signatures.
if (funcType === anyType || (!callSignatures.length && !constructSignatures.length && !(funcType.flags & TypeFlags.Union) && isTypeAssignableTo(funcType, globalFunctionType))) {
if (node.typeArguments) {
error(node, Diagnostics.Untyped_function_calls_may_not_accept_type_arguments);
}
return resolveUntypedCall(node);
}
// If FuncExpr's apparent type(section 3.8.1) is a function type, the call is a typed function call.
// TypeScript employs overload resolution in typed function calls in order to support functions
// with multiple call signatures.
if (!callSignatures.length) {
if (constructSignatures.length) {
error(node, Diagnostics.Value_of_type_0_is_not_callable_Did_you_mean_to_include_new, typeToString(funcType));
}
else {
error(node, Diagnostics.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature);
}
return resolveErrorCall(node);
}
return resolveCall(node, callSignatures, candidatesOutArray);
}
function resolveNewExpression(node: NewExpression, candidatesOutArray: Signature[]): Signature {
if (node.arguments && languageVersion < ScriptTarget.ES5) {
let spreadIndex = getSpreadArgumentIndex(node.arguments);
if (spreadIndex >= 0) {
error(node.arguments[spreadIndex], Diagnostics.Spread_operator_in_new_expressions_is_only_available_when_targeting_ECMAScript_5_and_higher);
}
}
let expressionType = checkExpression(node.expression);
// TS 1.0 spec: 4.11
// If ConstructExpr is of type Any, Args can be any argument
// list and the result of the operation is of type Any.
if (expressionType === anyType) {
if (node.typeArguments) {
error(node, Diagnostics.Untyped_function_calls_may_not_accept_type_arguments);
}
return resolveUntypedCall(node);
}
// If ConstructExpr's apparent type(section 3.8.1) is an object type with one or
// more construct signatures, the expression is processed in the same manner as a
// function call, but using the construct signatures as the initial set of candidate
// signatures for overload resolution. The result type of the function call becomes
// the result type of the operation.
expressionType = getApparentType(expressionType);
if (expressionType === unknownType) {
// Another error has already been reported
return resolveErrorCall(node);
}
// Technically, this signatures list may be incomplete. We are taking the apparent type,
// but we are not including construct signatures that may have been added to the Object or
// Function interface, since they have none by default. This is a bit of a leap of faith
// that the user will not add any.
let constructSignatures = getSignaturesOfType(expressionType, SignatureKind.Construct);
if (constructSignatures.length) {
return resolveCall(node, constructSignatures, candidatesOutArray);
}
// If ConstructExpr's apparent type is an object type with no construct signatures but
// one or more call signatures, the expression is processed as a function call. A compile-time
// error occurs if the result of the function call is not Void. The type of the result of the
// operation is Any.
let callSignatures = getSignaturesOfType(expressionType, SignatureKind.Call);
if (callSignatures.length) {
let signature = resolveCall(node, callSignatures, candidatesOutArray);
if (getReturnTypeOfSignature(signature) !== voidType) {
error(node, Diagnostics.Only_a_void_function_can_be_called_with_the_new_keyword);
}
return signature;
}
error(node, Diagnostics.Cannot_use_new_with_an_expression_whose_type_lacks_a_call_or_construct_signature);
return resolveErrorCall(node);
}
function resolveTaggedTemplateExpression(node: TaggedTemplateExpression, candidatesOutArray: Signature[]): Signature {
let tagType = checkExpression(node.tag);
let apparentType = getApparentType(tagType);
if (apparentType === unknownType) {
// Another error has already been reported
return resolveErrorCall(node);
}
let callSignatures = getSignaturesOfType(apparentType, SignatureKind.Call);
if (tagType === anyType || (!callSignatures.length && !(tagType.flags & TypeFlags.Union) && isTypeAssignableTo(tagType, globalFunctionType))) {
return resolveUntypedCall(node);
}
if (!callSignatures.length) {
error(node, Diagnostics.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature);
return resolveErrorCall(node);
}
return resolveCall(node, callSignatures, candidatesOutArray);
}
// candidatesOutArray is passed by signature help in the language service, and collectCandidates
// must fill it up with the appropriate candidate signatures
function getResolvedSignature(node: CallLikeExpression, candidatesOutArray?: Signature[]): Signature {
let links = getNodeLinks(node);
// If getResolvedSignature has already been called, we will have cached the resolvedSignature.
// However, it is possible that either candidatesOutArray was not passed in the first time,
// or that a different candidatesOutArray was passed in. Therefore, we need to redo the work
// to correctly fill the candidatesOutArray.
if (!links.resolvedSignature || candidatesOutArray) {
links.resolvedSignature = anySignature;
if (node.kind === SyntaxKind.CallExpression) {
links.resolvedSignature = resolveCallExpression(<CallExpression>node, candidatesOutArray);
}
else if (node.kind === SyntaxKind.NewExpression) {
links.resolvedSignature = resolveNewExpression(<NewExpression>node, candidatesOutArray);
}
else if (node.kind === SyntaxKind.TaggedTemplateExpression) {
links.resolvedSignature = resolveTaggedTemplateExpression(<TaggedTemplateExpression>node, candidatesOutArray);
}
else {
Debug.fail("Branch in 'getResolvedSignature' should be unreachable.");
}
}
return links.resolvedSignature;
}
function checkCallExpression(node: CallExpression): Type {
// Grammar checking; stop grammar-checking if checkGrammarTypeArguments return true
checkGrammarTypeArguments(node, node.typeArguments) || checkGrammarArguments(node, node.arguments);
let signature = getResolvedSignature(node);
if (node.expression.kind === SyntaxKind.SuperKeyword) {
return voidType;
}
if (node.kind === SyntaxKind.NewExpression) {
let declaration = signature.declaration;
if (declaration &&
declaration.kind !== SyntaxKind.Constructor &&
declaration.kind !== SyntaxKind.ConstructSignature &&
declaration.kind !== SyntaxKind.ConstructorType) {
// When resolved signature is a call signature (and not a construct signature) the result type is any
if (compilerOptions.noImplicitAny) {
error(node, Diagnostics.new_expression_whose_target_lacks_a_construct_signature_implicitly_has_an_any_type);
}
return anyType;
}
}
return getReturnTypeOfSignature(signature);
}
function checkTaggedTemplateExpression(node: TaggedTemplateExpression): Type {
return getReturnTypeOfSignature(getResolvedSignature(node));
}
function checkTypeAssertion(node: TypeAssertion): Type {
let exprType = checkExpression(node.expression);
let targetType = getTypeFromTypeNode(node.type);
if (produceDiagnostics && targetType !== unknownType) {
let widenedType = getWidenedType(exprType);
if (!(isTypeAssignableTo(targetType, widenedType))) {
checkTypeAssignableTo(exprType, targetType, node, Diagnostics.Neither_type_0_nor_type_1_is_assignable_to_the_other);
}
}
return targetType;
}
function getTypeAtPosition(signature: Signature, pos: number): Type {
return signature.hasRestParameter ?
pos < signature.parameters.length - 1 ? getTypeOfSymbol(signature.parameters[pos]) : getRestTypeOfSignature(signature) :
pos < signature.parameters.length ? getTypeOfSymbol(signature.parameters[pos]) : anyType;
}
function assignContextualParameterTypes(signature: Signature, context: Signature, mapper: TypeMapper) {
let len = signature.parameters.length - (signature.hasRestParameter ? 1 : 0);
for (let i = 0; i < len; i++) {
let parameter = signature.parameters[i];
let links = getSymbolLinks(parameter);
links.type = instantiateType(getTypeAtPosition(context, i), mapper);
}
if (signature.hasRestParameter && context.hasRestParameter && signature.parameters.length >= context.parameters.length) {
let parameter = lastOrUndefined(signature.parameters);
let links = getSymbolLinks(parameter);
links.type = instantiateType(getTypeOfSymbol(lastOrUndefined(context.parameters)), mapper);
}
}
function getReturnTypeFromBody(func: FunctionLikeDeclaration, contextualMapper?: TypeMapper): Type {
let contextualSignature = getContextualSignatureForFunctionLikeDeclaration(func);
if (!func.body) {
return unknownType;
}
let type: Type;
if (func.body.kind !== SyntaxKind.Block) {
type = checkExpressionCached(<Expression>func.body, contextualMapper);
}
else {
let types: Type[];
let funcIsGenerator = !!func.asteriskToken;
if (funcIsGenerator) {
types = checkAndAggregateYieldOperandTypes(<Block>func.body, contextualMapper);
if (types.length === 0) {
let iterableIteratorAny = createIterableIteratorType(anyType);
if (compilerOptions.noImplicitAny) {
error(func.asteriskToken,
Diagnostics.Generator_implicitly_has_type_0_because_it_does_not_yield_any_values_Consider_supplying_a_return_type, typeToString(iterableIteratorAny));
}
return iterableIteratorAny;
}
}
else {
types = checkAndAggregateReturnExpressionTypes(<Block>func.body, contextualMapper);
if (types.length === 0) {
return voidType;
}
}
// When yield/return statements are contextually typed we allow the return type to be a union type.
// Otherwise we require the yield/return expressions to have a best common supertype.
type = contextualSignature ? getUnionType(types) : getCommonSupertype(types);
if (!type) {
if (funcIsGenerator) {
error(func, Diagnostics.No_best_common_type_exists_among_yield_expressions);
return createIterableIteratorType(unknownType);
}
else {
error(func, Diagnostics.No_best_common_type_exists_among_return_expressions);
return unknownType;
}
}
if (funcIsGenerator) {
type = createIterableIteratorType(type);
}
}
if (!contextualSignature) {
reportErrorsFromWidening(func, type);
}
return getWidenedType(type);
}
function checkAndAggregateYieldOperandTypes(body: Block, contextualMapper?: TypeMapper): Type[] {
let aggregatedTypes: Type[] = [];
forEachYieldExpression(body, yieldExpression => {
let expr = yieldExpression.expression;
if (expr) {
let type = checkExpressionCached(expr, contextualMapper);
if (yieldExpression.asteriskToken) {
// A yield* expression effectively yields everything that its operand yields
type = checkElementTypeOfIterable(type, yieldExpression.expression);
}
if (!contains(aggregatedTypes, type)) {
aggregatedTypes.push(type);
}
}
});
return aggregatedTypes;
}
function checkAndAggregateReturnExpressionTypes(body: Block, contextualMapper?: TypeMapper): Type[] {
let aggregatedTypes: Type[] = [];
forEachReturnStatement(body, returnStatement => {
let expr = returnStatement.expression;
if (expr) {
let type = checkExpressionCached(expr, contextualMapper);
if (!contains(aggregatedTypes, type)) {
aggregatedTypes.push(type);
}
}
});
return aggregatedTypes;
}
function bodyContainsAReturnStatement(funcBody: Block) {
return forEachReturnStatement(funcBody, returnStatement => {
return true;
});
}
function bodyContainsSingleThrowStatement(body: Block) {
return (body.statements.length === 1) && (body.statements[0].kind === SyntaxKind.ThrowStatement);
}
// TypeScript Specification 1.0 (6.3) - July 2014
// An explicitly typed function whose return type isn't the Void or the Any type
// must have at least one return statement somewhere in its body.
// An exception to this rule is if the function implementation consists of a single 'throw' statement.
function checkIfNonVoidFunctionHasReturnExpressionsOrSingleThrowStatment(func: FunctionLikeDeclaration, returnType: Type): void {
if (!produceDiagnostics) {
return;
}
// Functions that return 'void' or 'any' don't need any return expressions.
if (returnType === voidType || returnType === anyType) {
return;
}
// If all we have is a function signature, or an arrow function with an expression body, then there is nothing to check.
if (nodeIsMissing(func.body) || func.body.kind !== SyntaxKind.Block) {
return;
}
let bodyBlock = <Block>func.body;
// Ensure the body has at least one return expression.
if (bodyContainsAReturnStatement(bodyBlock)) {
return;
}
// If there are no return expressions, then we need to check if
// the function body consists solely of a throw statement;
// this is to make an exception for unimplemented functions.
if (bodyContainsSingleThrowStatement(bodyBlock)) {
return;
}
// This function does not conform to the specification.
error(func.type, Diagnostics.A_function_whose_declared_type_is_neither_void_nor_any_must_return_a_value_or_consist_of_a_single_throw_statement);
}
function checkFunctionExpressionOrObjectLiteralMethod(node: FunctionExpression | MethodDeclaration, contextualMapper?: TypeMapper): Type {
Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isObjectLiteralMethod(node));
// Grammar checking
let hasGrammarError = checkGrammarDeclarationNameInStrictMode(node) || checkGrammarFunctionLikeDeclaration(node);
if (!hasGrammarError && node.kind === SyntaxKind.FunctionExpression) {
checkGrammarFunctionName(node.name) || checkGrammarForGenerator(node);
}
// The identityMapper object is used to indicate that function expressions are wildcards
if (contextualMapper === identityMapper && isContextSensitive(node)) {
return anyFunctionType;
}
let links = getNodeLinks(node);
let type = getTypeOfSymbol(node.symbol);
// Check if function expression is contextually typed and assign parameter types if so
if (!(links.flags & NodeCheckFlags.ContextChecked)) {
let contextualSignature = getContextualSignature(node);
// If a type check is started at a function expression that is an argument of a function call, obtaining the
// contextual type may recursively get back to here during overload resolution of the call. If so, we will have
// already assigned contextual types.
if (!(links.flags & NodeCheckFlags.ContextChecked)) {
links.flags |= NodeCheckFlags.ContextChecked;
if (contextualSignature) {
let signature = getSignaturesOfType(type, SignatureKind.Call)[0];
if (isContextSensitive(node)) {
assignContextualParameterTypes(signature, contextualSignature, contextualMapper || identityMapper);
}
if (!node.type && !signature.resolvedReturnType) {
let returnType = getReturnTypeFromBody(node, contextualMapper);
if (!signature.resolvedReturnType) {
signature.resolvedReturnType = returnType;
}
}
}
checkSignatureDeclaration(node);
}
}
if (produceDiagnostics && node.kind !== SyntaxKind.MethodDeclaration && node.kind !== SyntaxKind.MethodSignature) {
checkCollisionWithCapturedSuperVariable(node, (<FunctionExpression>node).name);
checkCollisionWithCapturedThisVariable(node, (<FunctionExpression>node).name);
}
return type;
}
function checkFunctionExpressionOrObjectLiteralMethodBody(node: FunctionExpression | MethodDeclaration) {
Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isObjectLiteralMethod(node));
if (node.type && !node.asteriskToken) {
checkIfNonVoidFunctionHasReturnExpressionsOrSingleThrowStatment(node, getTypeFromTypeNode(node.type));
}
if (node.body) {
if (node.body.kind === SyntaxKind.Block) {
checkSourceElement(node.body);
}
else {
let exprType = checkExpression(<Expression>node.body);
if (node.type) {
checkTypeAssignableTo(exprType, getTypeFromTypeNode(node.type), node.body, /*headMessage*/ undefined);
}
checkFunctionExpressionBodies(node.body);
}
}
}
function checkArithmeticOperandType(operand: Node, type: Type, diagnostic: DiagnosticMessage): boolean {
if (!allConstituentTypesHaveKind(type, TypeFlags.Any | TypeFlags.NumberLike)) {
error(operand, diagnostic);
return false;
}
return true;
}
function checkReferenceExpression(n: Node, invalidReferenceMessage: DiagnosticMessage, constantVariableMessage: DiagnosticMessage): boolean {
function findSymbol(n: Node): Symbol {
let symbol = getNodeLinks(n).resolvedSymbol;
// Because we got the symbol from the resolvedSymbol property, it might be of kind
// SymbolFlags.ExportValue. In this case it is necessary to get the actual export
// symbol, which will have the correct flags set on it.
return symbol && getExportSymbolOfValueSymbolIfExported(symbol);
}
function isReferenceOrErrorExpression(n: Node): boolean {
// TypeScript 1.0 spec (April 2014):
// Expressions are classified as values or references.
// References are the subset of expressions that are permitted as the target of an assignment.
// Specifically, references are combinations of identifiers(section 4.3), parentheses(section 4.7),
// and property accesses(section 4.10).
// All other expression constructs described in this chapter are classified as values.
switch (n.kind) {
case SyntaxKind.Identifier: {
let symbol = findSymbol(n);
// TypeScript 1.0 spec (April 2014): 4.3
// An identifier expression that references a variable or parameter is classified as a reference.
// An identifier expression that references any other kind of entity is classified as a value(and therefore cannot be the target of an assignment).
return !symbol || symbol === unknownSymbol || symbol === argumentsSymbol || (symbol.flags & SymbolFlags.Variable) !== 0;
}
case SyntaxKind.PropertyAccessExpression: {
let symbol = findSymbol(n);
// TypeScript 1.0 spec (April 2014): 4.10
// A property access expression is always classified as a reference.
// NOTE (not in spec): assignment to enum members should not be allowed
return !symbol || symbol === unknownSymbol || (symbol.flags & ~SymbolFlags.EnumMember) !== 0;
}
case SyntaxKind.ElementAccessExpression:
// old compiler doesn't check indexed assess
return true;
case SyntaxKind.ParenthesizedExpression:
return isReferenceOrErrorExpression((<ParenthesizedExpression>n).expression);
default:
return false;
}
}
function isConstVariableReference(n: Node): boolean {
switch (n.kind) {
case SyntaxKind.Identifier:
case SyntaxKind.PropertyAccessExpression: {
let symbol = findSymbol(n);
return symbol && (symbol.flags & SymbolFlags.Variable) !== 0 && (getDeclarationFlagsFromSymbol(symbol) & NodeFlags.Const) !== 0;
}
case SyntaxKind.ElementAccessExpression: {
let index = (<ElementAccessExpression>n).argumentExpression;
let symbol = findSymbol((<ElementAccessExpression>n).expression);
if (symbol && index && index.kind === SyntaxKind.StringLiteral) {
let name = (<LiteralExpression>index).text;
let prop = getPropertyOfType(getTypeOfSymbol(symbol), name);
return prop && (prop.flags & SymbolFlags.Variable) !== 0 && (getDeclarationFlagsFromSymbol(prop) & NodeFlags.Const) !== 0;
}
return false;
}
case SyntaxKind.ParenthesizedExpression:
return isConstVariableReference((<ParenthesizedExpression>n).expression);
default:
return false;
}
}
if (!isReferenceOrErrorExpression(n)) {
error(n, invalidReferenceMessage);
return false;
}
if (isConstVariableReference(n)) {
error(n, constantVariableMessage);
return false;
}
return true;
}
function checkDeleteExpression(node: DeleteExpression): Type {
// Grammar checking
if (node.parserContextFlags & ParserContextFlags.StrictMode && node.expression.kind === SyntaxKind.Identifier) {
// When a delete operator occurs within strict mode code, a SyntaxError is thrown if its
// UnaryExpression is a direct reference to a variable, function argument, or function name
grammarErrorOnNode(node.expression, Diagnostics.delete_cannot_be_called_on_an_identifier_in_strict_mode);
}
let operandType = checkExpression(node.expression);
return booleanType;
}
function checkTypeOfExpression(node: TypeOfExpression): Type {
let operandType = checkExpression(node.expression);
return stringType;
}
function checkVoidExpression(node: VoidExpression): Type {
let operandType = checkExpression(node.expression);
return undefinedType;
}
function checkPrefixUnaryExpression(node: PrefixUnaryExpression): Type {
// Grammar checking
// The identifier eval or arguments may not appear as the LeftHandSideExpression of an
// Assignment operator(11.13) or of a PostfixExpression(11.3) or as the UnaryExpression
// operated upon by a Prefix Increment(11.4.4) or a Prefix Decrement(11.4.5) operator
if ((node.operator === SyntaxKind.PlusPlusToken || node.operator === SyntaxKind.MinusMinusToken)) {
checkGrammarEvalOrArgumentsInStrictMode(node, <Identifier>node.operand);
}
let operandType = checkExpression(node.operand);
switch (node.operator) {
case SyntaxKind.PlusToken:
case SyntaxKind.MinusToken:
case SyntaxKind.TildeToken:
if (someConstituentTypeHasKind(operandType, TypeFlags.ESSymbol)) {
error(node.operand, Diagnostics.The_0_operator_cannot_be_applied_to_type_symbol, tokenToString(node.operator));
}
return numberType;
case SyntaxKind.ExclamationToken:
return booleanType;
case SyntaxKind.PlusPlusToken:
case SyntaxKind.MinusMinusToken:
let ok = checkArithmeticOperandType(node.operand, operandType, Diagnostics.An_arithmetic_operand_must_be_of_type_any_number_or_an_enum_type);
if (ok) {
// run check only if former checks succeeded to avoid reporting cascading errors
checkReferenceExpression(node.operand,
Diagnostics.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_property_or_indexer,
Diagnostics.The_operand_of_an_increment_or_decrement_operator_cannot_be_a_constant);
}
return numberType;
}
return unknownType;
}
function checkPostfixUnaryExpression(node: PostfixUnaryExpression): Type {
// Grammar checking
// The identifier eval or arguments may not appear as the LeftHandSideExpression of an
// Assignment operator(11.13) or of a PostfixExpression(11.3) or as the UnaryExpression
// operated upon by a Prefix Increment(11.4.4) or a Prefix Decrement(11.4.5) operator.
checkGrammarEvalOrArgumentsInStrictMode(node, <Identifier>node.operand);
let operandType = checkExpression(node.operand);
let ok = checkArithmeticOperandType(node.operand, operandType, Diagnostics.An_arithmetic_operand_must_be_of_type_any_number_or_an_enum_type);
if (ok) {
// run check only if former checks succeeded to avoid reporting cascading errors
checkReferenceExpression(node.operand,
Diagnostics.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_property_or_indexer,
Diagnostics.The_operand_of_an_increment_or_decrement_operator_cannot_be_a_constant);
}
return numberType;
}
// Just like isTypeOfKind below, except that it returns true if *any* constituent
// has this kind.
function someConstituentTypeHasKind(type: Type, kind: TypeFlags): boolean {
if (type.flags & kind) {
return true;
}
if (type.flags & TypeFlags.Union) {
let types = (<UnionType>type).types;
for (let current of types) {
if (current.flags & kind) {
return true;
}
}
return false;
}
return false;
}
// Return true if type has the given flags, or is a union type composed of types that all have those flags.
function allConstituentTypesHaveKind(type: Type, kind: TypeFlags): boolean {
if (type.flags & kind) {
return true;
}
if (type.flags & TypeFlags.Union) {
let types = (<UnionType>type).types;
for (let current of types) {
if (!(current.flags & kind)) {
return false;
}
}
return true;
}
return false;
}
function isConstEnumObjectType(type: Type): boolean {
return type.flags & (TypeFlags.ObjectType | TypeFlags.Anonymous) && type.symbol && isConstEnumSymbol(type.symbol);
}
function isConstEnumSymbol(symbol: Symbol): boolean {
return (symbol.flags & SymbolFlags.ConstEnum) !== 0;
}
function checkInstanceOfExpression(node: BinaryExpression, leftType: Type, rightType: Type): Type {
// TypeScript 1.0 spec (April 2014): 4.15.4
// The instanceof operator requires the left operand to be of type Any, an object type, or a type parameter type,
// and the right operand to be of type Any or a subtype of the 'Function' interface type.
// The result is always of the Boolean primitive type.
// NOTE: do not raise error if leftType is unknown as related error was already reported
if (allConstituentTypesHaveKind(leftType, TypeFlags.Primitive)) {
error(node.left, Diagnostics.The_left_hand_side_of_an_instanceof_expression_must_be_of_type_any_an_object_type_or_a_type_parameter);
}
// NOTE: do not raise error if right is unknown as related error was already reported
if (!(rightType.flags & TypeFlags.Any || isTypeSubtypeOf(rightType, globalFunctionType))) {
error(node.right, Diagnostics.The_right_hand_side_of_an_instanceof_expression_must_be_of_type_any_or_of_a_type_assignable_to_the_Function_interface_type);
}
return booleanType;
}
function checkInExpression(node: BinaryExpression, leftType: Type, rightType: Type): Type {
// TypeScript 1.0 spec (April 2014): 4.15.5
// The in operator requires the left operand to be of type Any, the String primitive type, or the Number primitive type,
// and the right operand to be of type Any, an object type, or a type parameter type.
// The result is always of the Boolean primitive type.
if (!allConstituentTypesHaveKind(leftType, TypeFlags.Any | TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbol)) {
error(node.left, Diagnostics.The_left_hand_side_of_an_in_expression_must_be_of_type_any_string_number_or_symbol);
}
if (!allConstituentTypesHaveKind(rightType, TypeFlags.Any | TypeFlags.ObjectType | TypeFlags.TypeParameter)) {
error(node.right, Diagnostics.The_right_hand_side_of_an_in_expression_must_be_of_type_any_an_object_type_or_a_type_parameter);
}
return booleanType;
}
function checkObjectLiteralAssignment(node: ObjectLiteralExpression, sourceType: Type, contextualMapper?: TypeMapper): Type {
let properties = node.properties;
for (let p of properties) {
if (p.kind === SyntaxKind.PropertyAssignment || p.kind === SyntaxKind.ShorthandPropertyAssignment) {
// TODO(andersh): Computed property support
let name = <Identifier>(<PropertyAssignment>p).name;
let type = sourceType.flags & TypeFlags.Any ? sourceType :
getTypeOfPropertyOfType(sourceType, name.text) ||
isNumericLiteralName(name.text) && getIndexTypeOfType(sourceType, IndexKind.Number) ||
getIndexTypeOfType(sourceType, IndexKind.String);
if (type) {
checkDestructuringAssignment((<PropertyAssignment>p).initializer || name, type);
}
else {
error(name, Diagnostics.Type_0_has_no_property_1_and_no_string_index_signature, typeToString(sourceType), declarationNameToString(name));
}
}
else {
error(p, Diagnostics.Property_assignment_expected);
}
}
return sourceType;
}
function checkArrayLiteralAssignment(node: ArrayLiteralExpression, sourceType: Type, contextualMapper?: TypeMapper): Type {
// This elementType will be used if the specific property corresponding to this index is not
// present (aka the tuple element property). This call also checks that the parentType is in
// fact an iterable or array (depending on target language).
let elementType = checkIteratedTypeOrElementType(sourceType, node, /*allowStringInput*/ false) || unknownType;
let elements = node.elements;
for (let i = 0; i < elements.length; i++) {
let e = elements[i];
if (e.kind !== SyntaxKind.OmittedExpression) {
if (e.kind !== SyntaxKind.SpreadElementExpression) {
let propName = "" + i;
let type = sourceType.flags & TypeFlags.Any ? sourceType :
isTupleLikeType(sourceType)
? getTypeOfPropertyOfType(sourceType, propName)
: elementType;
if (type) {
checkDestructuringAssignment(e, type, contextualMapper);
}
else {
if (isTupleType(sourceType)) {
error(e, Diagnostics.Tuple_type_0_with_length_1_cannot_be_assigned_to_tuple_with_length_2, typeToString(sourceType), (<TupleType>sourceType).elementTypes.length, elements.length);
}
else {
error(e, Diagnostics.Type_0_has_no_property_1, typeToString(sourceType), propName);
}
}
}
else {
if (i < elements.length - 1) {
error(e, Diagnostics.A_rest_element_must_be_last_in_an_array_destructuring_pattern);
}
else {
let restExpression = (<SpreadElementExpression>e).expression;
if (restExpression.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>restExpression).operatorToken.kind === SyntaxKind.EqualsToken) {
error((<BinaryExpression>restExpression).operatorToken, Diagnostics.A_rest_element_cannot_have_an_initializer);
}
else {
checkDestructuringAssignment(restExpression, createArrayType(elementType), contextualMapper);
}
}
}
}
}
return sourceType;
}
function checkDestructuringAssignment(target: Expression, sourceType: Type, contextualMapper?: TypeMapper): Type {
if (target.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>target).operatorToken.kind === SyntaxKind.EqualsToken) {
checkBinaryExpression(<BinaryExpression>target, contextualMapper);
target = (<BinaryExpression>target).left;
}
if (target.kind === SyntaxKind.ObjectLiteralExpression) {
return checkObjectLiteralAssignment(<ObjectLiteralExpression>target, sourceType, contextualMapper);
}
if (target.kind === SyntaxKind.ArrayLiteralExpression) {
return checkArrayLiteralAssignment(<ArrayLiteralExpression>target, sourceType, contextualMapper);
}
return checkReferenceAssignment(target, sourceType, contextualMapper);
}
function checkReferenceAssignment(target: Expression, sourceType: Type, contextualMapper?: TypeMapper): Type {
let targetType = checkExpression(target, contextualMapper);
if (checkReferenceExpression(target, Diagnostics.Invalid_left_hand_side_of_assignment_expression, Diagnostics.Left_hand_side_of_assignment_expression_cannot_be_a_constant)) {
checkTypeAssignableTo(sourceType, targetType, target, /*headMessage*/ undefined);
}
return sourceType;
}
function checkBinaryExpression(node: BinaryExpression, contextualMapper?: TypeMapper) {
// Grammar checking
if (isLeftHandSideExpression(node.left) && isAssignmentOperator(node.operatorToken.kind)) {
// ECMA 262 (Annex C) The identifier eval or arguments may not appear as the LeftHandSideExpression of an
// Assignment operator(11.13) or of a PostfixExpression(11.3)
checkGrammarEvalOrArgumentsInStrictMode(node, <Identifier>node.left);
}
let operator = node.operatorToken.kind;
if (operator === SyntaxKind.EqualsToken && (node.left.kind === SyntaxKind.ObjectLiteralExpression || node.left.kind === SyntaxKind.ArrayLiteralExpression)) {
return checkDestructuringAssignment(node.left, checkExpression(node.right, contextualMapper), contextualMapper);
}
let leftType = checkExpression(node.left, contextualMapper);
let rightType = checkExpression(node.right, contextualMapper);
switch (operator) {
case SyntaxKind.AsteriskToken:
case SyntaxKind.AsteriskEqualsToken:
case SyntaxKind.SlashToken:
case SyntaxKind.SlashEqualsToken:
case SyntaxKind.PercentToken:
case SyntaxKind.PercentEqualsToken:
case SyntaxKind.MinusToken:
case SyntaxKind.MinusEqualsToken:
case SyntaxKind.LessThanLessThanToken:
case SyntaxKind.LessThanLessThanEqualsToken:
case SyntaxKind.GreaterThanGreaterThanToken:
case SyntaxKind.GreaterThanGreaterThanEqualsToken:
case SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
case SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken:
case SyntaxKind.BarToken:
case SyntaxKind.BarEqualsToken:
case SyntaxKind.CaretToken:
case SyntaxKind.CaretEqualsToken:
case SyntaxKind.AmpersandToken:
case SyntaxKind.AmpersandEqualsToken:
// TypeScript 1.0 spec (April 2014): 4.15.1
// These operators require their operands to be of type Any, the Number primitive type,
// or an enum type. Operands of an enum type are treated
// as having the primitive type Number. If one operand is the null or undefined value,
// it is treated as having the type of the other operand.
// The result is always of the Number primitive type.
if (leftType.flags & (TypeFlags.Undefined | TypeFlags.Null)) leftType = rightType;
if (rightType.flags & (TypeFlags.Undefined | TypeFlags.Null)) rightType = leftType;
let suggestedOperator: SyntaxKind;
// if a user tries to apply a bitwise operator to 2 boolean operands
// try and return them a helpful suggestion
if ((leftType.flags & TypeFlags.Boolean) &&
(rightType.flags & TypeFlags.Boolean) &&
(suggestedOperator = getSuggestedBooleanOperator(node.operatorToken.kind)) !== undefined) {
error(node, Diagnostics.The_0_operator_is_not_allowed_for_boolean_types_Consider_using_1_instead, tokenToString(node.operatorToken.kind), tokenToString(suggestedOperator));
}
else {
// otherwise just check each operand separately and report errors as normal
let leftOk = checkArithmeticOperandType(node.left, leftType, Diagnostics.The_left_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_or_an_enum_type);
let rightOk = checkArithmeticOperandType(node.right, rightType, Diagnostics.The_right_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_or_an_enum_type);
if (leftOk && rightOk) {
checkAssignmentOperator(numberType);
}
}
return numberType;
case SyntaxKind.PlusToken:
case SyntaxKind.PlusEqualsToken:
// TypeScript 1.0 spec (April 2014): 4.15.2
// The binary + operator requires both operands to be of the Number primitive type or an enum type,
// or at least one of the operands to be of type Any or the String primitive type.
// If one operand is the null or undefined value, it is treated as having the type of the other operand.
if (leftType.flags & (TypeFlags.Undefined | TypeFlags.Null)) leftType = rightType;
if (rightType.flags & (TypeFlags.Undefined | TypeFlags.Null)) rightType = leftType;
let resultType: Type;
if (allConstituentTypesHaveKind(leftType, TypeFlags.NumberLike) && allConstituentTypesHaveKind(rightType, TypeFlags.NumberLike)) {
// Operands of an enum type are treated as having the primitive type Number.
// If both operands are of the Number primitive type, the result is of the Number primitive type.
resultType = numberType;
}
else {
if (allConstituentTypesHaveKind(leftType, TypeFlags.StringLike) || allConstituentTypesHaveKind(rightType, TypeFlags.StringLike)) {
// If one or both operands are of the String primitive type, the result is of the String primitive type.
resultType = stringType;
}
else if (leftType.flags & TypeFlags.Any || rightType.flags & TypeFlags.Any) {
// Otherwise, the result is of type Any.
// NOTE: unknown type here denotes error type. Old compiler treated this case as any type so do we.
resultType = anyType;
}
// Symbols are not allowed at all in arithmetic expressions
if (resultType && !checkForDisallowedESSymbolOperand(operator)) {
return resultType;
}
}
if (!resultType) {
reportOperatorError();
return anyType;
}
if (operator === SyntaxKind.PlusEqualsToken) {
checkAssignmentOperator(resultType);
}
return resultType;
case SyntaxKind.LessThanToken:
case SyntaxKind.GreaterThanToken:
case SyntaxKind.LessThanEqualsToken:
case SyntaxKind.GreaterThanEqualsToken:
if (!checkForDisallowedESSymbolOperand(operator)) {
return booleanType;
}
// Fall through
case SyntaxKind.EqualsEqualsToken:
case SyntaxKind.ExclamationEqualsToken:
case SyntaxKind.EqualsEqualsEqualsToken:
case SyntaxKind.ExclamationEqualsEqualsToken:
if (!isTypeAssignableTo(leftType, rightType) && !isTypeAssignableTo(rightType, leftType)) {
reportOperatorError();
}
return booleanType;
case SyntaxKind.InstanceOfKeyword:
return checkInstanceOfExpression(node, leftType, rightType);
case SyntaxKind.InKeyword:
return checkInExpression(node, leftType, rightType);
case SyntaxKind.AmpersandAmpersandToken:
return rightType;
case SyntaxKind.BarBarToken:
return getUnionType([leftType, rightType]);
case SyntaxKind.EqualsToken:
checkAssignmentOperator(rightType);
return rightType;
case SyntaxKind.CommaToken:
return rightType;
}
// Return true if there was no error, false if there was an error.
function checkForDisallowedESSymbolOperand(operator: SyntaxKind): boolean {
let offendingSymbolOperand =
someConstituentTypeHasKind(leftType, TypeFlags.ESSymbol) ? node.left :
someConstituentTypeHasKind(rightType, TypeFlags.ESSymbol) ? node.right :
undefined;
if (offendingSymbolOperand) {
error(offendingSymbolOperand, Diagnostics.The_0_operator_cannot_be_applied_to_type_symbol, tokenToString(operator));
return false;
}
return true;
}
function getSuggestedBooleanOperator(operator: SyntaxKind): SyntaxKind {
switch (operator) {
case SyntaxKind.BarToken:
case SyntaxKind.BarEqualsToken:
return SyntaxKind.BarBarToken;
case SyntaxKind.CaretToken:
case SyntaxKind.CaretEqualsToken:
return SyntaxKind.ExclamationEqualsEqualsToken;
case SyntaxKind.AmpersandToken:
case SyntaxKind.AmpersandEqualsToken:
return SyntaxKind.AmpersandAmpersandToken;
default:
return undefined;
}
}
function checkAssignmentOperator(valueType: Type): void {
if (produceDiagnostics && operator >= SyntaxKind.FirstAssignment && operator <= SyntaxKind.LastAssignment) {
// TypeScript 1.0 spec (April 2014): 4.17
// An assignment of the form
// VarExpr = ValueExpr
// requires VarExpr to be classified as a reference
// A compound assignment furthermore requires VarExpr to be classified as a reference (section 4.1)
// and the type of the non - compound operation to be assignable to the type of VarExpr.
let ok = checkReferenceExpression(node.left, Diagnostics.Invalid_left_hand_side_of_assignment_expression, Diagnostics.Left_hand_side_of_assignment_expression_cannot_be_a_constant);
// Use default messages
if (ok) {
// to avoid cascading errors check assignability only if 'isReference' check succeeded and no errors were reported
checkTypeAssignableTo(valueType, leftType, node.left, /*headMessage*/ undefined);
}
}
}
function reportOperatorError() {
error(node, Diagnostics.Operator_0_cannot_be_applied_to_types_1_and_2, tokenToString(node.operatorToken.kind), typeToString(leftType), typeToString(rightType));
}
}
function isYieldExpressionInClass(node: YieldExpression): boolean {
let current: Node = node
let parent = node.parent;
while (parent) {
if (isFunctionLike(parent) && current === (<FunctionLikeDeclaration>parent).body) {
return false;
}
else if (current.kind === SyntaxKind.ClassDeclaration || current.kind === SyntaxKind.ClassExpression) {
return true;
}
current = parent;
parent = parent.parent;
}
return false;
}
function checkYieldExpression(node: YieldExpression): Type {
// Grammar checking
if (!(node.parserContextFlags & ParserContextFlags.Yield) || isYieldExpressionInClass(node)) {
grammarErrorOnFirstToken(node, Diagnostics.A_yield_expression_is_only_allowed_in_a_generator_body);
}
if (node.expression) {
let func = getContainingFunction(node);
// If the user's code is syntactically correct, the func should always have a star. After all,
// we are in a yield context.
if (func && func.asteriskToken) {
let expressionType = checkExpressionCached(node.expression, /*contextualMapper*/ undefined);
let expressionElementType: Type;
let nodeIsYieldStar = !!node.asteriskToken;
if (nodeIsYieldStar) {
expressionElementType = checkElementTypeOfIterable(expressionType, node.expression);
}
// There is no point in doing an assignability check if the function
// has no explicit return type because the return type is directly computed
// from the yield expressions.
if (func.type) {
let signatureElementType = getElementTypeOfIterableIterator(getTypeFromTypeNode(func.type)) || anyType;
if (nodeIsYieldStar) {
checkTypeAssignableTo(expressionElementType, signatureElementType, node.expression, /*headMessage*/ undefined);
}
else {
checkTypeAssignableTo(expressionType, signatureElementType, node.expression, /*headMessage*/ undefined);
}
}
}
}
// Both yield and yield* expressions have type 'any'
return anyType;
}
function checkConditionalExpression(node: ConditionalExpression, contextualMapper?: TypeMapper): Type {
checkExpression(node.condition);
let type1 = checkExpression(node.whenTrue, contextualMapper);
let type2 = checkExpression(node.whenFalse, contextualMapper);
return getUnionType([type1, type2]);
}
function checkTemplateExpression(node: TemplateExpression): Type {
// We just want to check each expressions, but we are unconcerned with
// the type of each expression, as any value may be coerced into a string.
// It is worth asking whether this is what we really want though.
// A place where we actually *are* concerned with the expressions' types are
// in tagged templates.
forEach((<TemplateExpression>node).templateSpans, templateSpan => {
checkExpression(templateSpan.expression);
});
return stringType;
}
function checkExpressionWithContextualType(node: Expression, contextualType: Type, contextualMapper?: TypeMapper): Type {
let saveContextualType = node.contextualType;
node.contextualType = contextualType;
let result = checkExpression(node, contextualMapper);
node.contextualType = saveContextualType;
return result;
}
function checkExpressionCached(node: Expression, contextualMapper?: TypeMapper): Type {
let links = getNodeLinks(node);
if (!links.resolvedType) {
links.resolvedType = checkExpression(node, contextualMapper);
}
return links.resolvedType;
}
function checkPropertyAssignment(node: PropertyAssignment, contextualMapper?: TypeMapper): Type {
// Do not use hasDynamicName here, because that returns false for well known symbols.
// We want to perform checkComputedPropertyName for all computed properties, including
// well known symbols.
if (node.name.kind === SyntaxKind.ComputedPropertyName) {
checkComputedPropertyName(<ComputedPropertyName>node.name);
}
return checkExpression((<PropertyAssignment>node).initializer, contextualMapper);
}
function checkObjectLiteralMethod(node: MethodDeclaration, contextualMapper?: TypeMapper): Type {
// Grammar checking
checkGrammarMethod(node);
// Do not use hasDynamicName here, because that returns false for well known symbols.
// We want to perform checkComputedPropertyName for all computed properties, including
// well known symbols.
if (node.name.kind === SyntaxKind.ComputedPropertyName) {
checkComputedPropertyName(<ComputedPropertyName>node.name);
}
let uninstantiatedType = checkFunctionExpressionOrObjectLiteralMethod(node, contextualMapper);
return instantiateTypeWithSingleGenericCallSignature(node, uninstantiatedType, contextualMapper);
}
function instantiateTypeWithSingleGenericCallSignature(node: Expression | MethodDeclaration, type: Type, contextualMapper?: TypeMapper) {
if (contextualMapper && contextualMapper !== identityMapper) {
let signature = getSingleCallSignature(type);
if (signature && signature.typeParameters) {
let contextualType = getContextualType(<Expression>node);
if (contextualType) {
let contextualSignature = getSingleCallSignature(contextualType);
if (contextualSignature && !contextualSignature.typeParameters) {
return getOrCreateTypeFromSignature(instantiateSignatureInContextOf(signature, contextualSignature, contextualMapper));
}
}
}
}
return type;
}
function checkExpression(node: Expression, contextualMapper?: TypeMapper): Type {
checkGrammarIdentifierInStrictMode(node);
return checkExpressionOrQualifiedName(node, contextualMapper);
}
// Checks an expression and returns its type. The contextualMapper parameter serves two purposes: When
// contextualMapper is not undefined and not equal to the identityMapper function object it indicates that the
// expression is being inferentially typed (section 4.12.2 in spec) and provides the type mapper to use in
// conjunction with the generic contextual type. When contextualMapper is equal to the identityMapper function
// object, it serves as an indicator that all contained function and arrow expressions should be considered to
// have the wildcard function type; this form of type check is used during overload resolution to exclude
// contextually typed function and arrow expressions in the initial phase.
function checkExpressionOrQualifiedName(node: Expression | QualifiedName, contextualMapper?: TypeMapper): Type {
let type: Type;
if (node.kind == SyntaxKind.QualifiedName) {
type = checkQualifiedName(<QualifiedName>node);
}
else {
let uninstantiatedType = checkExpressionWorker(<Expression>node, contextualMapper);
type = instantiateTypeWithSingleGenericCallSignature(<Expression>node, uninstantiatedType, contextualMapper);
}
if (isConstEnumObjectType(type)) {
// enum object type for const enums are only permitted in:
// - 'left' in property access
// - 'object' in indexed access
// - target in rhs of import statement
let ok =
(node.parent.kind === SyntaxKind.PropertyAccessExpression && (<PropertyAccessExpression>node.parent).expression === node) ||
(node.parent.kind === SyntaxKind.ElementAccessExpression && (<ElementAccessExpression>node.parent).expression === node) ||
((node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.QualifiedName) && isInRightSideOfImportOrExportAssignment(<Identifier>node));
if (!ok) {
error(node, Diagnostics.const_enums_can_only_be_used_in_property_or_index_access_expressions_or_the_right_hand_side_of_an_import_declaration_or_export_assignment);
}
}
return type;
}
function checkNumericLiteral(node: LiteralExpression): Type {
// Grammar checking
checkGrammarNumericLiteral(node);
return numberType;
}
function checkExpressionWorker(node: Expression, contextualMapper: TypeMapper): Type {
switch (node.kind) {
case SyntaxKind.Identifier:
return checkIdentifier(<Identifier>node);
case SyntaxKind.ThisKeyword:
return checkThisExpression(node);
case SyntaxKind.SuperKeyword:
return checkSuperExpression(node);
case SyntaxKind.NullKeyword:
return nullType;
case SyntaxKind.TrueKeyword:
case SyntaxKind.FalseKeyword:
return booleanType;
case SyntaxKind.NumericLiteral:
return checkNumericLiteral(<LiteralExpression>node);
case SyntaxKind.TemplateExpression:
return checkTemplateExpression(<TemplateExpression>node);
case SyntaxKind.StringLiteral:
case SyntaxKind.NoSubstitutionTemplateLiteral:
return stringType;
case SyntaxKind.RegularExpressionLiteral:
return globalRegExpType;
case SyntaxKind.ArrayLiteralExpression:
return checkArrayLiteral(<ArrayLiteralExpression>node, contextualMapper);
case SyntaxKind.ObjectLiteralExpression:
return checkObjectLiteral(<ObjectLiteralExpression>node, contextualMapper);
case SyntaxKind.PropertyAccessExpression:
return checkPropertyAccessExpression(<PropertyAccessExpression>node);
case SyntaxKind.ElementAccessExpression:
return checkIndexedAccess(<ElementAccessExpression>node);
case SyntaxKind.CallExpression:
case SyntaxKind.NewExpression:
return checkCallExpression(<CallExpression>node);
case SyntaxKind.TaggedTemplateExpression:
return checkTaggedTemplateExpression(<TaggedTemplateExpression>node);
case SyntaxKind.TypeAssertionExpression:
return checkTypeAssertion(<TypeAssertion>node);
case SyntaxKind.ParenthesizedExpression:
return checkExpression((<ParenthesizedExpression>node).expression, contextualMapper);
case SyntaxKind.ClassExpression:
return checkClassExpression(<ClassExpression>node);
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
return checkFunctionExpressionOrObjectLiteralMethod(<FunctionExpression>node, contextualMapper);
case SyntaxKind.TypeOfExpression:
return checkTypeOfExpression(<TypeOfExpression>node);
case SyntaxKind.DeleteExpression:
return checkDeleteExpression(<DeleteExpression>node);
case SyntaxKind.VoidExpression:
return checkVoidExpression(<VoidExpression>node);
case SyntaxKind.PrefixUnaryExpression:
return checkPrefixUnaryExpression(<PrefixUnaryExpression>node);
case SyntaxKind.PostfixUnaryExpression:
return checkPostfixUnaryExpression(<PostfixUnaryExpression>node);
case SyntaxKind.BinaryExpression:
return checkBinaryExpression(<BinaryExpression>node, contextualMapper);
case SyntaxKind.ConditionalExpression:
return checkConditionalExpression(<ConditionalExpression>node, contextualMapper);
case SyntaxKind.SpreadElementExpression:
return checkSpreadElementExpression(<SpreadElementExpression>node, contextualMapper);
case SyntaxKind.OmittedExpression:
return undefinedType;
case SyntaxKind.YieldExpression:
return checkYieldExpression(<YieldExpression>node);
}
return unknownType;
}
// DECLARATION AND STATEMENT TYPE CHECKING
function checkTypeParameter(node: TypeParameterDeclaration) {
checkGrammarDeclarationNameInStrictMode(node);
// Grammar Checking
if (node.expression) {
grammarErrorOnFirstToken(node.expression, Diagnostics.Type_expected);
}
checkSourceElement(node.constraint);
if (produceDiagnostics) {
checkTypeParameterHasIllegalReferencesInConstraint(node);
checkTypeNameIsReserved(node.name, Diagnostics.Type_parameter_name_cannot_be_0);
}
// TODO: Check multiple declarations are identical
}
function checkParameter(node: ParameterDeclaration) {
// Grammar checking
// It is a SyntaxError if the Identifier "eval" or the Identifier "arguments" occurs as the
// Identifier in a PropertySetParameterList of a PropertyAssignment that is contained in strict code
// or if its FunctionBody is strict code(11.1.5).
// It is a SyntaxError if the identifier eval or arguments appears within a FormalParameterList of a
// strict mode FunctionLikeDeclaration or FunctionExpression(13.1)
// Grammar checking
checkGrammarDecorators(node) || checkGrammarModifiers(node) || checkGrammarEvalOrArgumentsInStrictMode(node, <Identifier>node.name);
checkVariableLikeDeclaration(node);
let func = getContainingFunction(node);
if (node.flags & NodeFlags.AccessibilityModifier) {
func = getContainingFunction(node);
if (!(func.kind === SyntaxKind.Constructor && nodeIsPresent(func.body))) {
error(node, Diagnostics.A_parameter_property_is_only_allowed_in_a_constructor_implementation);
}
}
if (node.questionToken && isBindingPattern(node.name) && func.body) {
error(node, Diagnostics.A_binding_pattern_parameter_cannot_be_optional_in_an_implementation_signature);
}
// Only check rest parameter type if it's not a binding pattern. Since binding patterns are
// not allowed in a rest parameter, we already have an error from checkGrammarParameterList.
if (node.dotDotDotToken && !isBindingPattern(node.name) && !isArrayType(getTypeOfSymbol(node.symbol))) {
error(node, Diagnostics.A_rest_parameter_must_be_of_an_array_type);
}
}
function isSyntacticallyValidGenerator(node: SignatureDeclaration): boolean {
if (!(<FunctionLikeDeclaration>node).asteriskToken || !(<FunctionLikeDeclaration>node).body) {
return false;
}
return node.kind === SyntaxKind.MethodDeclaration ||
node.kind === SyntaxKind.FunctionDeclaration ||
node.kind === SyntaxKind.FunctionExpression;
}
function checkSignatureDeclaration(node: SignatureDeclaration) {
// Grammar checking
if (node.kind === SyntaxKind.IndexSignature) {
checkGrammarIndexSignature(<SignatureDeclaration>node);
}
// TODO (yuisu): Remove this check in else-if when SyntaxKind.Construct is moved and ambient context is handled
else if (node.kind === SyntaxKind.FunctionType || node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.ConstructorType ||
node.kind === SyntaxKind.CallSignature || node.kind === SyntaxKind.Constructor ||
node.kind === SyntaxKind.ConstructSignature) {
checkGrammarFunctionLikeDeclaration(<FunctionLikeDeclaration>node);
}
checkTypeParameters(node.typeParameters);
forEach(node.parameters, checkParameter);
if (node.type) {
checkSourceElement(node.type);
}
if (produceDiagnostics) {
checkCollisionWithArgumentsInGeneratedCode(node);
if (compilerOptions.noImplicitAny && !node.type) {
switch (node.kind) {
case SyntaxKind.ConstructSignature:
error(node, Diagnostics.Construct_signature_which_lacks_return_type_annotation_implicitly_has_an_any_return_type);
break;
case SyntaxKind.CallSignature:
error(node, Diagnostics.Call_signature_which_lacks_return_type_annotation_implicitly_has_an_any_return_type);
break;
}
}
if (node.type) {
if (languageVersion >= ScriptTarget.ES6 && isSyntacticallyValidGenerator(node)) {
let returnType = getTypeFromTypeNode(node.type);
if (returnType === voidType) {
error(node.type, Diagnostics.A_generator_cannot_have_a_void_type_annotation);
}
else {
let generatorElementType = getElementTypeOfIterableIterator(returnType) || anyType;
let iterableIteratorInstantiation = createIterableIteratorType(generatorElementType);
// Naively, one could check that IterableIterator<any> is assignable to the return type annotation.
// However, that would not catch the error in the following case.
//
// interface BadGenerator extends Iterable<number>, Iterator<string> { }
// function* g(): BadGenerator { } // Iterable and Iterator have different types!
//
checkTypeAssignableTo(iterableIteratorInstantiation, returnType, node.type);
}
}
}
}
checkSpecializedSignatureDeclaration(node);
}
function checkTypeForDuplicateIndexSignatures(node: Node) {
if (node.kind === SyntaxKind.InterfaceDeclaration) {
let nodeSymbol = getSymbolOfNode(node);
// in case of merging interface declaration it is possible that we'll enter this check procedure several times for every declaration
// to prevent this run check only for the first declaration of a given kind
if (nodeSymbol.declarations.length > 0 && nodeSymbol.declarations[0] !== node) {
return;
}
}
// TypeScript 1.0 spec (April 2014)
// 3.7.4: An object type can contain at most one string index signature and one numeric index signature.
// 8.5: A class declaration can have at most one string index member declaration and one numeric index member declaration
let indexSymbol = getIndexSymbol(getSymbolOfNode(node));
if (indexSymbol) {
let seenNumericIndexer = false;
let seenStringIndexer = false;
for (let decl of indexSymbol.declarations) {
let declaration = <SignatureDeclaration>decl;
if (declaration.parameters.length === 1 && declaration.parameters[0].type) {
switch (declaration.parameters[0].type.kind) {
case SyntaxKind.StringKeyword:
if (!seenStringIndexer) {
seenStringIndexer = true;
}
else {
error(declaration, Diagnostics.Duplicate_string_index_signature);
}
break;
case SyntaxKind.NumberKeyword:
if (!seenNumericIndexer) {
seenNumericIndexer = true;
}
else {
error(declaration, Diagnostics.Duplicate_number_index_signature);
}
break;
}
}
}
}
}
function checkPropertyDeclaration(node: PropertyDeclaration) {
// Grammar checking
checkGrammarDecorators(node) || checkGrammarModifiers(node) || checkGrammarProperty(node) || checkGrammarComputedPropertyName(node.name);
checkVariableLikeDeclaration(node);
}
function checkMethodDeclaration(node: MethodDeclaration) {
// Grammar checking
checkGrammarMethod(node) || checkGrammarComputedPropertyName(node.name);
// Grammar checking for modifiers is done inside the function checkGrammarFunctionLikeDeclaration
checkFunctionLikeDeclaration(node);
}
function checkConstructorDeclaration(node: ConstructorDeclaration) {
// Grammar check on signature of constructor and modifier of the constructor is done in checkSignatureDeclaration function.
checkSignatureDeclaration(node);
// Grammar check for checking only related to constructoDeclaration
checkGrammarConstructorTypeParameters(node) || checkGrammarConstructorTypeAnnotation(node);
checkSourceElement(node.body);
let symbol = getSymbolOfNode(node);
let firstDeclaration = getDeclarationOfKind(symbol, node.kind);
// Only type check the symbol once
if (node === firstDeclaration) {
checkFunctionOrConstructorSymbol(symbol);
}
// exit early in the case of signature - super checks are not relevant to them
if (nodeIsMissing(node.body)) {
return;
}
if (!produceDiagnostics) {
return;
}
function isSuperCallExpression(n: Node): boolean {
return n.kind === SyntaxKind.CallExpression && (<CallExpression>n).expression.kind === SyntaxKind.SuperKeyword;
}
function containsSuperCall(n: Node): boolean {
if (isSuperCallExpression(n)) {
return true;
}
switch (n.kind) {
case SyntaxKind.FunctionExpression:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.ArrowFunction:
case SyntaxKind.ObjectLiteralExpression: return false;
default: return forEachChild(n, containsSuperCall);
}
}
function markThisReferencesAsErrors(n: Node): void {
if (n.kind === SyntaxKind.ThisKeyword) {
error(n, Diagnostics.this_cannot_be_referenced_in_current_location);
}
else if (n.kind !== SyntaxKind.FunctionExpression && n.kind !== SyntaxKind.FunctionDeclaration) {
forEachChild(n, markThisReferencesAsErrors);
}
}
function isInstancePropertyWithInitializer(n: Node): boolean {
return n.kind === SyntaxKind.PropertyDeclaration &&
!(n.flags & NodeFlags.Static) &&
!!(<PropertyDeclaration>n).initializer;
}
// TS 1.0 spec (April 2014): 8.3.2
// Constructors of classes with no extends clause may not contain super calls, whereas
// constructors of derived classes must contain at least one super call somewhere in their function body.
if (getClassExtendsHeritageClauseElement(<ClassDeclaration>node.parent)) {
if (containsSuperCall(node.body)) {
// The first statement in the body of a constructor must be a super call if both of the following are true:
// - The containing class is a derived class.
// - The constructor declares parameter properties
// or the containing class declares instance member variables with initializers.
let superCallShouldBeFirst =
forEach((<ClassDeclaration>node.parent).members, isInstancePropertyWithInitializer) ||
forEach(node.parameters, p => p.flags & (NodeFlags.Public | NodeFlags.Private | NodeFlags.Protected));
if (superCallShouldBeFirst) {
let statements = (<Block>node.body).statements;
if (!statements.length || statements[0].kind !== SyntaxKind.ExpressionStatement || !isSuperCallExpression((<ExpressionStatement>statements[0]).expression)) {
error(node, Diagnostics.A_super_call_must_be_the_first_statement_in_the_constructor_when_a_class_contains_initialized_properties_or_has_parameter_properties);
}
else {
// In such a required super call, it is a compile-time error for argument expressions to reference this.
markThisReferencesAsErrors((<ExpressionStatement>statements[0]).expression);
}
}
}
else {
error(node, Diagnostics.Constructors_for_derived_classes_must_contain_a_super_call);
}
}
}
function checkAccessorDeclaration(node: AccessorDeclaration) {
if (produceDiagnostics) {
// Grammar checking accessors
checkGrammarFunctionLikeDeclaration(node) || checkGrammarAccessor(node) || checkGrammarComputedPropertyName(node.name);
if (node.kind === SyntaxKind.GetAccessor) {
if (!isInAmbientContext(node) && nodeIsPresent(node.body) && !(bodyContainsAReturnStatement(<Block>node.body) || bodyContainsSingleThrowStatement(<Block>node.body))) {
error(node.name, Diagnostics.A_get_accessor_must_return_a_value_or_consist_of_a_single_throw_statement);
}
}
if (!hasDynamicName(node)) {
// TypeScript 1.0 spec (April 2014): 8.4.3
// Accessors for the same member name must specify the same accessibility.
let otherKind = node.kind === SyntaxKind.GetAccessor ? SyntaxKind.SetAccessor : SyntaxKind.GetAccessor;
let otherAccessor = <AccessorDeclaration>getDeclarationOfKind(node.symbol, otherKind);
if (otherAccessor) {
if (((node.flags & NodeFlags.AccessibilityModifier) !== (otherAccessor.flags & NodeFlags.AccessibilityModifier))) {
error(node.name, Diagnostics.Getter_and_setter_accessors_do_not_agree_in_visibility);
}
let currentAccessorType = getAnnotatedAccessorType(node);
let otherAccessorType = getAnnotatedAccessorType(otherAccessor);
// TypeScript 1.0 spec (April 2014): 4.5
// If both accessors include type annotations, the specified types must be identical.
if (currentAccessorType && otherAccessorType) {
if (!isTypeIdenticalTo(currentAccessorType, otherAccessorType)) {
error(node, Diagnostics.get_and_set_accessor_must_have_the_same_type);
}
}
}
}
getTypeOfAccessors(getSymbolOfNode(node));
}
checkFunctionLikeDeclaration(node);
}
function checkMissingDeclaration(node: Node) {
checkDecorators(node);
}
function checkTypeReferenceNode(node: TypeReferenceNode) {
checkGrammarTypeReferenceInStrictMode(node.typeName);
return checkTypeReferenceOrExpressionWithTypeArguments(node);
}
function checkExpressionWithTypeArguments(node: ExpressionWithTypeArguments) {
checkGrammarExpressionWithTypeArgumentsInStrictMode(<PropertyAccessExpression>node.expression);
return checkTypeReferenceOrExpressionWithTypeArguments(node);
}
function checkTypeReferenceOrExpressionWithTypeArguments(node: TypeReferenceNode | ExpressionWithTypeArguments) {
// Grammar checking
checkGrammarTypeArguments(node, node.typeArguments);
let type = getTypeFromTypeReferenceOrExpressionWithTypeArguments(node);
if (type !== unknownType && node.typeArguments) {
// Do type argument local checks only if referenced type is successfully resolved
let len = node.typeArguments.length;
for (let i = 0; i < len; i++) {
checkSourceElement(node.typeArguments[i]);
let constraint = getConstraintOfTypeParameter((<TypeReference>type).target.typeParameters[i]);
if (produceDiagnostics && constraint) {
let typeArgument = (<TypeReference>type).typeArguments[i];
checkTypeAssignableTo(typeArgument, constraint, node, Diagnostics.Type_0_does_not_satisfy_the_constraint_1);
}
}
}
}
function checkTypeQuery(node: TypeQueryNode) {
getTypeFromTypeQueryNode(node);
}
function checkTypeLiteral(node: TypeLiteralNode) {
forEach(node.members, checkSourceElement);
if (produceDiagnostics) {
let type = getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node);
checkIndexConstraints(type);
checkTypeForDuplicateIndexSignatures(node);
}
}
function checkArrayType(node: ArrayTypeNode) {
checkSourceElement(node.elementType);
}
function checkTupleType(node: TupleTypeNode) {
// Grammar checking
let hasErrorFromDisallowedTrailingComma = checkGrammarForDisallowedTrailingComma(node.elementTypes);
if (!hasErrorFromDisallowedTrailingComma && node.elementTypes.length === 0) {
grammarErrorOnNode(node, Diagnostics.A_tuple_type_element_list_cannot_be_empty);
}
forEach(node.elementTypes, checkSourceElement);
}
function checkUnionType(node: UnionTypeNode) {
forEach(node.types, checkSourceElement);
}
function isPrivateWithinAmbient(node: Node): boolean {
return (node.flags & NodeFlags.Private) && isInAmbientContext(node);
}
function checkSpecializedSignatureDeclaration(signatureDeclarationNode: SignatureDeclaration): void {
if (!produceDiagnostics) {
return;
}
let signature = getSignatureFromDeclaration(signatureDeclarationNode);
if (!signature.hasStringLiterals) {
return;
}
// TypeScript 1.0 spec (April 2014): 3.7.2.2
// Specialized signatures are not permitted in conjunction with a function body
if (nodeIsPresent((<FunctionLikeDeclaration>signatureDeclarationNode).body)) {
error(signatureDeclarationNode, Diagnostics.A_signature_with_an_implementation_cannot_use_a_string_literal_type);
return;
}
// TypeScript 1.0 spec (April 2014): 3.7.2.4
// Every specialized call or construct signature in an object type must be assignable
// to at least one non-specialized call or construct signature in the same object type
let signaturesToCheck: Signature[];
// Unnamed (call\construct) signatures in interfaces are inherited and not shadowed so examining just node symbol won't give complete answer.
// Use declaring type to obtain full list of signatures.
if (!signatureDeclarationNode.name && signatureDeclarationNode.parent && signatureDeclarationNode.parent.kind === SyntaxKind.InterfaceDeclaration) {
Debug.assert(signatureDeclarationNode.kind === SyntaxKind.CallSignature || signatureDeclarationNode.kind === SyntaxKind.ConstructSignature);
let signatureKind = signatureDeclarationNode.kind === SyntaxKind.CallSignature ? SignatureKind.Call : SignatureKind.Construct;
let containingSymbol = getSymbolOfNode(signatureDeclarationNode.parent);
let containingType = getDeclaredTypeOfSymbol(containingSymbol);
signaturesToCheck = getSignaturesOfType(containingType, signatureKind);
}
else {
signaturesToCheck = getSignaturesOfSymbol(getSymbolOfNode(signatureDeclarationNode));
}
for (let otherSignature of signaturesToCheck) {
if (!otherSignature.hasStringLiterals && isSignatureAssignableTo(signature, otherSignature)) {
return;
}
}
error(signatureDeclarationNode, Diagnostics.Specialized_overload_signature_is_not_assignable_to_any_non_specialized_signature);
}
function getEffectiveDeclarationFlags(n: Node, flagsToCheck: NodeFlags) {
let flags = getCombinedNodeFlags(n);
if (n.parent.kind !== SyntaxKind.InterfaceDeclaration && isInAmbientContext(n)) {
if (!(flags & NodeFlags.Ambient)) {
// It is nested in an ambient context, which means it is automatically exported
flags |= NodeFlags.Export;
}
flags |= NodeFlags.Ambient;
}
return flags & flagsToCheck;
}
function checkFunctionOrConstructorSymbol(symbol: Symbol): void {
if (!produceDiagnostics) {
return;
}
function getCanonicalOverload(overloads: Declaration[], implementation: FunctionLikeDeclaration) {
// Consider the canonical set of flags to be the flags of the bodyDeclaration or the first declaration
// Error on all deviations from this canonical set of flags
// The caveat is that if some overloads are defined in lib.d.ts, we don't want to
// report the errors on those. To achieve this, we will say that the implementation is
// the canonical signature only if it is in the same container as the first overload
let implementationSharesContainerWithFirstOverload = implementation !== undefined && implementation.parent === overloads[0].parent;
return implementationSharesContainerWithFirstOverload ? implementation : overloads[0];
}
function checkFlagAgreementBetweenOverloads(overloads: Declaration[], implementation: FunctionLikeDeclaration, flagsToCheck: NodeFlags, someOverloadFlags: NodeFlags, allOverloadFlags: NodeFlags): void {
// Error if some overloads have a flag that is not shared by all overloads. To find the
// deviations, we XOR someOverloadFlags with allOverloadFlags
let someButNotAllOverloadFlags = someOverloadFlags ^ allOverloadFlags;
if (someButNotAllOverloadFlags !== 0) {
let canonicalFlags = getEffectiveDeclarationFlags(getCanonicalOverload(overloads, implementation), flagsToCheck);
forEach(overloads, o => {
let deviation = getEffectiveDeclarationFlags(o, flagsToCheck) ^ canonicalFlags;
if (deviation & NodeFlags.Export) {
error(o.name, Diagnostics.Overload_signatures_must_all_be_exported_or_not_exported);
}
else if (deviation & NodeFlags.Ambient) {
error(o.name, Diagnostics.Overload_signatures_must_all_be_ambient_or_non_ambient);
}
else if (deviation & (NodeFlags.Private | NodeFlags.Protected)) {
error(o.name, Diagnostics.Overload_signatures_must_all_be_public_private_or_protected);
}
});
}
}
function checkQuestionTokenAgreementBetweenOverloads(overloads: Declaration[], implementation: FunctionLikeDeclaration, someHaveQuestionToken: boolean, allHaveQuestionToken: boolean): void {
if (someHaveQuestionToken !== allHaveQuestionToken) {
let canonicalHasQuestionToken = hasQuestionToken(getCanonicalOverload(overloads, implementation));
forEach(overloads, o => {
let deviation = hasQuestionToken(o) !== canonicalHasQuestionToken;
if (deviation) {
error(o.name, Diagnostics.Overload_signatures_must_all_be_optional_or_required);
}
});
}
}
let flagsToCheck: NodeFlags = NodeFlags.Export | NodeFlags.Ambient | NodeFlags.Private | NodeFlags.Protected;
let someNodeFlags: NodeFlags = 0;
let allNodeFlags = flagsToCheck;
let someHaveQuestionToken = false;
let allHaveQuestionToken = true;
let hasOverloads = false;
let bodyDeclaration: FunctionLikeDeclaration;
let lastSeenNonAmbientDeclaration: FunctionLikeDeclaration;
let previousDeclaration: FunctionLikeDeclaration;
let declarations = symbol.declarations;
let isConstructor = (symbol.flags & SymbolFlags.Constructor) !== 0;
function reportImplementationExpectedError(node: FunctionLikeDeclaration): void {
if (node.name && nodeIsMissing(node.name)) {
return;
}
let seen = false;
let subsequentNode = forEachChild(node.parent, c => {
if (seen) {
return c;
}
else {
seen = c === node;
}
});
if (subsequentNode) {
if (subsequentNode.kind === node.kind) {
let errorNode: Node = (<FunctionLikeDeclaration>subsequentNode).name || subsequentNode;
// TODO(jfreeman): These are methods, so handle computed name case
if (node.name && (<FunctionLikeDeclaration>subsequentNode).name && (<Identifier>node.name).text === (<Identifier>(<FunctionLikeDeclaration>subsequentNode).name).text) {
// the only situation when this is possible (same kind\same name but different symbol) - mixed static and instance class members
Debug.assert(node.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.MethodSignature);
Debug.assert((node.flags & NodeFlags.Static) !== (subsequentNode.flags & NodeFlags.Static));
let diagnostic = node.flags & NodeFlags.Static ? Diagnostics.Function_overload_must_be_static : Diagnostics.Function_overload_must_not_be_static;
error(errorNode, diagnostic);
return;
}
else if (nodeIsPresent((<FunctionLikeDeclaration>subsequentNode).body)) {
error(errorNode, Diagnostics.Function_implementation_name_must_be_0, declarationNameToString(node.name));
return;
}
}
}
let errorNode: Node = node.name || node;
if (isConstructor) {
error(errorNode, Diagnostics.Constructor_implementation_is_missing);
}
else {
error(errorNode, Diagnostics.Function_implementation_is_missing_or_not_immediately_following_the_declaration);
}
}
// when checking exported function declarations across modules check only duplicate implementations
// names and consistency of modifiers are verified when we check local symbol
let isExportSymbolInsideModule = symbol.parent && symbol.parent.flags & SymbolFlags.Module;
let duplicateFunctionDeclaration = false;
let multipleConstructorImplementation = false;
for (let current of declarations) {
let node = <FunctionLikeDeclaration>current;
let inAmbientContext = isInAmbientContext(node);
let inAmbientContextOrInterface = node.parent.kind === SyntaxKind.InterfaceDeclaration || node.parent.kind === SyntaxKind.TypeLiteral || inAmbientContext;
if (inAmbientContextOrInterface) {
// check if declarations are consecutive only if they are non-ambient
// 1. ambient declarations can be interleaved
// i.e. this is legal
// declare function foo();
// declare function bar();
// declare function foo();
// 2. mixing ambient and non-ambient declarations is a separate error that will be reported - do not want to report an extra one
previousDeclaration = undefined;
}
if (node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.MethodSignature || node.kind === SyntaxKind.Constructor) {
let currentNodeFlags = getEffectiveDeclarationFlags(node, flagsToCheck);
someNodeFlags |= currentNodeFlags;
allNodeFlags &= currentNodeFlags;
someHaveQuestionToken = someHaveQuestionToken || hasQuestionToken(node);
allHaveQuestionToken = allHaveQuestionToken && hasQuestionToken(node);
if (nodeIsPresent(node.body) && bodyDeclaration) {
if (isConstructor) {
multipleConstructorImplementation = true;
}
else {
duplicateFunctionDeclaration = true;
}
}
else if (!isExportSymbolInsideModule && previousDeclaration && previousDeclaration.parent === node.parent && previousDeclaration.end !== node.pos) {
reportImplementationExpectedError(previousDeclaration);
}
if (nodeIsPresent(node.body)) {
if (!bodyDeclaration) {
bodyDeclaration = node;
}
}
else {
hasOverloads = true;
}
previousDeclaration = node;
if (!inAmbientContextOrInterface) {
lastSeenNonAmbientDeclaration = node;
}
}
}
if (multipleConstructorImplementation) {
forEach(declarations, declaration => {
error(declaration, Diagnostics.Multiple_constructor_implementations_are_not_allowed);
});
}
if (duplicateFunctionDeclaration) {
forEach(declarations, declaration => {
error(declaration.name, Diagnostics.Duplicate_function_implementation);
});
}
if (!isExportSymbolInsideModule && lastSeenNonAmbientDeclaration && !lastSeenNonAmbientDeclaration.body) {
reportImplementationExpectedError(lastSeenNonAmbientDeclaration);
}
if (hasOverloads) {
checkFlagAgreementBetweenOverloads(declarations, bodyDeclaration, flagsToCheck, someNodeFlags, allNodeFlags);
checkQuestionTokenAgreementBetweenOverloads(declarations, bodyDeclaration, someHaveQuestionToken, allHaveQuestionToken);
if (bodyDeclaration) {
let signatures = getSignaturesOfSymbol(symbol);
let bodySignature = getSignatureFromDeclaration(bodyDeclaration);
// If the implementation signature has string literals, we will have reported an error in
// checkSpecializedSignatureDeclaration
if (!bodySignature.hasStringLiterals) {
// TypeScript 1.0 spec (April 2014): 6.1
// If a function declaration includes overloads, the overloads determine the call
// signatures of the type given to the function object
// and the function implementation signature must be assignable to that type
//
// TypeScript 1.0 spec (April 2014): 3.8.4
// Note that specialized call and construct signatures (section 3.7.2.4) are not significant when determining assignment compatibility
// Consider checking against specialized signatures too. Not doing so creates a type hole:
//
// function g(x: "hi", y: boolean);
// function g(x: string, y: {});
// function g(x: string, y: string) { }
//
// The implementation is completely unrelated to the specialized signature, yet we do not check this.
for (let signature of signatures) {
if (!signature.hasStringLiterals && !isSignatureAssignableTo(bodySignature, signature)) {
error(signature.declaration, Diagnostics.Overload_signature_is_not_compatible_with_function_implementation);
break;
}
}
}
}
}
}
function checkExportsOnMergedDeclarations(node: Node): void {
if (!produceDiagnostics) {
return;
}
// Exports should be checked only if enclosing module contains both exported and non exported declarations.
// In case if all declarations are non-exported check is unnecessary.
// if localSymbol is defined on node then node itself is exported - check is required
let symbol = node.localSymbol;
if (!symbol) {
// local symbol is undefined => this declaration is non-exported.
// however symbol might contain other declarations that are exported
symbol = getSymbolOfNode(node);
if (!(symbol.flags & SymbolFlags.Export)) {
// this is a pure local symbol (all declarations are non-exported) - no need to check anything
return;
}
}
// run the check only for the first declaration in the list
if (getDeclarationOfKind(symbol, node.kind) !== node) {
return;
}
// we use SymbolFlags.ExportValue, SymbolFlags.ExportType and SymbolFlags.ExportNamespace
// to denote disjoint declarationSpaces (without making new enum type).
let exportedDeclarationSpaces: SymbolFlags = 0;
let nonExportedDeclarationSpaces: SymbolFlags = 0;
forEach(symbol.declarations, d => {
let declarationSpaces = getDeclarationSpaces(d);
if (getEffectiveDeclarationFlags(d, NodeFlags.Export)) {
exportedDeclarationSpaces |= declarationSpaces;
}
else {
nonExportedDeclarationSpaces |= declarationSpaces;
}
});
let commonDeclarationSpace = exportedDeclarationSpaces & nonExportedDeclarationSpaces;
if (commonDeclarationSpace) {
// declaration spaces for exported and non-exported declarations intersect
forEach(symbol.declarations, d => {
if (getDeclarationSpaces(d) & commonDeclarationSpace) {
error(d.name, Diagnostics.Individual_declarations_in_merged_declaration_0_must_be_all_exported_or_all_local, declarationNameToString(d.name));
}
});
}
function getDeclarationSpaces(d: Declaration): SymbolFlags {
switch (d.kind) {
case SyntaxKind.InterfaceDeclaration:
return SymbolFlags.ExportType;
case SyntaxKind.ModuleDeclaration:
return (<ModuleDeclaration>d).name.kind === SyntaxKind.StringLiteral || getModuleInstanceState(d) !== ModuleInstanceState.NonInstantiated
? SymbolFlags.ExportNamespace | SymbolFlags.ExportValue
: SymbolFlags.ExportNamespace;
case SyntaxKind.ClassDeclaration:
case SyntaxKind.EnumDeclaration:
return SymbolFlags.ExportType | SymbolFlags.ExportValue;
case SyntaxKind.ImportEqualsDeclaration:
let result: SymbolFlags = 0;
let target = resolveAlias(getSymbolOfNode(d));
forEach(target.declarations, d => { result |= getDeclarationSpaces(d); });
return result;
default:
return SymbolFlags.ExportValue;
}
}
}
/** Check a decorator */
function checkDecorator(node: Decorator): void {
let expression: Expression = node.expression;
let exprType = checkExpression(expression);
switch (node.parent.kind) {
case SyntaxKind.ClassDeclaration:
let classSymbol = getSymbolOfNode(node.parent);
let classConstructorType = getTypeOfSymbol(classSymbol);
let classDecoratorType = instantiateSingleCallFunctionType(getGlobalClassDecoratorType(), [classConstructorType]);
checkTypeAssignableTo(exprType, classDecoratorType, node);
break;
case SyntaxKind.PropertyDeclaration:
checkTypeAssignableTo(exprType, getGlobalPropertyDecoratorType(), node);
break;
case SyntaxKind.MethodDeclaration:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
let methodType = getTypeOfNode(node.parent);
let methodDecoratorType = instantiateSingleCallFunctionType(getGlobalMethodDecoratorType(), [methodType]);
checkTypeAssignableTo(exprType, methodDecoratorType, node);
break;
case SyntaxKind.Parameter:
checkTypeAssignableTo(exprType, getGlobalParameterDecoratorType(), node);
break;
}
}
/** Checks a type reference node as an expression. */
function checkTypeNodeAsExpression(node: TypeNode) {
// When we are emitting type metadata for decorators, we need to try to check the type
// as if it were an expression so that we can emit the type in a value position when we
// serialize the type metadata.
if (node && node.kind === SyntaxKind.TypeReference) {
let type = getTypeFromTypeNode(node);
let shouldCheckIfUnknownType = type === unknownType && compilerOptions.separateCompilation;
if (!type || (!shouldCheckIfUnknownType && type.flags & (TypeFlags.Intrinsic | TypeFlags.NumberLike | TypeFlags.StringLike))) {
return;
}
if (shouldCheckIfUnknownType || type.symbol.valueDeclaration) {
checkExpressionOrQualifiedName((<TypeReferenceNode>node).typeName);
}
}
}
/**
* Checks the type annotation of an accessor declaration or property declaration as
* an expression if it is a type reference to a type with a value declaration.
*/
function checkTypeAnnotationAsExpression(node: AccessorDeclaration | PropertyDeclaration | ParameterDeclaration | MethodDeclaration) {
switch (node.kind) {
case SyntaxKind.PropertyDeclaration:
checkTypeNodeAsExpression((<PropertyDeclaration>node).type);
break;
case SyntaxKind.Parameter:
checkTypeNodeAsExpression((<ParameterDeclaration>node).type);
break;
case SyntaxKind.MethodDeclaration:
checkTypeNodeAsExpression((<MethodDeclaration>node).type);
break;
case SyntaxKind.GetAccessor:
checkTypeNodeAsExpression((<AccessorDeclaration>node).type);
break;
case SyntaxKind.SetAccessor:
checkTypeNodeAsExpression(getSetAccessorTypeAnnotationNode(<AccessorDeclaration>node));
break;
}
}
/** Checks the type annotation of the parameters of a function/method or the constructor of a class as expressions */
function checkParameterTypeAnnotationsAsExpressions(node: FunctionLikeDeclaration) {
// ensure all type annotations with a value declaration are checked as an expression
for (let parameter of node.parameters) {
checkTypeAnnotationAsExpression(parameter);
}
}
/** Check the decorators of a node */
function checkDecorators(node: Node): void {
if (!node.decorators) {
return;
}
// skip this check for nodes that cannot have decorators. These should have already had an error reported by
// checkGrammarDecorators.
if (!nodeCanBeDecorated(node)) {
return;
}
if (compilerOptions.emitDecoratorMetadata) {
// we only need to perform these checks if we are emitting serialized type metadata for the target of a decorator.
switch (node.kind) {
case SyntaxKind.ClassDeclaration:
var constructor = getFirstConstructorWithBody(<ClassDeclaration>node);
if (constructor) {
checkParameterTypeAnnotationsAsExpressions(constructor);
}
break;
case SyntaxKind.MethodDeclaration:
checkParameterTypeAnnotationsAsExpressions(<FunctionLikeDeclaration>node);
// fall-through
case SyntaxKind.SetAccessor:
case SyntaxKind.GetAccessor:
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.Parameter:
checkTypeAnnotationAsExpression(<PropertyDeclaration | ParameterDeclaration>node);
break;
}
}
emitDecorate = true;
if (node.kind === SyntaxKind.Parameter) {
emitParam = true;
}
forEach(node.decorators, checkDecorator);
}
function checkFunctionDeclaration(node: FunctionDeclaration): void {
if (produceDiagnostics) {
checkFunctionLikeDeclaration(node) ||
checkGrammarFunctionName(node.name) ||
checkGrammarForGenerator(node);
checkCollisionWithCapturedSuperVariable(node, node.name);
checkCollisionWithCapturedThisVariable(node, node.name);
checkCollisionWithRequireExportsInGeneratedCode(node, node.name);
}
}
function checkFunctionLikeDeclaration(node: FunctionLikeDeclaration): void {
checkGrammarDeclarationNameInStrictMode(node);
checkDecorators(node);
checkSignatureDeclaration(node);
// Do not use hasDynamicName here, because that returns false for well known symbols.
// We want to perform checkComputedPropertyName for all computed properties, including
// well known symbols.
if (node.name && node.name.kind === SyntaxKind.ComputedPropertyName) {
// This check will account for methods in class/interface declarations,
// as well as accessors in classes/object literals
checkComputedPropertyName(<ComputedPropertyName>node.name);
}
if (!hasDynamicName(node)) {
// first we want to check the local symbol that contain this declaration
// - if node.localSymbol !== undefined - this is current declaration is exported and localSymbol points to the local symbol
// - if node.localSymbol === undefined - this node is non-exported so we can just pick the result of getSymbolOfNode
let symbol = getSymbolOfNode(node);
let localSymbol = node.localSymbol || symbol;
let firstDeclaration = getDeclarationOfKind(localSymbol, node.kind);
// Only type check the symbol once
if (node === firstDeclaration) {
checkFunctionOrConstructorSymbol(localSymbol);
}
if (symbol.parent) {
// run check once for the first declaration
if (getDeclarationOfKind(symbol, node.kind) === node) {
// run check on export symbol to check that modifiers agree across all exported declarations
checkFunctionOrConstructorSymbol(symbol);
}
}
}
checkSourceElement(node.body);
if (node.type && !isAccessor(node.kind) && !node.asteriskToken) {
checkIfNonVoidFunctionHasReturnExpressionsOrSingleThrowStatment(node, getTypeFromTypeNode(node.type));
}
if (produceDiagnostics && !node.type) {
// Report an implicit any error if there is no body, no explicit return type, and node is not a private method
// in an ambient context
if (compilerOptions.noImplicitAny && nodeIsMissing(node.body) && !isPrivateWithinAmbient(node)) {
reportImplicitAnyError(node, anyType);
}
if (node.asteriskToken && nodeIsPresent(node.body)) {
// A generator with a body and no type annotation can still cause errors. It can error if the
// yielded values have no common supertype, or it can give an implicit any error if it has no
// yielded values. The only way to trigger these errors is to try checking its return type.
getReturnTypeOfSignature(getSignatureFromDeclaration(node));
}
}
}
function checkBlock(node: Block) {
// Grammar checking for SyntaxKind.Block
if (node.kind === SyntaxKind.Block) {
checkGrammarStatementInAmbientContext(node);
}
forEach(node.statements, checkSourceElement);
if (isFunctionBlock(node) || node.kind === SyntaxKind.ModuleBlock) {
checkFunctionExpressionBodies(node);
}
}
function checkCollisionWithArgumentsInGeneratedCode(node: SignatureDeclaration) {
// no rest parameters \ declaration context \ overload - no codegen impact
if (!hasRestParameters(node) || isInAmbientContext(node) || nodeIsMissing((<FunctionLikeDeclaration>node).body)) {
return;
}
forEach(node.parameters, p => {
if (p.name && !isBindingPattern(p.name) && (<Identifier>p.name).text === argumentsSymbol.name) {
error(p, Diagnostics.Duplicate_identifier_arguments_Compiler_uses_arguments_to_initialize_rest_parameters);
}
});
}
function needCollisionCheckForIdentifier(node: Node, identifier: Identifier, name: string): boolean {
if (!(identifier && identifier.text === name)) {
return false;
}
if (node.kind === SyntaxKind.PropertyDeclaration ||
node.kind === SyntaxKind.PropertySignature ||
node.kind === SyntaxKind.MethodDeclaration ||
node.kind === SyntaxKind.MethodSignature ||
node.kind === SyntaxKind.GetAccessor ||
node.kind === SyntaxKind.SetAccessor) {
// it is ok to have member named '_super' or '_this' - member access is always qualified
return false;
}
if (isInAmbientContext(node)) {
// ambient context - no codegen impact
return false;
}
let root = getRootDeclaration(node);
if (root.kind === SyntaxKind.Parameter && nodeIsMissing((<FunctionLikeDeclaration>root.parent).body)) {
// just an overload - no codegen impact
return false;
}
return true;
}
function checkCollisionWithCapturedThisVariable(node: Node, name: Identifier): void {
if (needCollisionCheckForIdentifier(node, name, "_this")) {
potentialThisCollisions.push(node);
}
}
// this function will run after checking the source file so 'CaptureThis' is correct for all nodes
function checkIfThisIsCapturedInEnclosingScope(node: Node): void {
let current = node;
while (current) {
if (getNodeCheckFlags(current) & NodeCheckFlags.CaptureThis) {
let isDeclaration = node.kind !== SyntaxKind.Identifier;
if (isDeclaration) {
error((<Declaration>node).name, Diagnostics.Duplicate_identifier_this_Compiler_uses_variable_declaration_this_to_capture_this_reference);
}
else {
error(node, Diagnostics.Expression_resolves_to_variable_declaration_this_that_compiler_uses_to_capture_this_reference);
}
return;
}
current = current.parent;
}
}
function checkCollisionWithCapturedSuperVariable(node: Node, name: Identifier) {
if (!needCollisionCheckForIdentifier(node, name, "_super")) {
return;
}
// bubble up and find containing type
let enclosingClass = <ClassDeclaration>getAncestor(node, SyntaxKind.ClassDeclaration);
// if containing type was not found or it is ambient - exit (no codegen)
if (!enclosingClass || isInAmbientContext(enclosingClass)) {
return;
}
if (getClassExtendsHeritageClauseElement(enclosingClass)) {
let isDeclaration = node.kind !== SyntaxKind.Identifier;
if (isDeclaration) {
error(node, Diagnostics.Duplicate_identifier_super_Compiler_uses_super_to_capture_base_class_reference);
}
else {
error(node, Diagnostics.Expression_resolves_to_super_that_compiler_uses_to_capture_base_class_reference);
}
}
}
function checkCollisionWithRequireExportsInGeneratedCode(node: Node, name: Identifier) {
if (!needCollisionCheckForIdentifier(node, name, "require") && !needCollisionCheckForIdentifier(node, name, "exports")) {
return;
}
// Uninstantiated modules shouldnt do this check
if (node.kind === SyntaxKind.ModuleDeclaration && getModuleInstanceState(node) !== ModuleInstanceState.Instantiated) {
return;
}
// In case of variable declaration, node.parent is variable statement so look at the variable statement's parent
let parent = getDeclarationContainer(node);
if (parent.kind === SyntaxKind.SourceFile && isExternalModule(<SourceFile>parent)) {
// If the declaration happens to be in external module, report error that require and exports are reserved keywords
error(name, Diagnostics.Duplicate_identifier_0_Compiler_reserves_name_1_in_top_level_scope_of_a_module,
declarationNameToString(name), declarationNameToString(name));
}
}
function checkVarDeclaredNamesNotShadowed(node: VariableDeclaration | BindingElement) {
// - ScriptBody : StatementList
// It is a Syntax Error if any element of the LexicallyDeclaredNames of StatementList
// also occurs in the VarDeclaredNames of StatementList.
// - Block : { StatementList }
// It is a Syntax Error if any element of the LexicallyDeclaredNames of StatementList
// also occurs in the VarDeclaredNames of StatementList.
// Variable declarations are hoisted to the top of their function scope. They can shadow
// block scoped declarations, which bind tighter. this will not be flagged as duplicate definition
// by the binder as the declaration scope is different.
// A non-initialized declaration is a no-op as the block declaration will resolve before the var
// declaration. the problem is if the declaration has an initializer. this will act as a write to the
// block declared value. this is fine for let, but not const.
// Only consider declarations with initializers, uninitialized let declarations will not
// step on a let/const variable.
// Do not consider let and const declarations, as duplicate block-scoped declarations
// are handled by the binder.
// We are only looking for let declarations that step on let\const declarations from a
// different scope. e.g.:
// {
// const x = 0; // localDeclarationSymbol obtained after name resolution will correspond to this declaration
// let x = 0; // symbol for this declaration will be 'symbol'
// }
// skip block-scoped variables and parameters
if ((getCombinedNodeFlags(node) & NodeFlags.BlockScoped) !== 0 || isParameterDeclaration(node)) {
return;
}
// skip variable declarations that don't have initializers
// NOTE: in ES6 spec initializer is required in variable declarations where name is binding pattern
// so we'll always treat binding elements as initialized
if (node.kind === SyntaxKind.VariableDeclaration && !node.initializer) {
return;
}
var symbol = getSymbolOfNode(node);
if (symbol.flags & SymbolFlags.FunctionScopedVariable) {
let localDeclarationSymbol = resolveName(node, (<Identifier>node.name).text, SymbolFlags.Variable, /*nodeNotFoundErrorMessage*/ undefined, /*nameArg*/ undefined);
if (localDeclarationSymbol &&
localDeclarationSymbol !== symbol &&
localDeclarationSymbol.flags & SymbolFlags.BlockScopedVariable) {
if (getDeclarationFlagsFromSymbol(localDeclarationSymbol) & NodeFlags.BlockScoped) {
let varDeclList = getAncestor(localDeclarationSymbol.valueDeclaration, SyntaxKind.VariableDeclarationList);
let container =
varDeclList.parent.kind === SyntaxKind.VariableStatement && varDeclList.parent.parent
? varDeclList.parent.parent
: undefined;
// names of block-scoped and function scoped variables can collide only
// if block scoped variable is defined in the function\module\source file scope (because of variable hoisting)
let namesShareScope =
container &&
(container.kind === SyntaxKind.Block && isFunctionLike(container.parent) ||
container.kind === SyntaxKind.ModuleBlock ||
container.kind === SyntaxKind.ModuleDeclaration ||
container.kind === SyntaxKind.SourceFile);
// here we know that function scoped variable is shadowed by block scoped one
// if they are defined in the same scope - binder has already reported redeclaration error
// otherwise if variable has an initializer - show error that initialization will fail
// since LHS will be block scoped name instead of function scoped
if (!namesShareScope) {
let name = symbolToString(localDeclarationSymbol);
error(node, Diagnostics.Cannot_initialize_outer_scoped_variable_0_in_the_same_scope_as_block_scoped_declaration_1, name, name);
}
}
}
}
}
// Check that a parameter initializer contains no references to parameters declared to the right of itself
function checkParameterInitializer(node: VariableLikeDeclaration): void {
if (getRootDeclaration(node).kind !== SyntaxKind.Parameter) {
return;
}
let func = getContainingFunction(node);
visit(node.initializer);
function visit(n: Node) {
if (n.kind === SyntaxKind.Identifier) {
let referencedSymbol = getNodeLinks(n).resolvedSymbol;
// check FunctionLikeDeclaration.locals (stores parameters\function local variable)
// if it contains entry with a specified name and if this entry matches the resolved symbol
if (referencedSymbol && referencedSymbol !== unknownSymbol && getSymbol(func.locals, referencedSymbol.name, SymbolFlags.Value) === referencedSymbol) {
if (referencedSymbol.valueDeclaration.kind === SyntaxKind.Parameter) {
if (referencedSymbol.valueDeclaration === node) {
error(n, Diagnostics.Parameter_0_cannot_be_referenced_in_its_initializer, declarationNameToString(node.name));
return;
}
if (referencedSymbol.valueDeclaration.pos < node.pos) {
// legal case - parameter initializer references some parameter strictly on left of current parameter declaration
return;
}
// fall through to error reporting
}
error(n, Diagnostics.Initializer_of_parameter_0_cannot_reference_identifier_1_declared_after_it, declarationNameToString(node.name), declarationNameToString(<Identifier>n));
}
}
else {
forEachChild(n, visit);
}
}
}
// Check variable, parameter, or property declaration
function checkVariableLikeDeclaration(node: VariableLikeDeclaration) {
checkGrammarDeclarationNameInStrictMode(node);
checkDecorators(node);
checkSourceElement(node.type);
// For a computed property, just check the initializer and exit
// Do not use hasDynamicName here, because that returns false for well known symbols.
// We want to perform checkComputedPropertyName for all computed properties, including
// well known symbols.
if (node.name.kind === SyntaxKind.ComputedPropertyName) {
checkComputedPropertyName(<ComputedPropertyName>node.name);
if (node.initializer) {
checkExpressionCached(node.initializer);
}
}
// For a binding pattern, check contained binding elements
if (isBindingPattern(node.name)) {
forEach((<BindingPattern>node.name).elements, checkSourceElement);
}
// For a parameter declaration with an initializer, error and exit if the containing function doesn't have a body
if (node.initializer && getRootDeclaration(node).kind === SyntaxKind.Parameter && nodeIsMissing(getContainingFunction(node).body)) {
error(node, Diagnostics.A_parameter_initializer_is_only_allowed_in_a_function_or_constructor_implementation);
return;
}
// For a binding pattern, validate the initializer and exit
if (isBindingPattern(node.name)) {
if (node.initializer) {
checkTypeAssignableTo(checkExpressionCached(node.initializer), getWidenedTypeForVariableLikeDeclaration(node), node, /*headMessage*/ undefined);
checkParameterInitializer(node);
}
return;
}
let symbol = getSymbolOfNode(node);
let type = getTypeOfVariableOrParameterOrProperty(symbol);
if (node === symbol.valueDeclaration) {
// Node is the primary declaration of the symbol, just validate the initializer
if (node.initializer) {
checkTypeAssignableTo(checkExpressionCached(node.initializer), type, node, /*headMessage*/ undefined);
checkParameterInitializer(node);
}
}
else {
// Node is a secondary declaration, check that type is identical to primary declaration and check that
// initializer is consistent with type associated with the node
let declarationType = getWidenedTypeForVariableLikeDeclaration(node);
if (type !== unknownType && declarationType !== unknownType && !isTypeIdenticalTo(type, declarationType)) {
error(node.name, Diagnostics.Subsequent_variable_declarations_must_have_the_same_type_Variable_0_must_be_of_type_1_but_here_has_type_2, declarationNameToString(node.name), typeToString(type), typeToString(declarationType));
}
if (node.initializer) {
checkTypeAssignableTo(checkExpressionCached(node.initializer), declarationType, node, /*headMessage*/ undefined);
}
}
if (node.kind !== SyntaxKind.PropertyDeclaration && node.kind !== SyntaxKind.PropertySignature) {
// We know we don't have a binding pattern or computed name here
checkExportsOnMergedDeclarations(node);
if (node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.BindingElement) {
checkVarDeclaredNamesNotShadowed(<VariableDeclaration | BindingElement>node);
}
checkCollisionWithCapturedSuperVariable(node, <Identifier>node.name);
checkCollisionWithCapturedThisVariable(node, <Identifier>node.name);
checkCollisionWithRequireExportsInGeneratedCode(node, <Identifier>node.name);
}
}
function checkVariableDeclaration(node: VariableDeclaration) {
checkGrammarVariableDeclaration(node);
return checkVariableLikeDeclaration(node);
}
function checkBindingElement(node: BindingElement) {
checkGrammarBindingElement(<BindingElement>node);
return checkVariableLikeDeclaration(node);
}
function checkVariableStatement(node: VariableStatement) {
// Grammar checking
checkGrammarDecorators(node) || checkGrammarModifiers(node) || checkGrammarVariableDeclarationList(node.declarationList) || checkGrammarForDisallowedLetOrConstStatement(node);
forEach(node.declarationList.declarations, checkSourceElement);
}
function checkGrammarDisallowedModifiersInBlockOrObjectLiteralExpression(node: Node) {
if (node.modifiers) {
if (inBlockOrObjectLiteralExpression(node)) {
return grammarErrorOnFirstToken(node, Diagnostics.Modifiers_cannot_appear_here);
}
}
}
function inBlockOrObjectLiteralExpression(node: Node) {
while (node) {
if (node.kind === SyntaxKind.Block || node.kind === SyntaxKind.ObjectLiteralExpression) {
return true;
}
node = node.parent;
}
}
function checkExpressionStatement(node: ExpressionStatement) {
// Grammar checking
checkGrammarStatementInAmbientContext(node)
checkExpression(node.expression);
}
function checkIfStatement(node: IfStatement) {
// Grammar checking
checkGrammarStatementInAmbientContext(node);
checkExpression(node.expression);
checkSourceElement(node.thenStatement);
checkSourceElement(node.elseStatement);
}
function checkDoStatement(node: DoStatement) {
// Grammar checking
checkGrammarStatementInAmbientContext(node);
checkSourceElement(node.statement);
checkExpression(node.expression);
}
function checkWhileStatement(node: WhileStatement) {
// Grammar checking
checkGrammarStatementInAmbientContext(node);
checkExpression(node.expression);
checkSourceElement(node.statement);
}
function checkForStatement(node: ForStatement) {
// Grammar checking
if (!checkGrammarStatementInAmbientContext(node)) {
if (node.initializer && node.initializer.kind == SyntaxKind.VariableDeclarationList) {
checkGrammarVariableDeclarationList(<VariableDeclarationList>node.initializer);
}
}
if (node.initializer) {
if (node.initializer.kind === SyntaxKind.VariableDeclarationList) {
forEach((<VariableDeclarationList>node.initializer).declarations, checkVariableDeclaration)
}
else {
checkExpression(<Expression>node.initializer)
}
}
if (node.condition) checkExpression(node.condition);
if (node.incrementor) checkExpression(node.incrementor);
checkSourceElement(node.statement);
}
function checkForOfStatement(node: ForOfStatement): void {
checkGrammarForInOrForOfStatement(node)
// Check the LHS and RHS
// If the LHS is a declaration, just check it as a variable declaration, which will in turn check the RHS
// via checkRightHandSideOfForOf.
// If the LHS is an expression, check the LHS, as a destructuring assignment or as a reference.
// Then check that the RHS is assignable to it.
if (node.initializer.kind === SyntaxKind.VariableDeclarationList) {
checkForInOrForOfVariableDeclaration(node);
}
else {
let varExpr = <Expression>node.initializer;
let iteratedType = checkRightHandSideOfForOf(node.expression);
// There may be a destructuring assignment on the left side
if (varExpr.kind === SyntaxKind.ArrayLiteralExpression || varExpr.kind === SyntaxKind.ObjectLiteralExpression) {
// iteratedType may be undefined. In this case, we still want to check the structure of
// varExpr, in particular making sure it's a valid LeftHandSideExpression. But we'd like
// to short circuit the type relation checking as much as possible, so we pass the unknownType.
checkDestructuringAssignment(varExpr, iteratedType || unknownType);
}
else {
let leftType = checkExpression(varExpr);
checkReferenceExpression(varExpr, /*invalidReferenceMessage*/ Diagnostics.Invalid_left_hand_side_in_for_of_statement,
/*constantVariableMessage*/ Diagnostics.The_left_hand_side_of_a_for_of_statement_cannot_be_a_previously_defined_constant);
// iteratedType will be undefined if the rightType was missing properties/signatures
// required to get its iteratedType (like [Symbol.iterator] or next). This may be
// because we accessed properties from anyType, or it may have led to an error inside
// getElementTypeOfIterable.
if (iteratedType) {
checkTypeAssignableTo(iteratedType, leftType, varExpr, /*headMessage*/ undefined);
}
}
}
checkSourceElement(node.statement);
}
function checkForInStatement(node: ForInStatement) {
// Grammar checking
checkGrammarForInOrForOfStatement(node);
// TypeScript 1.0 spec (April 2014): 5.4
// In a 'for-in' statement of the form
// for (let VarDecl in Expr) Statement
// VarDecl must be a variable declaration without a type annotation that declares a variable of type Any,
// and Expr must be an expression of type Any, an object type, or a type parameter type.
if (node.initializer.kind === SyntaxKind.VariableDeclarationList) {
let variable = (<VariableDeclarationList>node.initializer).declarations[0];
if (variable && isBindingPattern(variable.name)) {
error(variable.name, Diagnostics.The_left_hand_side_of_a_for_in_statement_cannot_be_a_destructuring_pattern);
}
checkForInOrForOfVariableDeclaration(node);
}
else {
// In a 'for-in' statement of the form
// for (Var in Expr) Statement
// Var must be an expression classified as a reference of type Any or the String primitive type,
// and Expr must be an expression of type Any, an object type, or a type parameter type.
let varExpr = <Expression>node.initializer;
let leftType = checkExpression(varExpr);
if (varExpr.kind === SyntaxKind.ArrayLiteralExpression || varExpr.kind === SyntaxKind.ObjectLiteralExpression) {
error(varExpr, Diagnostics.The_left_hand_side_of_a_for_in_statement_cannot_be_a_destructuring_pattern);
}
else if (!allConstituentTypesHaveKind(leftType, TypeFlags.Any | TypeFlags.StringLike)) {
error(varExpr, Diagnostics.The_left_hand_side_of_a_for_in_statement_must_be_of_type_string_or_any);
}
else {
// run check only former check succeeded to avoid cascading errors
checkReferenceExpression(varExpr, Diagnostics.Invalid_left_hand_side_in_for_in_statement, Diagnostics.The_left_hand_side_of_a_for_in_statement_cannot_be_a_previously_defined_constant);
}
}
let rightType = checkExpression(node.expression);
// unknownType is returned i.e. if node.expression is identifier whose name cannot be resolved
// in this case error about missing name is already reported - do not report extra one
if (!allConstituentTypesHaveKind(rightType, TypeFlags.Any | TypeFlags.ObjectType | TypeFlags.TypeParameter)) {
error(node.expression, Diagnostics.The_right_hand_side_of_a_for_in_statement_must_be_of_type_any_an_object_type_or_a_type_parameter);
}
checkSourceElement(node.statement);
}
function checkForInOrForOfVariableDeclaration(iterationStatement: ForInStatement | ForOfStatement): void {
let variableDeclarationList = <VariableDeclarationList>iterationStatement.initializer;
// checkGrammarForInOrForOfStatement will check that there is exactly one declaration.
if (variableDeclarationList.declarations.length >= 1) {
let decl = variableDeclarationList.declarations[0];
checkVariableDeclaration(decl);
}
}
function checkRightHandSideOfForOf(rhsExpression: Expression): Type {
let expressionType = getTypeOfExpression(rhsExpression);
return checkIteratedTypeOrElementType(expressionType, rhsExpression, /*allowStringInput*/ true);
}
function checkIteratedTypeOrElementType(inputType: Type, errorNode: Node, allowStringInput: boolean): Type {
if (inputType.flags & TypeFlags.Any) {
return inputType;
}
if (languageVersion >= ScriptTarget.ES6) {
return checkElementTypeOfIterable(inputType, errorNode);
}
if (allowStringInput) {
return checkElementTypeOfArrayOrString(inputType, errorNode);
}
if (isArrayLikeType(inputType)) {
let indexType = getIndexTypeOfType(inputType, IndexKind.Number);
if (indexType) {
return indexType;
}
}
error(errorNode, Diagnostics.Type_0_is_not_an_array_type, typeToString(inputType));
return unknownType;
}
/**
* When errorNode is undefined, it means we should not report any errors.
*/
function checkElementTypeOfIterable(iterable: Type, errorNode: Node): Type {
let elementType = getElementTypeOfIterable(iterable, errorNode);
// Now even though we have extracted the iteratedType, we will have to validate that the type
// passed in is actually an Iterable.
if (errorNode && elementType) {
checkTypeAssignableTo(iterable, createIterableType(elementType), errorNode);
}
return elementType || anyType;
}
/**
* We want to treat type as an iterable, and get the type it is an iterable of. The iterable
* must have the following structure (annotated with the names of the variables below):
*
* { // iterable
* [Symbol.iterator]: { // iteratorFunction
* (): Iterator<T>
* }
* }
*
* T is the type we are after. At every level that involves analyzing return types
* of signatures, we union the return types of all the signatures.
*
* Another thing to note is that at any step of this process, we could run into a dead end,
* meaning either the property is missing, or we run into the anyType. If either of these things
* happens, we return undefined to signal that we could not find the iterated type. If a property
* is missing, and the previous step did not result in 'any', then we also give an error if the
* caller requested it. Then the caller can decide what to do in the case where there is no iterated
* type. This is different from returning anyType, because that would signify that we have matched the
* whole pattern and that T (above) is 'any'.
*/
function getElementTypeOfIterable(type: Type, errorNode: Node): Type {
if (type.flags & TypeFlags.Any) {
return undefined;
}
let typeAsIterable = <IterableOrIteratorType>type;
if (!typeAsIterable.iterableElementType) {
// As an optimization, if the type is instantiated directly using the globalIterableType (Iterable<number>),
// then just grab its type argument.
if ((type.flags & TypeFlags.Reference) && (<GenericType>type).target === globalIterableType) {
typeAsIterable.iterableElementType = (<GenericType>type).typeArguments[0];
}
else {
let iteratorFunction = getTypeOfPropertyOfType(type, getPropertyNameForKnownSymbolName("iterator"));
if (iteratorFunction && iteratorFunction.flags & TypeFlags.Any) {
return undefined;
}
let iteratorFunctionSignatures = iteratorFunction ? getSignaturesOfType(iteratorFunction, SignatureKind.Call) : emptyArray;
if (iteratorFunctionSignatures.length === 0) {
if (errorNode) {
error(errorNode, Diagnostics.Type_must_have_a_Symbol_iterator_method_that_returns_an_iterator);
}
return undefined;
}
typeAsIterable.iterableElementType = getElementTypeOfIterator(getUnionType(map(iteratorFunctionSignatures, getReturnTypeOfSignature)), errorNode);
}
}
return typeAsIterable.iterableElementType;
}
/**
* This function has very similar logic as getElementTypeOfIterable, except that it operates on
* Iterators instead of Iterables. Here is the structure:
*
* { // iterator
* next: { // iteratorNextFunction
* (): { // iteratorNextResult
* value: T // iteratorNextValue
* }
* }
* }
*
*/
function getElementTypeOfIterator(type: Type, errorNode: Node): Type {
if (type.flags & TypeFlags.Any) {
return undefined;
}
let typeAsIterator = <IterableOrIteratorType>type;
if (!typeAsIterator.iteratorElementType) {
// As an optimization, if the type is instantiated directly using the globalIteratorType (Iterator<number>),
// then just grab its type argument.
if ((type.flags & TypeFlags.Reference) && (<GenericType>type).target === globalIteratorType) {
typeAsIterator.iteratorElementType = (<GenericType>type).typeArguments[0];
}
else {
let iteratorNextFunction = getTypeOfPropertyOfType(type, "next");
if (iteratorNextFunction && iteratorNextFunction.flags & TypeFlags.Any) {
return undefined;
}
let iteratorNextFunctionSignatures = iteratorNextFunction ? getSignaturesOfType(iteratorNextFunction, SignatureKind.Call) : emptyArray;
if (iteratorNextFunctionSignatures.length === 0) {
if (errorNode) {
error(errorNode, Diagnostics.An_iterator_must_have_a_next_method);
}
return undefined;
}
let iteratorNextResult = getUnionType(map(iteratorNextFunctionSignatures, getReturnTypeOfSignature));
if (iteratorNextResult.flags & TypeFlags.Any) {
return undefined;
}
let iteratorNextValue = getTypeOfPropertyOfType(iteratorNextResult, "value");
if (!iteratorNextValue) {
if (errorNode) {
error(errorNode, Diagnostics.The_type_returned_by_the_next_method_of_an_iterator_must_have_a_value_property);
}
return undefined;
}
typeAsIterator.iteratorElementType = iteratorNextValue;
}
}
return typeAsIterator.iteratorElementType;
}
function getElementTypeOfIterableIterator(type: Type): Type {
if (type.flags & TypeFlags.Any) {
return undefined;
}
// As an optimization, if the type is instantiated directly using the globalIterableIteratorType (IterableIterator<number>),
// then just grab its type argument.
if ((type.flags & TypeFlags.Reference) && (<GenericType>type).target === globalIterableIteratorType) {
return (<GenericType>type).typeArguments[0];
}
return getElementTypeOfIterable(type, /*errorNode*/ undefined) ||
getElementTypeOfIterator(type, /*errorNode*/ undefined);
}
/**
* This function does the following steps:
* 1. Break up arrayOrStringType (possibly a union) into its string constituents and array constituents.
* 2. Take the element types of the array constituents.
* 3. Return the union of the element types, and string if there was a string constitutent.
*
* For example:
* string -> string
* number[] -> number
* string[] | number[] -> string | number
* string | number[] -> string | number
* string | string[] | number[] -> string | number
*
* It also errors if:
* 1. Some constituent is neither a string nor an array.
* 2. Some constituent is a string and target is less than ES5 (because in ES3 string is not indexable).
*/
function checkElementTypeOfArrayOrString(arrayOrStringType: Type, errorNode: Node): Type {
Debug.assert(languageVersion < ScriptTarget.ES6);
// After we remove all types that are StringLike, we will know if there was a string constituent
// based on whether the remaining type is the same as the initial type.
let arrayType = removeTypesFromUnionType(arrayOrStringType, TypeFlags.StringLike, /*isTypeOfKind*/ true, /*allowEmptyUnionResult*/ true);
let hasStringConstituent = arrayOrStringType !== arrayType;
let reportedError = false;
if (hasStringConstituent) {
if (languageVersion < ScriptTarget.ES5) {
error(errorNode, Diagnostics.Using_a_string_in_a_for_of_statement_is_only_supported_in_ECMAScript_5_and_higher);
reportedError = true;
}
// Now that we've removed all the StringLike types, if no constituents remain, then the entire
// arrayOrStringType was a string.
if (arrayType === emptyObjectType) {
return stringType;
}
}
if (!isArrayLikeType(arrayType)) {
if (!reportedError) {
// Which error we report depends on whether there was a string constituent. For example,
// if the input type is number | string, we want to say that number is not an array type.
// But if the input was just number, we want to say that number is not an array type
// or a string type.
let diagnostic = hasStringConstituent
? Diagnostics.Type_0_is_not_an_array_type
: Diagnostics.Type_0_is_not_an_array_type_or_a_string_type;
error(errorNode, diagnostic, typeToString(arrayType));
}
return hasStringConstituent ? stringType : unknownType;
}
let arrayElementType = getIndexTypeOfType(arrayType, IndexKind.Number) || unknownType;
if (hasStringConstituent) {
// This is just an optimization for the case where arrayOrStringType is string | string[]
if (arrayElementType.flags & TypeFlags.StringLike) {
return stringType;
}
return getUnionType([arrayElementType, stringType]);
}
return arrayElementType;
}
function checkBreakOrContinueStatement(node: BreakOrContinueStatement) {
// Grammar checking
checkGrammarStatementInAmbientContext(node) || checkGrammarBreakOrContinueStatement(node);
// TODO: Check that target label is valid
}
function isGetAccessorWithAnnotatatedSetAccessor(node: FunctionLikeDeclaration) {
return !!(node.kind === SyntaxKind.GetAccessor && getSetAccessorTypeAnnotationNode(<AccessorDeclaration>getDeclarationOfKind(node.symbol, SyntaxKind.SetAccessor)));
}
function checkReturnStatement(node: ReturnStatement) {
// Grammar checking
if (!checkGrammarStatementInAmbientContext(node)) {
let functionBlock = getContainingFunction(node);
if (!functionBlock) {
grammarErrorOnFirstToken(node, Diagnostics.A_return_statement_can_only_be_used_within_a_function_body);
}
}
if (node.expression) {
let func = getContainingFunction(node);
if (func) {
let returnType = getReturnTypeOfSignature(getSignatureFromDeclaration(func));
let exprType = checkExpressionCached(node.expression);
if (func.asteriskToken) {
// A generator does not need its return expressions checked against its return type.
// Instead, the yield expressions are checked against the element type.
// TODO: Check return expressions of generators when return type tracking is added
// for generators.
return;
}
if (func.kind === SyntaxKind.SetAccessor) {
error(node.expression, Diagnostics.Setters_cannot_return_a_value);
}
else if (func.kind === SyntaxKind.Constructor) {
if (!isTypeAssignableTo(exprType, returnType)) {
error(node.expression, Diagnostics.Return_type_of_constructor_signature_must_be_assignable_to_the_instance_type_of_the_class);
}
}
else if (func.type || isGetAccessorWithAnnotatatedSetAccessor(func)) {
checkTypeAssignableTo(exprType, returnType, node.expression, /*headMessage*/ undefined);
}
}
}
}
function checkWithStatement(node: WithStatement) {
// Grammar checking for withStatement
if (!checkGrammarStatementInAmbientContext(node)) {
if (node.parserContextFlags & ParserContextFlags.StrictMode) {
grammarErrorOnFirstToken(node, Diagnostics.with_statements_are_not_allowed_in_strict_mode);
}
}
checkExpression(node.expression);
error(node.expression, Diagnostics.All_symbols_within_a_with_block_will_be_resolved_to_any);
}
function checkSwitchStatement(node: SwitchStatement) {
// Grammar checking
checkGrammarStatementInAmbientContext(node);
let firstDefaultClause: CaseOrDefaultClause;
let hasDuplicateDefaultClause = false;
let expressionType = checkExpression(node.expression);
forEach(node.caseBlock.clauses, clause => {
// Grammar check for duplicate default clauses, skip if we already report duplicate default clause
if (clause.kind === SyntaxKind.DefaultClause && !hasDuplicateDefaultClause) {
if (firstDefaultClause === undefined) {
firstDefaultClause = clause;
}
else {
let sourceFile = getSourceFileOfNode(node);
let start = skipTrivia(sourceFile.text, clause.pos);
let end = clause.statements.length > 0 ? clause.statements[0].pos : clause.end;
grammarErrorAtPos(sourceFile, start, end - start, Diagnostics.A_default_clause_cannot_appear_more_than_once_in_a_switch_statement);
hasDuplicateDefaultClause = true;
}
}
if (produceDiagnostics && clause.kind === SyntaxKind.CaseClause) {
let caseClause = <CaseClause>clause;
// TypeScript 1.0 spec (April 2014):5.9
// In a 'switch' statement, each 'case' expression must be of a type that is assignable to or from the type of the 'switch' expression.
let caseType = checkExpression(caseClause.expression);
if (!isTypeAssignableTo(expressionType, caseType)) {
// check 'expressionType isAssignableTo caseType' failed, try the reversed check and report errors if it fails
checkTypeAssignableTo(caseType, expressionType, caseClause.expression, /*headMessage*/ undefined);
}
}
forEach(clause.statements, checkSourceElement);
});
}
function checkLabeledStatement(node: LabeledStatement) {
// Grammar checking
if (!checkGrammarStatementInAmbientContext(node)) {
let current = node.parent;
while (current) {
if (isFunctionLike(current)) {
break;
}
if (current.kind === SyntaxKind.LabeledStatement && (<LabeledStatement>current).label.text === node.label.text) {
let sourceFile = getSourceFileOfNode(node);
grammarErrorOnNode(node.label, Diagnostics.Duplicate_label_0, getTextOfNodeFromSourceText(sourceFile.text, node.label));
break;
}
current = current.parent;
}
}
// ensure that label is unique
checkSourceElement(node.statement);
}
function checkThrowStatement(node: ThrowStatement) {
// Grammar checking
if (!checkGrammarStatementInAmbientContext(node)) {
if (node.expression === undefined) {
grammarErrorAfterFirstToken(node, Diagnostics.Line_break_not_permitted_here);
}
}
if (node.expression) {
checkExpression(node.expression);
}
}
function checkTryStatement(node: TryStatement) {
// Grammar checking
checkGrammarStatementInAmbientContext(node);
checkBlock(node.tryBlock);
let catchClause = node.catchClause;
if (catchClause) {
// Grammar checking
if (catchClause.variableDeclaration) {
if (catchClause.variableDeclaration.name.kind !== SyntaxKind.Identifier) {
grammarErrorOnFirstToken(catchClause.variableDeclaration.name, Diagnostics.Catch_clause_variable_name_must_be_an_identifier);
}
else if (catchClause.variableDeclaration.type) {
grammarErrorOnFirstToken(catchClause.variableDeclaration.type, Diagnostics.Catch_clause_variable_cannot_have_a_type_annotation);
}
else if (catchClause.variableDeclaration.initializer) {
grammarErrorOnFirstToken(catchClause.variableDeclaration.initializer, Diagnostics.Catch_clause_variable_cannot_have_an_initializer);
}
else {
let identifierName = (<Identifier>catchClause.variableDeclaration.name).text;
let locals = catchClause.block.locals;
if (locals && hasProperty(locals, identifierName)) {
let localSymbol = locals[identifierName]
if (localSymbol && (localSymbol.flags & SymbolFlags.BlockScopedVariable) !== 0) {
grammarErrorOnNode(localSymbol.valueDeclaration, Diagnostics.Cannot_redeclare_identifier_0_in_catch_clause, identifierName);
}
}
// It is a SyntaxError if a TryStatement with a Catch occurs within strict code and the Identifier of the
// Catch production is eval or arguments
checkGrammarEvalOrArgumentsInStrictMode(node, <Identifier>catchClause.variableDeclaration.name);
}
}
checkBlock(catchClause.block);
}
if (node.finallyBlock) {
checkBlock(node.finallyBlock);
}
}
function checkIndexConstraints(type: Type) {
let declaredNumberIndexer = getIndexDeclarationOfSymbol(type.symbol, IndexKind.Number);
let declaredStringIndexer = getIndexDeclarationOfSymbol(type.symbol, IndexKind.String);
let stringIndexType = getIndexTypeOfType(type, IndexKind.String);
let numberIndexType = getIndexTypeOfType(type, IndexKind.Number);
if (stringIndexType || numberIndexType) {
forEach(getPropertiesOfObjectType(type), prop => {
let propType = getTypeOfSymbol(prop);
checkIndexConstraintForProperty(prop, propType, type, declaredStringIndexer, stringIndexType, IndexKind.String);
checkIndexConstraintForProperty(prop, propType, type, declaredNumberIndexer, numberIndexType, IndexKind.Number);
});
if (type.flags & TypeFlags.Class && type.symbol.valueDeclaration.kind === SyntaxKind.ClassDeclaration) {
let classDeclaration = <ClassDeclaration>type.symbol.valueDeclaration;
for (let member of classDeclaration.members) {
// Only process instance properties with computed names here.
// Static properties cannot be in conflict with indexers,
// and properties with literal names were already checked.
if (!(member.flags & NodeFlags.Static) && hasDynamicName(member)) {
let propType = getTypeOfSymbol(member.symbol);
checkIndexConstraintForProperty(member.symbol, propType, type, declaredStringIndexer, stringIndexType, IndexKind.String);
checkIndexConstraintForProperty(member.symbol, propType, type, declaredNumberIndexer, numberIndexType, IndexKind.Number);
}
}
}
}
let errorNode: Node;
if (stringIndexType && numberIndexType) {
errorNode = declaredNumberIndexer || declaredStringIndexer;
// condition 'errorNode === undefined' may appear if types does not declare nor string neither number indexer
if (!errorNode && (type.flags & TypeFlags.Interface)) {
let someBaseTypeHasBothIndexers = forEach(getBaseTypes(<InterfaceType>type), base => getIndexTypeOfType(base, IndexKind.String) && getIndexTypeOfType(base, IndexKind.Number));
errorNode = someBaseTypeHasBothIndexers ? undefined : type.symbol.declarations[0];
}
}
if (errorNode && !isTypeAssignableTo(numberIndexType, stringIndexType)) {
error(errorNode, Diagnostics.Numeric_index_type_0_is_not_assignable_to_string_index_type_1,
typeToString(numberIndexType), typeToString(stringIndexType));
}
function checkIndexConstraintForProperty(
prop: Symbol,
propertyType: Type,
containingType: Type,
indexDeclaration: Declaration,
indexType: Type,
indexKind: IndexKind): void {
if (!indexType) {
return;
}
// index is numeric and property name is not valid numeric literal
if (indexKind === IndexKind.Number && !isNumericName(prop.valueDeclaration.name)) {
return;
}
// perform property check if property or indexer is declared in 'type'
// this allows to rule out cases when both property and indexer are inherited from the base class
let errorNode: Node;
if (prop.valueDeclaration.name.kind === SyntaxKind.ComputedPropertyName || prop.parent === containingType.symbol) {
errorNode = prop.valueDeclaration;
}
else if (indexDeclaration) {
errorNode = indexDeclaration;
}
else if (containingType.flags & TypeFlags.Interface) {
// for interfaces property and indexer might be inherited from different bases
// check if any base class already has both property and indexer.
// check should be performed only if 'type' is the first type that brings property\indexer together
let someBaseClassHasBothPropertyAndIndexer = forEach(getBaseTypes(<InterfaceType>containingType), base => getPropertyOfObjectType(base, prop.name) && getIndexTypeOfType(base, indexKind));
errorNode = someBaseClassHasBothPropertyAndIndexer ? undefined : containingType.symbol.declarations[0];
}
if (errorNode && !isTypeAssignableTo(propertyType, indexType)) {
let errorMessage =
indexKind === IndexKind.String
? Diagnostics.Property_0_of_type_1_is_not_assignable_to_string_index_type_2
: Diagnostics.Property_0_of_type_1_is_not_assignable_to_numeric_index_type_2;
error(errorNode, errorMessage, symbolToString(prop), typeToString(propertyType), typeToString(indexType));
}
}
}
function checkTypeNameIsReserved(name: DeclarationName, message: DiagnosticMessage): void {
// TS 1.0 spec (April 2014): 3.6.1
// The predefined type keywords are reserved and cannot be used as names of user defined types.
switch ((<Identifier>name).text) {
case "any":
case "number":
case "boolean":
case "string":
case "symbol":
case "void":
error(name, message, (<Identifier>name).text);
}
}
// Check each type parameter and check that list has no duplicate type parameter declarations
function checkTypeParameters(typeParameterDeclarations: TypeParameterDeclaration[]) {
if (typeParameterDeclarations) {
for (let i = 0, n = typeParameterDeclarations.length; i < n; i++) {
let node = typeParameterDeclarations[i];
checkTypeParameter(node);
if (produceDiagnostics) {
for (let j = 0; j < i; j++) {
if (typeParameterDeclarations[j].symbol === node.symbol) {
error(node.name, Diagnostics.Duplicate_identifier_0, declarationNameToString(node.name));
}
}
}
}
}
}
function checkClassExpression(node: ClassExpression): Type {
grammarErrorOnNode(node, Diagnostics.class_expressions_are_not_currently_supported);
forEach(node.members, checkSourceElement);
return unknownType;
}
function checkClassDeclaration(node: ClassDeclaration) {
checkGrammarDeclarationNameInStrictMode(node);
// Grammar checking
if (!node.name && !(node.flags & NodeFlags.Default)) {
grammarErrorOnFirstToken(node, Diagnostics.A_class_declaration_without_the_default_modifier_must_have_a_name);
}
checkGrammarClassDeclarationHeritageClauses(node);
checkDecorators(node);
if (node.name) {
checkTypeNameIsReserved(node.name, Diagnostics.Class_name_cannot_be_0);
checkCollisionWithCapturedThisVariable(node, node.name);
checkCollisionWithRequireExportsInGeneratedCode(node, node.name);
}
checkTypeParameters(node.typeParameters);
checkExportsOnMergedDeclarations(node);
let symbol = getSymbolOfNode(node);
let type = <InterfaceType>getDeclaredTypeOfSymbol(symbol);
let staticType = <ObjectType>getTypeOfSymbol(symbol);
let baseTypeNode = getClassExtendsHeritageClauseElement(node);
if (baseTypeNode) {
if (!isSupportedExpressionWithTypeArguments(baseTypeNode)) {
error(baseTypeNode.expression, Diagnostics.Only_identifiers_Slashqualified_names_with_optional_type_arguments_are_currently_supported_in_a_class_extends_clauses);
}
emitExtends = emitExtends || !isInAmbientContext(node);
checkExpressionWithTypeArguments(baseTypeNode);
}
let baseTypes = getBaseTypes(type);
if (baseTypes.length) {
if (produceDiagnostics) {
let baseType = baseTypes[0];
checkTypeAssignableTo(type, baseType, node.name || node, Diagnostics.Class_0_incorrectly_extends_base_class_1);
let staticBaseType = getTypeOfSymbol(baseType.symbol);
checkTypeAssignableTo(staticType, getTypeWithoutConstructors(staticBaseType), node.name || node,
Diagnostics.Class_static_side_0_incorrectly_extends_base_class_static_side_1);
if (baseType.symbol !== resolveEntityName(baseTypeNode.expression, SymbolFlags.Value)) {
error(baseTypeNode, Diagnostics.Type_name_0_in_extends_clause_does_not_reference_constructor_function_for_0, typeToString(baseType));
}
checkKindsOfPropertyMemberOverrides(type, baseType);
}
}
if (baseTypes.length || (baseTypeNode && compilerOptions.separateCompilation)) {
// Check that base type can be evaluated as expression
checkExpressionOrQualifiedName(baseTypeNode.expression);
}
let implementedTypeNodes = getClassImplementsHeritageClauseElements(node);
if (implementedTypeNodes) {
forEach(implementedTypeNodes, typeRefNode => {
if (!isSupportedExpressionWithTypeArguments(typeRefNode)) {
error(typeRefNode.expression, Diagnostics.A_class_can_only_implement_an_identifier_Slashqualified_name_with_optional_type_arguments);
}
checkExpressionWithTypeArguments(typeRefNode);
if (produceDiagnostics) {
let t = getTypeFromTypeNode(typeRefNode);
if (t !== unknownType) {
let declaredType = (t.flags & TypeFlags.Reference) ? (<TypeReference>t).target : t;
if (declaredType.flags & (TypeFlags.Class | TypeFlags.Interface)) {
checkTypeAssignableTo(type, t, node.name || node, Diagnostics.Class_0_incorrectly_implements_interface_1);
}
else {
error(typeRefNode, Diagnostics.A_class_may_only_implement_another_class_or_interface);
}
}
}
});
}
forEach(node.members, checkSourceElement);
if (produceDiagnostics) {
checkIndexConstraints(type);
checkTypeForDuplicateIndexSignatures(node);
}
}
function getTargetSymbol(s: Symbol) {
// if symbol is instantiated its flags are not copied from the 'target'
// so we'll need to get back original 'target' symbol to work with correct set of flags
return s.flags & SymbolFlags.Instantiated ? getSymbolLinks(s).target : s;
}
function checkKindsOfPropertyMemberOverrides(type: InterfaceType, baseType: ObjectType): void {
// TypeScript 1.0 spec (April 2014): 8.2.3
// A derived class inherits all members from its base class it doesn't override.
// Inheritance means that a derived class implicitly contains all non - overridden members of the base class.
// Both public and private property members are inherited, but only public property members can be overridden.
// A property member in a derived class is said to override a property member in a base class
// when the derived class property member has the same name and kind(instance or static)
// as the base class property member.
// The type of an overriding property member must be assignable(section 3.8.4)
// to the type of the overridden property member, or otherwise a compile - time error occurs.
// Base class instance member functions can be overridden by derived class instance member functions,
// but not by other kinds of members.
// Base class instance member variables and accessors can be overridden by
// derived class instance member variables and accessors, but not by other kinds of members.
// NOTE: assignability is checked in checkClassDeclaration
let baseProperties = getPropertiesOfObjectType(baseType);
for (let baseProperty of baseProperties) {
let base = getTargetSymbol(baseProperty);
if (base.flags & SymbolFlags.Prototype) {
continue;
}
let derived = getTargetSymbol(getPropertyOfObjectType(type, base.name));
if (derived) {
let baseDeclarationFlags = getDeclarationFlagsFromSymbol(base);
let derivedDeclarationFlags = getDeclarationFlagsFromSymbol(derived);
if ((baseDeclarationFlags & NodeFlags.Private) || (derivedDeclarationFlags & NodeFlags.Private)) {
// either base or derived property is private - not override, skip it
continue;
}
if ((baseDeclarationFlags & NodeFlags.Static) !== (derivedDeclarationFlags & NodeFlags.Static)) {
// value of 'static' is not the same for properties - not override, skip it
continue;
}
if ((base.flags & derived.flags & SymbolFlags.Method) || ((base.flags & SymbolFlags.PropertyOrAccessor) && (derived.flags & SymbolFlags.PropertyOrAccessor))) {
// method is overridden with method or property/accessor is overridden with property/accessor - correct case
continue;
}
let errorMessage: DiagnosticMessage;
if (base.flags & SymbolFlags.Method) {
if (derived.flags & SymbolFlags.Accessor) {
errorMessage = Diagnostics.Class_0_defines_instance_member_function_1_but_extended_class_2_defines_it_as_instance_member_accessor;
}
else {
Debug.assert((derived.flags & SymbolFlags.Property) !== 0);
errorMessage = Diagnostics.Class_0_defines_instance_member_function_1_but_extended_class_2_defines_it_as_instance_member_property;
}
}
else if (base.flags & SymbolFlags.Property) {
Debug.assert((derived.flags & SymbolFlags.Method) !== 0);
errorMessage = Diagnostics.Class_0_defines_instance_member_property_1_but_extended_class_2_defines_it_as_instance_member_function;
}
else {
Debug.assert((base.flags & SymbolFlags.Accessor) !== 0);
Debug.assert((derived.flags & SymbolFlags.Method) !== 0);
errorMessage = Diagnostics.Class_0_defines_instance_member_accessor_1_but_extended_class_2_defines_it_as_instance_member_function;
}
error(derived.valueDeclaration.name, errorMessage, typeToString(baseType), symbolToString(base), typeToString(type));
}
}
}
function isAccessor(kind: SyntaxKind): boolean {
return kind === SyntaxKind.GetAccessor || kind === SyntaxKind.SetAccessor;
}
function areTypeParametersIdentical(list1: TypeParameterDeclaration[], list2: TypeParameterDeclaration[]) {
if (!list1 && !list2) {
return true;
}
if (!list1 || !list2 || list1.length !== list2.length) {
return false;
}
// TypeScript 1.0 spec (April 2014):
// When a generic interface has multiple declarations, all declarations must have identical type parameter
// lists, i.e. identical type parameter names with identical constraints in identical order.
for (let i = 0, len = list1.length; i < len; i++) {
let tp1 = list1[i];
let tp2 = list2[i];
if (tp1.name.text !== tp2.name.text) {
return false;
}
if (!tp1.constraint && !tp2.constraint) {
continue;
}
if (!tp1.constraint || !tp2.constraint) {
return false;
}
if (!isTypeIdenticalTo(getTypeFromTypeNode(tp1.constraint), getTypeFromTypeNode(tp2.constraint))) {
return false;
}
}
return true;
}
function checkInheritedPropertiesAreIdentical(type: InterfaceType, typeNode: Node): boolean {
let baseTypes = getBaseTypes(type);
if (baseTypes.length < 2) {
return true;
}
let seen: Map<{ prop: Symbol; containingType: Type }> = {};
forEach(resolveDeclaredMembers(type).declaredProperties, p => { seen[p.name] = { prop: p, containingType: type }; });
let ok = true;
for (let base of baseTypes) {
let properties = getPropertiesOfObjectType(base);
for (let prop of properties) {
if (!hasProperty(seen, prop.name)) {
seen[prop.name] = { prop: prop, containingType: base };
}
else {
let existing = seen[prop.name];
let isInheritedProperty = existing.containingType !== type;
if (isInheritedProperty && !isPropertyIdenticalTo(existing.prop, prop)) {
ok = false;
let typeName1 = typeToString(existing.containingType);
let typeName2 = typeToString(base);
let errorInfo = chainDiagnosticMessages(undefined, Diagnostics.Named_property_0_of_types_1_and_2_are_not_identical, symbolToString(prop), typeName1, typeName2);
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Interface_0_cannot_simultaneously_extend_types_1_and_2, typeToString(type), typeName1, typeName2);
diagnostics.add(createDiagnosticForNodeFromMessageChain(typeNode, errorInfo));
}
}
}
}
return ok;
}
function checkInterfaceDeclaration(node: InterfaceDeclaration) {
// Grammar checking
checkGrammarDeclarationNameInStrictMode(node) || checkGrammarDecorators(node) || checkGrammarModifiers(node) || checkGrammarInterfaceDeclaration(node);
checkTypeParameters(node.typeParameters);
if (produceDiagnostics) {
checkTypeNameIsReserved(node.name, Diagnostics.Interface_name_cannot_be_0);
checkExportsOnMergedDeclarations(node);
let symbol = getSymbolOfNode(node);
let firstInterfaceDecl = <InterfaceDeclaration>getDeclarationOfKind(symbol, SyntaxKind.InterfaceDeclaration);
if (symbol.declarations.length > 1) {
if (node !== firstInterfaceDecl && !areTypeParametersIdentical(firstInterfaceDecl.typeParameters, node.typeParameters)) {
error(node.name, Diagnostics.All_declarations_of_an_interface_must_have_identical_type_parameters);
}
}
// Only check this symbol once
if (node === firstInterfaceDecl) {
let type = <InterfaceType>getDeclaredTypeOfSymbol(symbol);
// run subsequent checks only if first set succeeded
if (checkInheritedPropertiesAreIdentical(type, node.name)) {
forEach(getBaseTypes(type), baseType => {
checkTypeAssignableTo(type, baseType, node.name, Diagnostics.Interface_0_incorrectly_extends_interface_1);
});
checkIndexConstraints(type);
}
}
}
forEach(getInterfaceBaseTypeNodes(node), heritageElement => {
if (!isSupportedExpressionWithTypeArguments(heritageElement)) {
error(heritageElement.expression, Diagnostics.An_interface_can_only_extend_an_identifier_Slashqualified_name_with_optional_type_arguments);
}
checkExpressionWithTypeArguments(heritageElement);
});
forEach(node.members, checkSourceElement);
if (produceDiagnostics) {
checkTypeForDuplicateIndexSignatures(node);
}
}
function checkTypeAliasDeclaration(node: TypeAliasDeclaration) {
// Grammar checking
checkGrammarDecorators(node) || checkGrammarModifiers(node);
checkTypeNameIsReserved(node.name, Diagnostics.Type_alias_name_cannot_be_0);
checkSourceElement(node.type);
}
function computeEnumMemberValues(node: EnumDeclaration) {
let nodeLinks = getNodeLinks(node);
if (!(nodeLinks.flags & NodeCheckFlags.EnumValuesComputed)) {
let enumSymbol = getSymbolOfNode(node);
let enumType = getDeclaredTypeOfSymbol(enumSymbol);
let autoValue = 0;
let ambient = isInAmbientContext(node);
let enumIsConst = isConst(node);
forEach(node.members, member => {
if (member.name.kind !== SyntaxKind.ComputedPropertyName && isNumericLiteralName((<Identifier>member.name).text)) {
error(member.name, Diagnostics.An_enum_member_cannot_have_a_numeric_name);
}
let initializer = member.initializer;
if (initializer) {
autoValue = getConstantValueForEnumMemberInitializer(initializer);
if (autoValue === undefined) {
if (enumIsConst) {
error(initializer, Diagnostics.In_const_enum_declarations_member_initializer_must_be_constant_expression);
}
else if (!ambient) {
// Only here do we need to check that the initializer is assignable to the enum type.
// If it is a constant value (not undefined), it is syntactically constrained to be a number.
// Also, we do not need to check this for ambients because there is already
// a syntax error if it is not a constant.
checkTypeAssignableTo(checkExpression(initializer), enumType, initializer, /*headMessage*/ undefined);
}
}
else if (enumIsConst) {
if (isNaN(autoValue)) {
error(initializer, Diagnostics.const_enum_member_initializer_was_evaluated_to_disallowed_value_NaN);
}
else if (!isFinite(autoValue)) {
error(initializer, Diagnostics.const_enum_member_initializer_was_evaluated_to_a_non_finite_value);
}
}
}
else if (ambient && !enumIsConst) {
autoValue = undefined;
}
if (autoValue !== undefined) {
getNodeLinks(member).enumMemberValue = autoValue++;
}
});
nodeLinks.flags |= NodeCheckFlags.EnumValuesComputed;
}
function getConstantValueForEnumMemberInitializer(initializer: Expression): number {
return evalConstant(initializer);
function evalConstant(e: Node): number {
switch (e.kind) {
case SyntaxKind.PrefixUnaryExpression:
let value = evalConstant((<PrefixUnaryExpression>e).operand);
if (value === undefined) {
return undefined;
}
switch ((<PrefixUnaryExpression>e).operator) {
case SyntaxKind.PlusToken: return value;
case SyntaxKind.MinusToken: return -value;
case SyntaxKind.TildeToken: return ~value;
}
return undefined;
case SyntaxKind.BinaryExpression:
let left = evalConstant((<BinaryExpression>e).left);
if (left === undefined) {
return undefined;
}
let right = evalConstant((<BinaryExpression>e).right);
if (right === undefined) {
return undefined;
}
switch ((<BinaryExpression>e).operatorToken.kind) {
case SyntaxKind.BarToken: return left | right;
case SyntaxKind.AmpersandToken: return left & right;
case SyntaxKind.GreaterThanGreaterThanToken: return left >> right;
case SyntaxKind.GreaterThanGreaterThanGreaterThanToken: return left >>> right;
case SyntaxKind.LessThanLessThanToken: return left << right;
case SyntaxKind.CaretToken: return left ^ right;
case SyntaxKind.AsteriskToken: return left * right;
case SyntaxKind.SlashToken: return left / right;
case SyntaxKind.PlusToken: return left + right;
case SyntaxKind.MinusToken: return left - right;
case SyntaxKind.PercentToken: return left % right;
}
return undefined;
case SyntaxKind.NumericLiteral:
return +(<LiteralExpression>e).text;
case SyntaxKind.ParenthesizedExpression:
return evalConstant((<ParenthesizedExpression>e).expression);
case SyntaxKind.Identifier:
case SyntaxKind.ElementAccessExpression:
case SyntaxKind.PropertyAccessExpression:
let member = initializer.parent;
let currentType = getTypeOfSymbol(getSymbolOfNode(member.parent));
let enumType: Type;
let propertyName: string;
if (e.kind === SyntaxKind.Identifier) {
// unqualified names can refer to member that reside in different declaration of the enum so just doing name resolution won't work.
// instead pick current enum type and later try to fetch member from the type
enumType = currentType;
propertyName = (<Identifier>e).text;
}
else {
let expression: Expression;
if (e.kind === SyntaxKind.ElementAccessExpression) {
if ((<ElementAccessExpression>e).argumentExpression === undefined ||
(<ElementAccessExpression>e).argumentExpression.kind !== SyntaxKind.StringLiteral) {
return undefined;
}
expression = (<ElementAccessExpression>e).expression;
propertyName = (<LiteralExpression>(<ElementAccessExpression>e).argumentExpression).text;
}
else {
expression = (<PropertyAccessExpression>e).expression;
propertyName = (<PropertyAccessExpression>e).name.text;
}
// expression part in ElementAccess\PropertyAccess should be either identifier or dottedName
var current = expression;
while (current) {
if (current.kind === SyntaxKind.Identifier) {
break;
}
else if (current.kind === SyntaxKind.PropertyAccessExpression) {
current = (<ElementAccessExpression>current).expression;
}
else {
return undefined;
}
}
enumType = checkExpression(expression);
// allow references to constant members of other enums
if (!(enumType.symbol && (enumType.symbol.flags & SymbolFlags.Enum))) {
return undefined;
}
}
if (propertyName === undefined) {
return undefined;
}
let property = getPropertyOfObjectType(enumType, propertyName);
if (!property || !(property.flags & SymbolFlags.EnumMember)) {
return undefined;
}
let propertyDecl = property.valueDeclaration;
// self references are illegal
if (member === propertyDecl) {
return undefined;
}
// illegal case: forward reference
if (!isDefinedBefore(propertyDecl, member)) {
return undefined;
}
return <number>getNodeLinks(propertyDecl).enumMemberValue;
}
}
}
}
function checkEnumDeclaration(node: EnumDeclaration) {
if (!produceDiagnostics) {
return;
}
// Grammar checking
checkGrammarDeclarationNameInStrictMode(node) || checkGrammarDecorators(node) || checkGrammarModifiers(node) || checkGrammarEnumDeclaration(node);
checkTypeNameIsReserved(node.name, Diagnostics.Enum_name_cannot_be_0);
checkCollisionWithCapturedThisVariable(node, node.name);
checkCollisionWithRequireExportsInGeneratedCode(node, node.name);
checkExportsOnMergedDeclarations(node);
computeEnumMemberValues(node);
let enumIsConst = isConst(node);
if (compilerOptions.separateCompilation && enumIsConst && isInAmbientContext(node)) {
error(node.name, Diagnostics.Ambient_const_enums_are_not_allowed_when_the_separateCompilation_flag_is_provided);
}
// Spec 2014 - Section 9.3:
// It isn't possible for one enum declaration to continue the automatic numbering sequence of another,
// and when an enum type has multiple declarations, only one declaration is permitted to omit a value
// for the first member.
//
// Only perform this check once per symbol
let enumSymbol = getSymbolOfNode(node);
let firstDeclaration = getDeclarationOfKind(enumSymbol, node.kind);
if (node === firstDeclaration) {
if (enumSymbol.declarations.length > 1) {
// check that const is placed\omitted on all enum declarations
forEach(enumSymbol.declarations, decl => {
if (isConstEnumDeclaration(decl) !== enumIsConst) {
error(decl.name, Diagnostics.Enum_declarations_must_all_be_const_or_non_const);
}
});
}
let seenEnumMissingInitialInitializer = false;
forEach(enumSymbol.declarations, declaration => {
// return true if we hit a violation of the rule, false otherwise
if (declaration.kind !== SyntaxKind.EnumDeclaration) {
return false;
}
let enumDeclaration = <EnumDeclaration>declaration;
if (!enumDeclaration.members.length) {
return false;
}
let firstEnumMember = enumDeclaration.members[0];
if (!firstEnumMember.initializer) {
if (seenEnumMissingInitialInitializer) {
error(firstEnumMember.name, Diagnostics.In_an_enum_with_multiple_declarations_only_one_declaration_can_omit_an_initializer_for_its_first_enum_element);
}
else {
seenEnumMissingInitialInitializer = true;
}
}
});
}
}
function getFirstNonAmbientClassOrFunctionDeclaration(symbol: Symbol): Declaration {
let declarations = symbol.declarations;
for (let declaration of declarations) {
if ((declaration.kind === SyntaxKind.ClassDeclaration ||
(declaration.kind === SyntaxKind.FunctionDeclaration && nodeIsPresent((<FunctionLikeDeclaration>declaration).body))) &&
!isInAmbientContext(declaration)) {
return declaration;
}
}
return undefined;
}
function inSameLexicalScope(node1: Node, node2: Node) {
let container1 = getEnclosingBlockScopeContainer(node1);
let container2 = getEnclosingBlockScopeContainer(node2);
if (isGlobalSourceFile(container1)) {
return isGlobalSourceFile(container2);
}
else if (isGlobalSourceFile(container2)) {
return false;
}
else {
return container1 === container2;
}
}
function checkModuleDeclaration(node: ModuleDeclaration) {
if (produceDiagnostics) {
// Grammar checking
if (!checkGrammarDeclarationNameInStrictMode(node) && !checkGrammarDecorators(node) && !checkGrammarModifiers(node)) {
if (!isInAmbientContext(node) && node.name.kind === SyntaxKind.StringLiteral) {
grammarErrorOnNode(node.name, Diagnostics.Only_ambient_modules_can_use_quoted_names);
}
}
checkCollisionWithCapturedThisVariable(node, node.name);
checkCollisionWithRequireExportsInGeneratedCode(node, node.name);
checkExportsOnMergedDeclarations(node);
let symbol = getSymbolOfNode(node);
// The following checks only apply on a non-ambient instantiated module declaration.
if (symbol.flags & SymbolFlags.ValueModule
&& symbol.declarations.length > 1
&& !isInAmbientContext(node)
&& isInstantiatedModule(node, compilerOptions.preserveConstEnums || compilerOptions.separateCompilation)) {
let firstNonAmbientClassOrFunc = getFirstNonAmbientClassOrFunctionDeclaration(symbol);
if (firstNonAmbientClassOrFunc) {
if (getSourceFileOfNode(node) !== getSourceFileOfNode(firstNonAmbientClassOrFunc)) {
error(node.name, Diagnostics.A_namespace_declaration_cannot_be_in_a_different_file_from_a_class_or_function_with_which_it_is_merged);
}
else if (node.pos < firstNonAmbientClassOrFunc.pos) {
error(node.name, Diagnostics.A_namespace_declaration_cannot_be_located_prior_to_a_class_or_function_with_which_it_is_merged);
}
}
// if the module merges with a class declaration in the same lexical scope,
// we need to track this to ensure the correct emit.
let mergedClass = getDeclarationOfKind(symbol, SyntaxKind.ClassDeclaration);
if (mergedClass &&
inSameLexicalScope(node, mergedClass)) {
getNodeLinks(node).flags |= NodeCheckFlags.LexicalModuleMergesWithClass;
}
}
// Checks for ambient external modules.
if (node.name.kind === SyntaxKind.StringLiteral) {
if (!isGlobalSourceFile(node.parent)) {
error(node.name, Diagnostics.Ambient_modules_cannot_be_nested_in_other_modules);
}
if (isExternalModuleNameRelative(node.name.text)) {
error(node.name, Diagnostics.Ambient_module_declaration_cannot_specify_relative_module_name);
}
}
}
checkSourceElement(node.body);
}
function getFirstIdentifier(node: EntityName | Expression): Identifier {
while (true) {
if (node.kind === SyntaxKind.QualifiedName) {
node = (<QualifiedName>node).left;
}
else if (node.kind === SyntaxKind.PropertyAccessExpression) {
node = (<PropertyAccessExpression>node).expression;
}
else {
break;
}
}
Debug.assert(node.kind === SyntaxKind.Identifier);
return <Identifier>node;
}
function checkExternalImportOrExportDeclaration(node: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration): boolean {
let moduleName = getExternalModuleName(node);
if (!nodeIsMissing(moduleName) && moduleName.kind !== SyntaxKind.StringLiteral) {
error(moduleName, Diagnostics.String_literal_expected);
return false;
}
let inAmbientExternalModule = node.parent.kind === SyntaxKind.ModuleBlock && (<ModuleDeclaration>node.parent.parent).name.kind === SyntaxKind.StringLiteral;
if (node.parent.kind !== SyntaxKind.SourceFile && !inAmbientExternalModule) {
error(moduleName, node.kind === SyntaxKind.ExportDeclaration ?
Diagnostics.Export_declarations_are_not_permitted_in_a_namespace :
Diagnostics.Import_declarations_in_a_namespace_cannot_reference_a_module);
return false;
}
if (inAmbientExternalModule && isExternalModuleNameRelative((<LiteralExpression>moduleName).text)) {
// TypeScript 1.0 spec (April 2013): 12.1.6
// An ExternalImportDeclaration in an AmbientExternalModuleDeclaration may reference
// other external modules only through top - level external module names.
// Relative external module names are not permitted.
error(node, Diagnostics.Import_or_export_declaration_in_an_ambient_module_declaration_cannot_reference_module_through_relative_module_name);
return false;
}
return true;
}
function checkAliasSymbol(node: ImportEqualsDeclaration | ImportClause | NamespaceImport | ImportSpecifier | ExportSpecifier) {
let symbol = getSymbolOfNode(node);
let target = resolveAlias(symbol);
if (target !== unknownSymbol) {
let excludedMeanings =
(symbol.flags & SymbolFlags.Value ? SymbolFlags.Value : 0) |
(symbol.flags & SymbolFlags.Type ? SymbolFlags.Type : 0) |
(symbol.flags & SymbolFlags.Namespace ? SymbolFlags.Namespace : 0);
if (target.flags & excludedMeanings) {
let message = node.kind === SyntaxKind.ExportSpecifier ?
Diagnostics.Export_declaration_conflicts_with_exported_declaration_of_0 :
Diagnostics.Import_declaration_conflicts_with_local_declaration_of_0;
error(node, message, symbolToString(symbol));
}
}
}
function checkImportBinding(node: ImportEqualsDeclaration | ImportClause | NamespaceImport | ImportSpecifier) {
checkCollisionWithCapturedThisVariable(node, node.name);
checkCollisionWithRequireExportsInGeneratedCode(node, node.name);
checkAliasSymbol(node);
}
function checkImportDeclaration(node: ImportDeclaration) {
if (!checkGrammarImportDeclarationNameInStrictMode(node) && !checkGrammarDecorators(node) && !checkGrammarModifiers(node) && (node.flags & NodeFlags.Modifier)) {
grammarErrorOnFirstToken(node, Diagnostics.An_import_declaration_cannot_have_modifiers);
}
if (checkExternalImportOrExportDeclaration(node)) {
let importClause = node.importClause;
if (importClause) {
if (importClause.name) {
checkImportBinding(importClause);
}
if (importClause.namedBindings) {
if (importClause.namedBindings.kind === SyntaxKind.NamespaceImport) {
checkImportBinding(<NamespaceImport>importClause.namedBindings);
}
else {
forEach((<NamedImports>importClause.namedBindings).elements, checkImportBinding);
}
}
}
}
}
function checkImportEqualsDeclaration(node: ImportEqualsDeclaration) {
checkGrammarDeclarationNameInStrictMode(node) || checkGrammarDecorators(node) || checkGrammarModifiers(node);
if (isInternalModuleImportEqualsDeclaration(node) || checkExternalImportOrExportDeclaration(node)) {
checkImportBinding(node);
if (node.flags & NodeFlags.Export) {
markExportAsReferenced(node);
}
if (isInternalModuleImportEqualsDeclaration(node)) {
let target = resolveAlias(getSymbolOfNode(node));
if (target !== unknownSymbol) {
if (target.flags & SymbolFlags.Value) {
// Target is a value symbol, check that it is not hidden by a local declaration with the same name
let moduleName = getFirstIdentifier(<EntityName>node.moduleReference);
if (!(resolveEntityName(moduleName, SymbolFlags.Value | SymbolFlags.Namespace).flags & SymbolFlags.Namespace)) {
error(moduleName, Diagnostics.Module_0_is_hidden_by_a_local_declaration_with_the_same_name, declarationNameToString(moduleName));
}
}
if (target.flags & SymbolFlags.Type) {
checkTypeNameIsReserved(node.name, Diagnostics.Import_name_cannot_be_0);
}
}
}
else {
if (languageVersion >= ScriptTarget.ES6) {
// Import equals declaration is deprecated in es6 or above
grammarErrorOnNode(node, Diagnostics.Import_assignment_cannot_be_used_when_targeting_ECMAScript_6_or_higher_Consider_using_import_Asterisk_as_ns_from_mod_import_a_from_mod_or_import_d_from_mod_instead);
}
}
}
}
function checkExportDeclaration(node: ExportDeclaration) {
if (!checkGrammarDecorators(node) && !checkGrammarModifiers(node) && (node.flags & NodeFlags.Modifier)) {
grammarErrorOnFirstToken(node, Diagnostics.An_export_declaration_cannot_have_modifiers);
}
if (!node.moduleSpecifier || checkExternalImportOrExportDeclaration(node)) {
if (node.exportClause) {
// export { x, y }
// export { x, y } from "foo"
forEach(node.exportClause.elements, checkExportSpecifier);
let inAmbientExternalModule = node.parent.kind === SyntaxKind.ModuleBlock && (<ModuleDeclaration>node.parent.parent).name.kind === SyntaxKind.StringLiteral;
if (node.parent.kind !== SyntaxKind.SourceFile && !inAmbientExternalModule) {
error(node, Diagnostics.Export_declarations_are_not_permitted_in_a_namespace);
}
}
else {
// export * from "foo"
let moduleSymbol = resolveExternalModuleName(node, node.moduleSpecifier);
if (moduleSymbol && moduleSymbol.exports["export="]) {
error(node.moduleSpecifier, Diagnostics.Module_0_uses_export_and_cannot_be_used_with_export_Asterisk, symbolToString(moduleSymbol));
}
}
}
}
function checkExportSpecifier(node: ExportSpecifier) {
checkAliasSymbol(node);
if (!(<ExportDeclaration>node.parent.parent).moduleSpecifier) {
markExportAsReferenced(node);
}
}
function checkExportAssignment(node: ExportAssignment) {
let container = node.parent.kind === SyntaxKind.SourceFile ? <SourceFile>node.parent : <ModuleDeclaration>node.parent.parent;
if (container.kind === SyntaxKind.ModuleDeclaration && (<ModuleDeclaration>container).name.kind === SyntaxKind.Identifier) {
error(node, Diagnostics.An_export_assignment_cannot_be_used_in_a_namespace);
return;
}
// Grammar checking
if (!checkGrammarDecorators(node) && !checkGrammarModifiers(node) && (node.flags & NodeFlags.Modifier)) {
grammarErrorOnFirstToken(node, Diagnostics.An_export_assignment_cannot_have_modifiers);
}
if (node.expression.kind === SyntaxKind.Identifier) {
markExportAsReferenced(node);
}
else {
checkExpressionCached(node.expression);
}
checkExternalModuleExports(container);
if (node.isExportEquals && !isInAmbientContext(node)) {
if (languageVersion >= ScriptTarget.ES6) {
// export assignment is deprecated in es6 or above
grammarErrorOnNode(node, Diagnostics.Export_assignment_cannot_be_used_when_targeting_ECMAScript_6_or_higher_Consider_using_export_default_instead);
}
else if (compilerOptions.module === ModuleKind.System) {
// system modules does not support export assignment
grammarErrorOnNode(node, Diagnostics.Export_assignment_is_not_supported_when_module_flag_is_system);
}
}
}
function getModuleStatements(node: Declaration): ModuleElement[] {
if (node.kind === SyntaxKind.SourceFile) {
return (<SourceFile>node).statements;
}
if (node.kind === SyntaxKind.ModuleDeclaration && (<ModuleDeclaration>node).body.kind === SyntaxKind.ModuleBlock) {
return (<ModuleBlock>(<ModuleDeclaration>node).body).statements;
}
return emptyArray;
}
function hasExportedMembers(moduleSymbol: Symbol) {
for (var id in moduleSymbol.exports) {
if (id !== "export=") {
return true;
}
}
return false;
}
function checkExternalModuleExports(node: SourceFile | ModuleDeclaration) {
let moduleSymbol = getSymbolOfNode(node);
let links = getSymbolLinks(moduleSymbol);
if (!links.exportsChecked) {
let exportEqualsSymbol = moduleSymbol.exports["export="];
if (exportEqualsSymbol && hasExportedMembers(moduleSymbol)) {
let declaration = getDeclarationOfAliasSymbol(exportEqualsSymbol) || exportEqualsSymbol.valueDeclaration;
error(declaration, Diagnostics.An_export_assignment_cannot_be_used_in_a_module_with_other_exported_elements);
}
links.exportsChecked = true;
}
}
function checkSourceElement(node: Node): void {
if (!node) return;
switch (node.kind) {
case SyntaxKind.TypeParameter:
return checkTypeParameter(<TypeParameterDeclaration>node);
case SyntaxKind.Parameter:
return checkParameter(<ParameterDeclaration>node);
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.PropertySignature:
return checkPropertyDeclaration(<PropertyDeclaration>node);
case SyntaxKind.FunctionType:
case SyntaxKind.ConstructorType:
case SyntaxKind.CallSignature:
case SyntaxKind.ConstructSignature:
return checkSignatureDeclaration(<SignatureDeclaration>node);
case SyntaxKind.IndexSignature:
return checkSignatureDeclaration(<SignatureDeclaration>node);
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
return checkMethodDeclaration(<MethodDeclaration>node);
case SyntaxKind.Constructor:
return checkConstructorDeclaration(<ConstructorDeclaration>node);
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
return checkAccessorDeclaration(<AccessorDeclaration>node);
case SyntaxKind.TypeReference:
return checkTypeReferenceNode(<TypeReferenceNode>node);
case SyntaxKind.TypeQuery:
return checkTypeQuery(<TypeQueryNode>node);
case SyntaxKind.TypeLiteral:
return checkTypeLiteral(<TypeLiteralNode>node);
case SyntaxKind.ArrayType:
return checkArrayType(<ArrayTypeNode>node);
case SyntaxKind.TupleType:
return checkTupleType(<TupleTypeNode>node);
case SyntaxKind.UnionType:
return checkUnionType(<UnionTypeNode>node);
case SyntaxKind.ParenthesizedType:
return checkSourceElement((<ParenthesizedTypeNode>node).type);
case SyntaxKind.FunctionDeclaration:
return checkFunctionDeclaration(<FunctionDeclaration>node);
case SyntaxKind.Block:
case SyntaxKind.ModuleBlock:
return checkBlock(<Block>node);
case SyntaxKind.VariableStatement:
return checkVariableStatement(<VariableStatement>node);
case SyntaxKind.ExpressionStatement:
return checkExpressionStatement(<ExpressionStatement>node);
case SyntaxKind.IfStatement:
return checkIfStatement(<IfStatement>node);
case SyntaxKind.DoStatement:
return checkDoStatement(<DoStatement>node);
case SyntaxKind.WhileStatement:
return checkWhileStatement(<WhileStatement>node);
case SyntaxKind.ForStatement:
return checkForStatement(<ForStatement>node);
case SyntaxKind.ForInStatement:
return checkForInStatement(<ForInStatement>node);
case SyntaxKind.ForOfStatement:
return checkForOfStatement(<ForOfStatement>node);
case SyntaxKind.ContinueStatement:
case SyntaxKind.BreakStatement:
return checkBreakOrContinueStatement(<BreakOrContinueStatement>node);
case SyntaxKind.ReturnStatement:
return checkReturnStatement(<ReturnStatement>node);
case SyntaxKind.WithStatement:
return checkWithStatement(<WithStatement>node);
case SyntaxKind.SwitchStatement:
return checkSwitchStatement(<SwitchStatement>node);
case SyntaxKind.LabeledStatement:
return checkLabeledStatement(<LabeledStatement>node);
case SyntaxKind.ThrowStatement:
return checkThrowStatement(<ThrowStatement>node);
case SyntaxKind.TryStatement:
return checkTryStatement(<TryStatement>node);
case SyntaxKind.VariableDeclaration:
return checkVariableDeclaration(<VariableDeclaration>node);
case SyntaxKind.BindingElement:
return checkBindingElement(<BindingElement>node);
case SyntaxKind.ClassDeclaration:
return checkClassDeclaration(<ClassDeclaration>node);
case SyntaxKind.InterfaceDeclaration:
return checkInterfaceDeclaration(<InterfaceDeclaration>node);
case SyntaxKind.TypeAliasDeclaration:
return checkTypeAliasDeclaration(<TypeAliasDeclaration>node);
case SyntaxKind.EnumDeclaration:
return checkEnumDeclaration(<EnumDeclaration>node);
case SyntaxKind.ModuleDeclaration:
return checkModuleDeclaration(<ModuleDeclaration>node);
case SyntaxKind.ImportDeclaration:
return checkImportDeclaration(<ImportDeclaration>node);
case SyntaxKind.ImportEqualsDeclaration:
return checkImportEqualsDeclaration(<ImportEqualsDeclaration>node);
case SyntaxKind.ExportDeclaration:
return checkExportDeclaration(<ExportDeclaration>node);
case SyntaxKind.ExportAssignment:
return checkExportAssignment(<ExportAssignment>node);
case SyntaxKind.EmptyStatement:
checkGrammarStatementInAmbientContext(node);
return;
case SyntaxKind.DebuggerStatement:
checkGrammarStatementInAmbientContext(node);
return;
case SyntaxKind.MissingDeclaration:
return checkMissingDeclaration(node);
}
}
// Function expression bodies are checked after all statements in the enclosing body. This is to ensure
// constructs like the following are permitted:
// let foo = function () {
// let s = foo();
// return "hello";
// }
// Here, performing a full type check of the body of the function expression whilst in the process of
// determining the type of foo would cause foo to be given type any because of the recursive reference.
// Delaying the type check of the body ensures foo has been assigned a type.
function checkFunctionExpressionBodies(node: Node): void {
switch (node.kind) {
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
forEach((<FunctionLikeDeclaration>node).parameters, checkFunctionExpressionBodies);
checkFunctionExpressionOrObjectLiteralMethodBody(<FunctionExpression>node);
break;
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
forEach(node.decorators, checkFunctionExpressionBodies);
forEach((<MethodDeclaration>node).parameters, checkFunctionExpressionBodies);
if (isObjectLiteralMethod(node)) {
checkFunctionExpressionOrObjectLiteralMethodBody(<MethodDeclaration>node);
}
break;
case SyntaxKind.Constructor:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.FunctionDeclaration:
forEach((<FunctionLikeDeclaration>node).parameters, checkFunctionExpressionBodies);
break;
case SyntaxKind.WithStatement:
checkFunctionExpressionBodies((<WithStatement>node).expression);
break;
case SyntaxKind.Decorator:
case SyntaxKind.Parameter:
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.PropertySignature:
case SyntaxKind.ObjectBindingPattern:
case SyntaxKind.ArrayBindingPattern:
case SyntaxKind.BindingElement:
case SyntaxKind.ArrayLiteralExpression:
case SyntaxKind.ObjectLiteralExpression:
case SyntaxKind.PropertyAssignment:
case SyntaxKind.PropertyAccessExpression:
case SyntaxKind.ElementAccessExpression:
case SyntaxKind.CallExpression:
case SyntaxKind.NewExpression:
case SyntaxKind.TaggedTemplateExpression:
case SyntaxKind.TemplateExpression:
case SyntaxKind.TemplateSpan:
case SyntaxKind.TypeAssertionExpression:
case SyntaxKind.ParenthesizedExpression:
case SyntaxKind.TypeOfExpression:
case SyntaxKind.VoidExpression:
case SyntaxKind.DeleteExpression:
case SyntaxKind.PrefixUnaryExpression:
case SyntaxKind.PostfixUnaryExpression:
case SyntaxKind.BinaryExpression:
case SyntaxKind.ConditionalExpression:
case SyntaxKind.SpreadElementExpression:
case SyntaxKind.Block:
case SyntaxKind.ModuleBlock:
case SyntaxKind.VariableStatement:
case SyntaxKind.ExpressionStatement:
case SyntaxKind.IfStatement:
case SyntaxKind.DoStatement:
case SyntaxKind.WhileStatement:
case SyntaxKind.ForStatement:
case SyntaxKind.ForInStatement:
case SyntaxKind.ForOfStatement:
case SyntaxKind.ContinueStatement:
case SyntaxKind.BreakStatement:
case SyntaxKind.ReturnStatement:
case SyntaxKind.SwitchStatement:
case SyntaxKind.CaseBlock:
case SyntaxKind.CaseClause:
case SyntaxKind.DefaultClause:
case SyntaxKind.LabeledStatement:
case SyntaxKind.ThrowStatement:
case SyntaxKind.TryStatement:
case SyntaxKind.CatchClause:
case SyntaxKind.VariableDeclaration:
case SyntaxKind.VariableDeclarationList:
case SyntaxKind.ClassDeclaration:
case SyntaxKind.EnumDeclaration:
case SyntaxKind.EnumMember:
case SyntaxKind.ExportAssignment:
case SyntaxKind.SourceFile:
forEachChild(node, checkFunctionExpressionBodies);
break;
}
}
function checkSourceFile(node: SourceFile) {
let start = new Date().getTime();
checkSourceFileWorker(node);
checkTime += new Date().getTime() - start;
}
// Fully type check a source file and collect the relevant diagnostics.
function checkSourceFileWorker(node: SourceFile) {
let links = getNodeLinks(node);
if (!(links.flags & NodeCheckFlags.TypeChecked)) {
// Grammar checking
checkGrammarSourceFile(node);
emitExtends = false;
emitDecorate = false;
emitParam = false;
potentialThisCollisions.length = 0;
forEach(node.statements, checkSourceElement);
checkFunctionExpressionBodies(node);
if (isExternalModule(node)) {
checkExternalModuleExports(node);
}
if (potentialThisCollisions.length) {
forEach(potentialThisCollisions, checkIfThisIsCapturedInEnclosingScope);
potentialThisCollisions.length = 0;
}
if (emitExtends) {
links.flags |= NodeCheckFlags.EmitExtends;
}
if (emitDecorate) {
links.flags |= NodeCheckFlags.EmitDecorate;
}
if (emitParam) {
links.flags |= NodeCheckFlags.EmitParam;
}
links.flags |= NodeCheckFlags.TypeChecked;
}
}
function getDiagnostics(sourceFile?: SourceFile): Diagnostic[] {
throwIfNonDiagnosticsProducing();
if (sourceFile) {
checkSourceFile(sourceFile);
return diagnostics.getDiagnostics(sourceFile.fileName);
}
forEach(host.getSourceFiles(), checkSourceFile);
return diagnostics.getDiagnostics();
}
function getGlobalDiagnostics(): Diagnostic[] {
throwIfNonDiagnosticsProducing();
return diagnostics.getGlobalDiagnostics();
}
function throwIfNonDiagnosticsProducing() {
if (!produceDiagnostics) {
throw new Error("Trying to get diagnostics from a type checker that does not produce them.");
}
}
// Language service support
function isInsideWithStatementBody(node: Node): boolean {
if (node) {
while (node.parent) {
if (node.parent.kind === SyntaxKind.WithStatement && (<WithStatement>node.parent).statement === node) {
return true;
}
node = node.parent;
}
}
return false;
}
function getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[] {
let symbols: SymbolTable = {};
let memberFlags: NodeFlags = 0;
if (isInsideWithStatementBody(location)) {
// We cannot answer semantic questions within a with block, do not proceed any further
return [];
}
populateSymbols();
return symbolsToArray(symbols);
function populateSymbols() {
while (location) {
if (location.locals && !isGlobalSourceFile(location)) {
copySymbols(location.locals, meaning);
}
switch (location.kind) {
case SyntaxKind.SourceFile:
if (!isExternalModule(<SourceFile>location)) {
break;
}
case SyntaxKind.ModuleDeclaration:
copySymbols(getSymbolOfNode(location).exports, meaning & SymbolFlags.ModuleMember);
break;
case SyntaxKind.EnumDeclaration:
copySymbols(getSymbolOfNode(location).exports, meaning & SymbolFlags.EnumMember);
break;
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
if (!(memberFlags & NodeFlags.Static)) {
copySymbols(getSymbolOfNode(location).members, meaning & SymbolFlags.Type);
}
break;
case SyntaxKind.FunctionExpression:
if ((<FunctionExpression>location).name) {
copySymbol(location.symbol, meaning);
}
break;
}
memberFlags = location.flags;
location = location.parent;
}
copySymbols(globals, meaning);
}
// Returns 'true' if we should stop processing symbols.
function copySymbol(symbol: Symbol, meaning: SymbolFlags): void {
if (symbol.flags & meaning) {
let id = symbol.name;
if (!isReservedMemberName(id) && !hasProperty(symbols, id)) {
symbols[id] = symbol;
}
}
}
function copySymbols(source: SymbolTable, meaning: SymbolFlags): void {
if (meaning) {
for (let id in source) {
if (hasProperty(source, id)) {
copySymbol(source[id], meaning);
}
}
}
}
if (isInsideWithStatementBody(location)) {
// We cannot answer semantic questions within a with block, do not proceed any further
return [];
}
while (location) {
if (location.locals && !isGlobalSourceFile(location)) {
copySymbols(location.locals, meaning);
}
switch (location.kind) {
case SyntaxKind.SourceFile:
if (!isExternalModule(<SourceFile>location)) break;
case SyntaxKind.ModuleDeclaration:
copySymbols(getSymbolOfNode(location).exports, meaning & SymbolFlags.ModuleMember);
break;
case SyntaxKind.EnumDeclaration:
copySymbols(getSymbolOfNode(location).exports, meaning & SymbolFlags.EnumMember);
break;
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
if (!(memberFlags & NodeFlags.Static)) {
copySymbols(getSymbolOfNode(location).members, meaning & SymbolFlags.Type);
}
break;
case SyntaxKind.FunctionExpression:
if ((<FunctionExpression>location).name) {
copySymbol(location.symbol, meaning);
}
break;
}
memberFlags = location.flags;
location = location.parent;
}
copySymbols(globals, meaning);
return symbolsToArray(symbols);
}
function isTypeDeclarationName(name: Node): boolean {
return name.kind == SyntaxKind.Identifier &&
isTypeDeclaration(name.parent) &&
(<Declaration>name.parent).name === name;
}
function isTypeDeclaration(node: Node): boolean {
switch (node.kind) {
case SyntaxKind.TypeParameter:
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.TypeAliasDeclaration:
case SyntaxKind.EnumDeclaration:
return true;
}
}
// True if the given identifier is part of a type reference
function isTypeReferenceIdentifier(entityName: EntityName): boolean {
let node: Node = entityName;
while (node.parent && node.parent.kind === SyntaxKind.QualifiedName) {
node = node.parent;
}
return node.parent && node.parent.kind === SyntaxKind.TypeReference;
}
function isHeritageClauseElementIdentifier(entityName: Node): boolean {
let node = entityName;
while (node.parent && node.parent.kind === SyntaxKind.PropertyAccessExpression) {
node = node.parent;
}
return node.parent && node.parent.kind === SyntaxKind.ExpressionWithTypeArguments;
}
function getLeftSideOfImportEqualsOrExportAssignment(nodeOnRightSide: EntityName): ImportEqualsDeclaration | ExportAssignment {
while (nodeOnRightSide.parent.kind === SyntaxKind.QualifiedName) {
nodeOnRightSide = <QualifiedName>nodeOnRightSide.parent;
}
if (nodeOnRightSide.parent.kind === SyntaxKind.ImportEqualsDeclaration) {
return (<ImportEqualsDeclaration>nodeOnRightSide.parent).moduleReference === nodeOnRightSide && <ImportEqualsDeclaration>nodeOnRightSide.parent;
}
if (nodeOnRightSide.parent.kind === SyntaxKind.ExportAssignment) {
return (<ExportAssignment>nodeOnRightSide.parent).expression === <Node>nodeOnRightSide && <ExportAssignment>nodeOnRightSide.parent;
}
return undefined;
}
function isInRightSideOfImportOrExportAssignment(node: EntityName) {
return getLeftSideOfImportEqualsOrExportAssignment(node) !== undefined;
}
function getSymbolOfEntityNameOrPropertyAccessExpression(entityName: EntityName | PropertyAccessExpression): Symbol {
if (isDeclarationName(entityName)) {
return getSymbolOfNode(entityName.parent);
}
if (entityName.parent.kind === SyntaxKind.ExportAssignment) {
return resolveEntityName(<Identifier>entityName,
/*all meanings*/ SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias);
}
if (entityName.kind !== SyntaxKind.PropertyAccessExpression) {
if (isInRightSideOfImportOrExportAssignment(<EntityName>entityName)) {
// Since we already checked for ExportAssignment, this really could only be an Import
return getSymbolOfPartOfRightHandSideOfImportEquals(<EntityName>entityName);
}
}
if (isRightSideOfQualifiedNameOrPropertyAccess(entityName)) {
entityName = <QualifiedName | PropertyAccessExpression>entityName.parent;
}
if (isHeritageClauseElementIdentifier(<EntityName>entityName)) {
let meaning = entityName.parent.kind === SyntaxKind.ExpressionWithTypeArguments ? SymbolFlags.Type : SymbolFlags.Namespace;
meaning |= SymbolFlags.Alias;
return resolveEntityName(<EntityName>entityName, meaning);
}
else if (isExpression(entityName)) {
if (nodeIsMissing(entityName)) {
// Missing entity name.
return undefined;
}
if (entityName.kind === SyntaxKind.Identifier) {
// Include aliases in the meaning, this ensures that we do not follow aliases to where they point and instead
// return the alias symbol.
let meaning: SymbolFlags = SymbolFlags.Value | SymbolFlags.Alias;
return resolveEntityName(<Identifier>entityName, meaning);
}
else if (entityName.kind === SyntaxKind.PropertyAccessExpression) {
let symbol = getNodeLinks(entityName).resolvedSymbol;
if (!symbol) {
checkPropertyAccessExpression(<PropertyAccessExpression>entityName);
}
return getNodeLinks(entityName).resolvedSymbol;
}
else if (entityName.kind === SyntaxKind.QualifiedName) {
let symbol = getNodeLinks(entityName).resolvedSymbol;
if (!symbol) {
checkQualifiedName(<QualifiedName>entityName);
}
return getNodeLinks(entityName).resolvedSymbol;
}
}
else if (isTypeReferenceIdentifier(<EntityName>entityName)) {
let meaning = entityName.parent.kind === SyntaxKind.TypeReference ? SymbolFlags.Type : SymbolFlags.Namespace;
// Include aliases in the meaning, this ensures that we do not follow aliases to where they point and instead
// return the alias symbol.
meaning |= SymbolFlags.Alias;
return resolveEntityName(<EntityName>entityName, meaning);
}
// Do we want to return undefined here?
return undefined;
}
function getSymbolInfo(node: Node) {
if (isInsideWithStatementBody(node)) {
// We cannot answer semantic questions within a with block, do not proceed any further
return undefined;
}
if (isDeclarationName(node)) {
// This is a declaration, call getSymbolOfNode
return getSymbolOfNode(node.parent);
}
if (node.kind === SyntaxKind.Identifier && isInRightSideOfImportOrExportAssignment(<Identifier>node)) {
return node.parent.kind === SyntaxKind.ExportAssignment
? getSymbolOfEntityNameOrPropertyAccessExpression(<Identifier>node)
: getSymbolOfPartOfRightHandSideOfImportEquals(<Identifier>node);
}
switch (node.kind) {
case SyntaxKind.Identifier:
case SyntaxKind.PropertyAccessExpression:
case SyntaxKind.QualifiedName:
return getSymbolOfEntityNameOrPropertyAccessExpression(<EntityName | PropertyAccessExpression>node);
case SyntaxKind.ThisKeyword:
case SyntaxKind.SuperKeyword:
let type = checkExpression(<Expression>node);
return type.symbol;
case SyntaxKind.ConstructorKeyword:
// constructor keyword for an overload, should take us to the definition if it exist
let constructorDeclaration = node.parent;
if (constructorDeclaration && constructorDeclaration.kind === SyntaxKind.Constructor) {
return (<ClassDeclaration>constructorDeclaration.parent).symbol;
}
return undefined;
case SyntaxKind.StringLiteral:
// External module name in an import declaration
let moduleName: Expression;
if ((isExternalModuleImportEqualsDeclaration(node.parent.parent) &&
getExternalModuleImportEqualsDeclarationExpression(node.parent.parent) === node) ||
((node.parent.kind === SyntaxKind.ImportDeclaration || node.parent.kind === SyntaxKind.ExportDeclaration) &&
(<ImportDeclaration>node.parent).moduleSpecifier === node)) {
return resolveExternalModuleName(node, <LiteralExpression>node);
}
// Intentional fall-through
case SyntaxKind.NumericLiteral:
// index access
if (node.parent.kind == SyntaxKind.ElementAccessExpression && (<ElementAccessExpression>node.parent).argumentExpression === node) {
let objectType = checkExpression((<ElementAccessExpression>node.parent).expression);
if (objectType === unknownType) return undefined;
let apparentType = getApparentType(objectType);
if (apparentType === unknownType) return undefined;
return getPropertyOfType(apparentType, (<LiteralExpression>node).text);
}
break;
}
return undefined;
}
function getShorthandAssignmentValueSymbol(location: Node): Symbol {
// The function returns a value symbol of an identifier in the short-hand property assignment.
// This is necessary as an identifier in short-hand property assignment can contains two meaning:
// property name and property value.
if (location && location.kind === SyntaxKind.ShorthandPropertyAssignment) {
return resolveEntityName((<ShorthandPropertyAssignment>location).name, SymbolFlags.Value);
}
return undefined;
}
function getTypeOfNode(node: Node): Type {
if (isInsideWithStatementBody(node)) {
// We cannot answer semantic questions within a with block, do not proceed any further
return unknownType;
}
if (isTypeNode(node)) {
return getTypeFromTypeNode(<TypeNode>node);
}
if (isExpression(node)) {
return getTypeOfExpression(<Expression>node);
}
if (isTypeDeclaration(node)) {
// In this case, we call getSymbolOfNode instead of getSymbolInfo because it is a declaration
let symbol = getSymbolOfNode(node);
return getDeclaredTypeOfSymbol(symbol);
}
if (isTypeDeclarationName(node)) {
let symbol = getSymbolInfo(node);
return symbol && getDeclaredTypeOfSymbol(symbol);
}
if (isDeclaration(node)) {
// In this case, we call getSymbolOfNode instead of getSymbolInfo because it is a declaration
let symbol = getSymbolOfNode(node);
return getTypeOfSymbol(symbol);
}
if (isDeclarationName(node)) {
let symbol = getSymbolInfo(node);
return symbol && getTypeOfSymbol(symbol);
}
if (isInRightSideOfImportOrExportAssignment(<Identifier>node)) {
let symbol = getSymbolInfo(node);
let declaredType = symbol && getDeclaredTypeOfSymbol(symbol);
return declaredType !== unknownType ? declaredType : getTypeOfSymbol(symbol);
}
return unknownType;
}
function getTypeOfExpression(expr: Expression): Type {
if (isRightSideOfQualifiedNameOrPropertyAccess(expr)) {
expr = <Expression>expr.parent;
}
return checkExpression(expr);
}
// Return the list of properties of the given type, augmented with properties from Function
// if the type has call or construct signatures
function getAugmentedPropertiesOfType(type: Type): Symbol[] {
type = getApparentType(type);
let propsByName = createSymbolTable(getPropertiesOfType(type));
if (getSignaturesOfType(type, SignatureKind.Call).length || getSignaturesOfType(type, SignatureKind.Construct).length) {
forEach(getPropertiesOfType(globalFunctionType), p => {
if (!hasProperty(propsByName, p.name)) {
propsByName[p.name] = p;
}
});
}
return getNamedMembers(propsByName);
}
function getRootSymbols(symbol: Symbol): Symbol[] {
if (symbol.flags & SymbolFlags.UnionProperty) {
let symbols: Symbol[] = [];
let name = symbol.name;
forEach(getSymbolLinks(symbol).unionType.types, t => {
symbols.push(getPropertyOfType(t, name));
});
return symbols;
}
else if (symbol.flags & SymbolFlags.Transient) {
let target = getSymbolLinks(symbol).target;
if (target) {
return [target];
}
}
return [symbol];
}
// Emitter support
function isExternalModuleSymbol(symbol: Symbol): boolean {
return symbol.flags & SymbolFlags.ValueModule && symbol.declarations.length === 1 && symbol.declarations[0].kind === SyntaxKind.SourceFile;
}
function getAliasNameSubstitution(symbol: Symbol, getGeneratedNameForNode: (node: Node) => string): string {
// If this is es6 or higher, just use the name of the export
// no need to qualify it.
if (languageVersion >= ScriptTarget.ES6) {
return undefined;
}
let node = getDeclarationOfAliasSymbol(symbol);
if (node) {
if (node.kind === SyntaxKind.ImportClause) {
let defaultKeyword: string;
if (languageVersion === ScriptTarget.ES3) {
defaultKeyword = "[\"default\"]";
} else {
defaultKeyword = ".default";
}
return getGeneratedNameForNode(<ImportDeclaration>node.parent) + defaultKeyword;
}
if (node.kind === SyntaxKind.ImportSpecifier) {
let moduleName = getGeneratedNameForNode(<ImportDeclaration>node.parent.parent.parent);
let propertyName = (<ImportSpecifier>node).propertyName || (<ImportSpecifier>node).name;
return moduleName + "." + unescapeIdentifier(propertyName.text);
}
}
}
function getExportNameSubstitution(symbol: Symbol, location: Node, getGeneratedNameForNode: (Node: Node) => string): string {
if (isExternalModuleSymbol(symbol.parent)) {
// 1. If this is es6 or higher, just use the name of the export
// no need to qualify it.
// 2. export mechanism for System modules is different from CJS\AMD
// and it does not need qualifications for exports
if (languageVersion >= ScriptTarget.ES6 || compilerOptions.module === ModuleKind.System) {
return undefined;
}
return "exports." + unescapeIdentifier(symbol.name);
}
let node = location;
let containerSymbol = getParentOfSymbol(symbol);
while (node) {
if ((node.kind === SyntaxKind.ModuleDeclaration || node.kind === SyntaxKind.EnumDeclaration) && getSymbolOfNode(node) === containerSymbol) {
return getGeneratedNameForNode(<ModuleDeclaration | EnumDeclaration>node) + "." + unescapeIdentifier(symbol.name);
}
node = node.parent;
}
}
function getExpressionNameSubstitution(node: Identifier, getGeneratedNameForNode: (Node: Node) => string): string {
let symbol = getNodeLinks(node).resolvedSymbol || (isDeclarationName(node) ? getSymbolOfNode(node.parent) : undefined);
if (symbol) {
// Whan an identifier resolves to a parented symbol, it references an exported entity from
// another declaration of the same internal module.
if (symbol.parent) {
return getExportNameSubstitution(symbol, node.parent, getGeneratedNameForNode);
}
// If we reference an exported entity within the same module declaration, then whether
// we prefix depends on the kind of entity. SymbolFlags.ExportHasLocal encompasses all the
// kinds that we do NOT prefix.
let exportSymbol = getExportSymbolOfValueSymbolIfExported(symbol);
if (symbol !== exportSymbol && !(exportSymbol.flags & SymbolFlags.ExportHasLocal)) {
return getExportNameSubstitution(exportSymbol, node.parent, getGeneratedNameForNode);
}
// Named imports from ES6 import declarations are rewritten
if (symbol.flags & SymbolFlags.Alias) {
return getAliasNameSubstitution(symbol, getGeneratedNameForNode);
}
}
}
function isValueAliasDeclaration(node: Node): boolean {
switch (node.kind) {
case SyntaxKind.ImportEqualsDeclaration:
case SyntaxKind.ImportClause:
case SyntaxKind.NamespaceImport:
case SyntaxKind.ImportSpecifier:
case SyntaxKind.ExportSpecifier:
return isAliasResolvedToValue(getSymbolOfNode(node));
case SyntaxKind.ExportDeclaration:
let exportClause = (<ExportDeclaration>node).exportClause;
return exportClause && forEach(exportClause.elements, isValueAliasDeclaration);
case SyntaxKind.ExportAssignment:
return (<ExportAssignment>node).expression && (<ExportAssignment>node).expression.kind === SyntaxKind.Identifier ? isAliasResolvedToValue(getSymbolOfNode(node)) : true;
}
return false;
}
function isTopLevelValueImportEqualsWithEntityName(node: ImportEqualsDeclaration): boolean {
if (node.parent.kind !== SyntaxKind.SourceFile || !isInternalModuleImportEqualsDeclaration(node)) {
// parent is not source file or it is not reference to internal module
return false;
}
var isValue = isAliasResolvedToValue(getSymbolOfNode(node));
return isValue && node.moduleReference && !nodeIsMissing(node.moduleReference);
}
function isAliasResolvedToValue(symbol: Symbol): boolean {
let target = resolveAlias(symbol);
if (target === unknownSymbol && compilerOptions.separateCompilation) {
return true;
}
// const enums and modules that contain only const enums are not considered values from the emit perespective
return target !== unknownSymbol && target && target.flags & SymbolFlags.Value && !isConstEnumOrConstEnumOnlyModule(target);
}
function isConstEnumOrConstEnumOnlyModule(s: Symbol): boolean {
return isConstEnumSymbol(s) || s.constEnumOnlyModule;
}
function isReferencedAliasDeclaration(node: Node, checkChildren?: boolean): boolean {
if (isAliasSymbolDeclaration(node)) {
let symbol = getSymbolOfNode(node);
if (getSymbolLinks(symbol).referenced) {
return true;
}
}
if (checkChildren) {
return forEachChild(node, node => isReferencedAliasDeclaration(node, checkChildren));
}
return false;
}
function isImplementationOfOverload(node: FunctionLikeDeclaration) {
if (nodeIsPresent(node.body)) {
let symbol = getSymbolOfNode(node);
let signaturesOfSymbol = getSignaturesOfSymbol(symbol);
// If this function body corresponds to function with multiple signature, it is implementation of overload
// e.g.: function foo(a: string): string;
// function foo(a: number): number;
// function foo(a: any) { // This is implementation of the overloads
// return a;
// }
return signaturesOfSymbol.length > 1 ||
// If there is single signature for the symbol, it is overload if that signature isn't coming from the node
// e.g.: function foo(a: string): string;
// function foo(a: any) { // This is implementation of the overloads
// return a;
// }
(signaturesOfSymbol.length === 1 && signaturesOfSymbol[0].declaration !== node);
}
return false;
}
function getNodeCheckFlags(node: Node): NodeCheckFlags {
return getNodeLinks(node).flags;
}
function getEnumMemberValue(node: EnumMember): number {
computeEnumMemberValues(<EnumDeclaration>node.parent);
return getNodeLinks(node).enumMemberValue;
}
function getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): number {
if (node.kind === SyntaxKind.EnumMember) {
return getEnumMemberValue(<EnumMember>node);
}
let symbol = getNodeLinks(node).resolvedSymbol;
if (symbol && (symbol.flags & SymbolFlags.EnumMember)) {
// inline property\index accesses only for const enums
if (isConstEnumDeclaration(symbol.valueDeclaration.parent)) {
return getEnumMemberValue(<EnumMember>symbol.valueDeclaration);
}
}
return undefined;
}
/** Serializes an EntityName (with substitutions) to an appropriate JS constructor value. Used by the __metadata decorator. */
function serializeEntityName(node: EntityName, getGeneratedNameForNode: (Node: Node) => string, fallbackPath?: string[]): string {
if (node.kind === SyntaxKind.Identifier) {
var substitution = getExpressionNameSubstitution(<Identifier>node, getGeneratedNameForNode);
var text = substitution || (<Identifier>node).text;
if (fallbackPath) {
fallbackPath.push(text);
}
else {
return text;
}
}
else {
var left = serializeEntityName((<QualifiedName>node).left, getGeneratedNameForNode, fallbackPath);
var right = serializeEntityName((<QualifiedName>node).right, getGeneratedNameForNode, fallbackPath);
if (!fallbackPath) {
return left + "." + right;
}
}
}
/** Serializes a TypeReferenceNode to an appropriate JS constructor value. Used by the __metadata decorator. */
function serializeTypeReferenceNode(node: TypeReferenceNode, getGeneratedNameForNode: (Node: Node) => string): string | string[] {
// serialization of a TypeReferenceNode uses the following rules:
//
// * The serialized type of a TypeReference that is `void` is "void 0".
// * The serialized type of a TypeReference that is a `boolean` is "Boolean".
// * The serialized type of a TypeReference that is an enum or `number` is "Number".
// * The serialized type of a TypeReference that is a string literal or `string` is "String".
// * The serialized type of a TypeReference that is a tuple is "Array".
// * The serialized type of a TypeReference that is a `symbol` is "Symbol".
// * The serialized type of a TypeReference with a value declaration is its entity name.
// * The serialized type of a TypeReference with a call or construct signature is "Function".
// * The serialized type of any other type is "Object".
let type = getTypeFromTypeNode(node);
if (type.flags & TypeFlags.Void) {
return "void 0";
}
else if (type.flags & TypeFlags.Boolean) {
return "Boolean";
}
else if (type.flags & TypeFlags.NumberLike) {
return "Number";
}
else if (type.flags & TypeFlags.StringLike) {
return "String";
}
else if (type.flags & TypeFlags.Tuple) {
return "Array";
}
else if (type.flags & TypeFlags.ESSymbol) {
return "Symbol";
}
else if (type === unknownType) {
var fallbackPath: string[] = [];
serializeEntityName(node.typeName, getGeneratedNameForNode, fallbackPath);
return fallbackPath;
}
else if (type.symbol && type.symbol.valueDeclaration) {
return serializeEntityName(node.typeName, getGeneratedNameForNode);
}
else if (typeHasCallOrConstructSignatures(type)) {
return "Function";
}
return "Object";
}
/** Serializes a TypeNode to an appropriate JS constructor value. Used by the __metadata decorator. */
function serializeTypeNode(node: TypeNode | LiteralExpression, getGeneratedNameForNode: (Node: Node) => string): string | string[] {
// serialization of a TypeNode uses the following rules:
//
// * The serialized type of `void` is "void 0" (undefined).
// * The serialized type of a parenthesized type is the serialized type of its nested type.
// * The serialized type of a Function or Constructor type is "Function".
// * The serialized type of an Array or Tuple type is "Array".
// * The serialized type of `boolean` is "Boolean".
// * The serialized type of `string` or a string-literal type is "String".
// * The serialized type of a type reference is handled by `serializeTypeReferenceNode`.
// * The serialized type of any other type node is "Object".
if (node) {
switch (node.kind) {
case SyntaxKind.VoidKeyword:
return "void 0";
case SyntaxKind.ParenthesizedType:
return serializeTypeNode((<ParenthesizedTypeNode>node).type, getGeneratedNameForNode);
case SyntaxKind.FunctionType:
case SyntaxKind.ConstructorType:
return "Function";
case SyntaxKind.ArrayType:
case SyntaxKind.TupleType:
return "Array";
case SyntaxKind.BooleanKeyword:
return "Boolean";
case SyntaxKind.StringKeyword:
case SyntaxKind.StringLiteral:
return "String";
case SyntaxKind.NumberKeyword:
return "Number";
case SyntaxKind.TypeReference:
return serializeTypeReferenceNode(<TypeReferenceNode>node, getGeneratedNameForNode);
case SyntaxKind.TypeQuery:
case SyntaxKind.TypeLiteral:
case SyntaxKind.UnionType:
case SyntaxKind.AnyKeyword:
break;
default:
Debug.fail("Cannot serialize unexpected type node.");
break;
}
}
return "Object";
}
/** Serializes the type of a declaration to an appropriate JS constructor value. Used by the __metadata decorator for a class member. */
function serializeTypeOfNode(node: Node, getGeneratedNameForNode: (Node: Node) => string): string | string[] {
// serialization of the type of a declaration uses the following rules:
//
// * The serialized type of a ClassDeclaration is "Function"
// * The serialized type of a ParameterDeclaration is the serialized type of its type annotation.
// * The serialized type of a PropertyDeclaration is the serialized type of its type annotation.
// * The serialized type of an AccessorDeclaration is the serialized type of the return type annotation of its getter or parameter type annotation of its setter.
// * The serialized type of any other FunctionLikeDeclaration is "Function".
// * The serialized type of any other node is "void 0".
//
// For rules on serializing type annotations, see `serializeTypeNode`.
switch (node.kind) {
case SyntaxKind.ClassDeclaration: return "Function";
case SyntaxKind.PropertyDeclaration: return serializeTypeNode((<PropertyDeclaration>node).type, getGeneratedNameForNode);
case SyntaxKind.Parameter: return serializeTypeNode((<ParameterDeclaration>node).type, getGeneratedNameForNode);
case SyntaxKind.GetAccessor: return serializeTypeNode((<AccessorDeclaration>node).type, getGeneratedNameForNode);
case SyntaxKind.SetAccessor: return serializeTypeNode(getSetAccessorTypeAnnotationNode(<AccessorDeclaration>node), getGeneratedNameForNode);
}
if (isFunctionLike(node)) {
return "Function";
}
return "void 0";
}
/** Serializes the parameter types of a function or the constructor of a class. Used by the __metadata decorator for a method or set accessor. */
function serializeParameterTypesOfNode(node: Node, getGeneratedNameForNode: (Node: Node) => string): (string | string[])[] {
// serialization of parameter types uses the following rules:
//
// * If the declaration is a class, the parameters of the first constructor with a body are used.
// * If the declaration is function-like and has a body, the parameters of the function are used.
//
// For the rules on serializing the type of each parameter declaration, see `serializeTypeOfDeclaration`.
if (node) {
var valueDeclaration: FunctionLikeDeclaration;
if (node.kind === SyntaxKind.ClassDeclaration) {
valueDeclaration = getFirstConstructorWithBody(<ClassDeclaration>node);
}
else if (isFunctionLike(node) && nodeIsPresent((<FunctionLikeDeclaration>node).body)) {
valueDeclaration = <FunctionLikeDeclaration>node;
}
if (valueDeclaration) {
var result: (string | string[])[];
var parameters = valueDeclaration.parameters;
var parameterCount = parameters.length;
if (parameterCount > 0) {
result = new Array<string>(parameterCount);
for (var i = 0; i < parameterCount; i++) {
if (parameters[i].dotDotDotToken) {
var parameterType = parameters[i].type;
if (parameterType.kind === SyntaxKind.ArrayType) {
parameterType = (<ArrayTypeNode>parameterType).elementType;
}
else if (parameterType.kind === SyntaxKind.TypeReference && (<TypeReferenceNode>parameterType).typeArguments && (<TypeReferenceNode>parameterType).typeArguments.length === 1) {
parameterType = (<TypeReferenceNode>parameterType).typeArguments[0];
}
else {
parameterType = undefined;
}
result[i] = serializeTypeNode(parameterType, getGeneratedNameForNode);
}
else {
result[i] = serializeTypeOfNode(parameters[i], getGeneratedNameForNode);
}
}
return result;
}
}
}
return emptyArray;
}
/** Serializes the return type of function. Used by the __metadata decorator for a method. */
function serializeReturnTypeOfNode(node: Node, getGeneratedNameForNode: (Node: Node) => string): string | string[] {
if (node && isFunctionLike(node)) {
return serializeTypeNode((<FunctionLikeDeclaration>node).type, getGeneratedNameForNode);
}
return "void 0";
}
function writeTypeOfDeclaration(declaration: AccessorDeclaration | VariableLikeDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter) {
// Get type of the symbol if this is the valid symbol otherwise get type at location
let symbol = getSymbolOfNode(declaration);
let type = symbol && !(symbol.flags & (SymbolFlags.TypeLiteral | SymbolFlags.Signature))
? getTypeOfSymbol(symbol)
: unknownType;
getSymbolDisplayBuilder().buildTypeDisplay(type, writer, enclosingDeclaration, flags);
}
function writeReturnTypeOfSignatureDeclaration(signatureDeclaration: SignatureDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter) {
let signature = getSignatureFromDeclaration(signatureDeclaration);
getSymbolDisplayBuilder().buildTypeDisplay(getReturnTypeOfSignature(signature), writer, enclosingDeclaration, flags);
}
function writeTypeOfExpression(expr: Expression, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter) {
var type = getTypeOfExpression(expr);
getSymbolDisplayBuilder().buildTypeDisplay(type, writer, enclosingDeclaration, flags);
}
function hasGlobalName(name: string): boolean {
return hasProperty(globals, name);
}
function resolvesToSomeValue(location: Node, name: string): boolean {
Debug.assert(!nodeIsSynthesized(location), "resolvesToSomeValue called with a synthesized location");
return !!resolveName(location, name, SymbolFlags.Value, /*nodeNotFoundMessage*/ undefined, /*nameArg*/ undefined);
}
function getReferencedValueDeclaration(reference: Identifier): Declaration {
Debug.assert(!nodeIsSynthesized(reference));
let symbol =
getNodeLinks(reference).resolvedSymbol ||
resolveName(reference, reference.text, SymbolFlags.Value | SymbolFlags.Alias, /*nodeNotFoundMessage*/ undefined, /*nameArg*/ undefined);
return symbol && getExportSymbolOfValueSymbolIfExported(symbol).valueDeclaration;
}
function getBlockScopedVariableId(n: Identifier): number {
Debug.assert(!nodeIsSynthesized(n));
let isVariableDeclarationOrBindingElement =
n.parent.kind === SyntaxKind.BindingElement || (n.parent.kind === SyntaxKind.VariableDeclaration && (<VariableDeclaration>n.parent).name === n);
let symbol =
(isVariableDeclarationOrBindingElement ? getSymbolOfNode(n.parent) : undefined) ||
getNodeLinks(n).resolvedSymbol ||
resolveName(n, n.text, SymbolFlags.Value | SymbolFlags.Alias, /*nodeNotFoundMessage*/ undefined, /*nameArg*/ undefined);
let isLetOrConst =
symbol &&
(symbol.flags & SymbolFlags.BlockScopedVariable) &&
symbol.valueDeclaration.parent.kind !== SyntaxKind.CatchClause;
if (isLetOrConst) {
// side-effect of calling this method:
// assign id to symbol if it was not yet set
getSymbolLinks(symbol);
return symbol.id;
}
return undefined;
}
function instantiateSingleCallFunctionType(functionType: Type, typeArguments: Type[]): Type {
if (functionType === unknownType) {
return unknownType;
}
let signature = getSingleCallSignature(functionType);
if (!signature) {
return unknownType;
}
let instantiatedSignature = getSignatureInstantiation(signature, typeArguments);
return getOrCreateTypeFromSignature(instantiatedSignature);
}
function createResolver(): EmitResolver {
return {
getExpressionNameSubstitution,
isValueAliasDeclaration,
hasGlobalName,
isReferencedAliasDeclaration,
getNodeCheckFlags,
isTopLevelValueImportEqualsWithEntityName,
isDeclarationVisible,
isImplementationOfOverload,
writeTypeOfDeclaration,
writeReturnTypeOfSignatureDeclaration,
writeTypeOfExpression,
isSymbolAccessible,
isEntityNameVisible,
getConstantValue,
resolvesToSomeValue,
collectLinkedAliases,
getBlockScopedVariableId,
getReferencedValueDeclaration,
serializeTypeOfNode,
serializeParameterTypesOfNode,
serializeReturnTypeOfNode,
};
}
function initializeTypeChecker() {
// Bind all source files and propagate errors
forEach(host.getSourceFiles(), file => {
bindSourceFile(file);
});
// Initialize global symbol table
forEach(host.getSourceFiles(), file => {
if (!isExternalModule(file)) {
mergeSymbolTable(globals, file.locals);
}
});
// Initialize special symbols
getSymbolLinks(undefinedSymbol).type = undefinedType;
getSymbolLinks(argumentsSymbol).type = getGlobalType("IArguments");
getSymbolLinks(unknownSymbol).type = unknownType;
globals[undefinedSymbol.name] = undefinedSymbol;
// Initialize special types
globalArrayType = <GenericType>getGlobalType("Array", /*arity*/ 1);
globalObjectType = getGlobalType("Object");
globalFunctionType = getGlobalType("Function");
globalStringType = getGlobalType("String");
globalNumberType = getGlobalType("Number");
globalBooleanType = getGlobalType("Boolean");
globalRegExpType = getGlobalType("RegExp");
getGlobalClassDecoratorType = memoize(() => getGlobalType("ClassDecorator"));
getGlobalPropertyDecoratorType = memoize(() => getGlobalType("PropertyDecorator"));
getGlobalMethodDecoratorType = memoize(() => getGlobalType("MethodDecorator"));
getGlobalParameterDecoratorType = memoize(() => getGlobalType("ParameterDecorator"));
// If we're in ES6 mode, load the TemplateStringsArray.
// Otherwise, default to 'unknown' for the purposes of type checking in LS scenarios.
if (languageVersion >= ScriptTarget.ES6) {
globalTemplateStringsArrayType = getGlobalType("TemplateStringsArray");
globalESSymbolType = getGlobalType("Symbol");
globalESSymbolConstructorSymbol = getGlobalValueSymbol("Symbol");
globalIterableType = <GenericType>getGlobalType("Iterable", /*arity*/ 1);
globalIteratorType = <GenericType>getGlobalType("Iterator", /*arity*/ 1);
globalIterableIteratorType = <GenericType>getGlobalType("IterableIterator", /*arity*/ 1);
}
else {
globalTemplateStringsArrayType = unknownType;
// Consider putting Symbol interface in lib.d.ts. On the plus side, putting it in lib.d.ts would make it
// extensible for Polyfilling Symbols. But putting it into lib.d.ts could also break users that have
// a global Symbol already, particularly if it is a class.
globalESSymbolType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
globalESSymbolConstructorSymbol = undefined;
globalIterableType = emptyGenericType;
globalIteratorType = emptyGenericType;
globalIterableIteratorType = emptyGenericType;
}
anyArrayType = createArrayType(anyType);
}
// GRAMMAR CHECKING
function isReservedWordInStrictMode(node: Identifier): boolean {
// Check that originalKeywordKind is less than LastFutureReservedWord to see if an Identifier is a strict-mode reserved word
return (node.parserContextFlags & ParserContextFlags.StrictMode) &&
(SyntaxKind.FirstFutureReservedWord <= node.originalKeywordKind && node.originalKeywordKind <= SyntaxKind.LastFutureReservedWord);
}
function reportStrictModeGrammarErrorInClassDeclaration(identifier: Identifier, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): boolean {
// We are checking if this name is inside class declaration or class expression (which are under class definitions inside ES6 spec.)
// if so, we would like to give more explicit invalid usage error.
if (getAncestor(identifier, SyntaxKind.ClassDeclaration) || getAncestor(identifier, SyntaxKind.ClassExpression)) {
return grammarErrorOnNode(identifier, message, arg0);
}
return false;
}
function checkGrammarImportDeclarationNameInStrictMode(node: ImportDeclaration): boolean {
// Check if the import declaration used strict-mode reserved word in its names bindings
if (node.importClause) {
let impotClause = node.importClause;
if (impotClause.namedBindings) {
let nameBindings = impotClause.namedBindings;
if (nameBindings.kind === SyntaxKind.NamespaceImport) {
let name = <Identifier>(<NamespaceImport>nameBindings).name;
if (isReservedWordInStrictMode(name)) {
let nameText = declarationNameToString(name);
return grammarErrorOnNode(name, Diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode, nameText);
}
}
else if (nameBindings.kind === SyntaxKind.NamedImports) {
let reportError = false;
for (let element of (<NamedImports>nameBindings).elements) {
let name = element.name;
if (isReservedWordInStrictMode(name)) {
let nameText = declarationNameToString(name);
reportError = reportError || grammarErrorOnNode(name, Diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode, nameText);
}
}
return reportError;
}
}
}
return false;
}
function checkGrammarDeclarationNameInStrictMode(node: Declaration): boolean {
let name = node.name;
if (name && name.kind === SyntaxKind.Identifier && isReservedWordInStrictMode(<Identifier>name)) {
let nameText = declarationNameToString(name);
switch (node.kind) {
case SyntaxKind.Parameter:
case SyntaxKind.VariableDeclaration:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.TypeParameter:
case SyntaxKind.BindingElement:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.TypeAliasDeclaration:
case SyntaxKind.EnumDeclaration:
return checkGrammarIdentifierInStrictMode(<Identifier>name);
case SyntaxKind.ClassDeclaration:
// Report an error if the class declaration uses strict-mode reserved word.
return grammarErrorOnNode(name, Diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode_Class_definitions_are_automatically_in_strict_mode, nameText);
case SyntaxKind.ModuleDeclaration:
// Report an error if the module declaration uses strict-mode reserved word.
// TODO(yuisu): fix this when having external module in strict mode
return grammarErrorOnNode(name, Diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode, nameText);
case SyntaxKind.ImportEqualsDeclaration:
// TODO(yuisu): fix this when having external module in strict mode
return grammarErrorOnNode(name, Diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode, nameText);
}
}
return false;
}
function checkGrammarTypeReferenceInStrictMode(typeName: Identifier | QualifiedName) {
// Check if the type reference is using strict mode keyword
// Example:
// class C {
// foo(x: public){} // Error.
// }
if (typeName.kind === SyntaxKind.Identifier) {
checkGrammarTypeNameInStrictMode(<Identifier>typeName);
}
// Report an error for each identifier in QualifiedName
// Example:
// foo (x: B.private.bar) // error at private
// foo (x: public.private.package) // error at public, private, and package
else if (typeName.kind === SyntaxKind.QualifiedName) {
// Walk from right to left and report a possible error at each Identifier in QualifiedName
// Example:
// x1: public.private.package // error at public and private
checkGrammarTypeNameInStrictMode((<QualifiedName>typeName).right);
checkGrammarTypeReferenceInStrictMode((<QualifiedName>typeName).left);
}
}
// This function will report an error for every identifier in property access expression
// whether it violates strict mode reserved words.
// Example:
// public // error at public
// public.private.package // error at public
// B.private.B // no error
function checkGrammarExpressionWithTypeArgumentsInStrictMode(expression: Expression) {
// Example:
// class C extends public // error at public
if (expression && expression.kind === SyntaxKind.Identifier) {
return checkGrammarIdentifierInStrictMode(expression);
}
else if (expression && expression.kind === SyntaxKind.PropertyAccessExpression) {
// Walk from left to right in PropertyAccessExpression until we are at the left most expression
// in PropertyAccessExpression. According to grammar production of MemberExpression,
// the left component expression is a PrimaryExpression (i.e. Identifier) while the other
// component after dots can be IdentifierName.
checkGrammarExpressionWithTypeArgumentsInStrictMode((<PropertyAccessExpression>expression).expression);
}
}
// The function takes an identifier itself or an expression which has SyntaxKind.Identifier.
function checkGrammarIdentifierInStrictMode(node: Expression | Identifier, nameText?: string): boolean {
if (node && node.kind === SyntaxKind.Identifier && isReservedWordInStrictMode(<Identifier>node)) {
if (!nameText) {
nameText = declarationNameToString(<Identifier>node);
}
// TODO (yuisu): Fix when module is a strict mode
let errorReport = reportStrictModeGrammarErrorInClassDeclaration(<Identifier>node, Diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode_Class_definitions_are_automatically_in_strict_mode, nameText)||
grammarErrorOnNode(node, Diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode, nameText);
return errorReport;
}
return false;
}
// The function takes an identifier when uses as a typeName in TypeReferenceNode
function checkGrammarTypeNameInStrictMode(node: Identifier): boolean {
if (node && node.kind === SyntaxKind.Identifier && isReservedWordInStrictMode(<Identifier>node)) {
let nameText = declarationNameToString(<Identifier>node);
// TODO (yuisu): Fix when module is a strict mode
let errorReport = reportStrictModeGrammarErrorInClassDeclaration(<Identifier>node, Diagnostics.Type_expected_0_is_a_reserved_word_in_strict_mode_Class_definitions_are_automatically_in_strict_mode, nameText) ||
grammarErrorOnNode(node, Diagnostics.Type_expected_0_is_a_reserved_word_in_strict_mode, nameText);
return errorReport;
}
return false;
}
function checkGrammarDecorators(node: Node): boolean {
if (!node.decorators) {
return false;
}
if (!nodeCanBeDecorated(node)) {
return grammarErrorOnFirstToken(node, Diagnostics.Decorators_are_not_valid_here);
}
else if (languageVersion < ScriptTarget.ES5) {
return grammarErrorOnFirstToken(node, Diagnostics.Decorators_are_only_available_when_targeting_ECMAScript_5_and_higher);
}
else if (node.kind === SyntaxKind.GetAccessor || node.kind === SyntaxKind.SetAccessor) {
let accessors = getAllAccessorDeclarations((<ClassDeclaration>node.parent).members, <AccessorDeclaration>node);
if (accessors.firstAccessor.decorators && node === accessors.secondAccessor) {
return grammarErrorOnFirstToken(node, Diagnostics.Decorators_cannot_be_applied_to_multiple_get_Slashset_accessors_of_the_same_name);
}
}
return false;
}
function checkGrammarModifiers(node: Node): boolean {
switch (node.kind) {
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.Constructor:
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.PropertySignature:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
case SyntaxKind.IndexSignature:
case SyntaxKind.ModuleDeclaration:
case SyntaxKind.ImportDeclaration:
case SyntaxKind.ImportEqualsDeclaration:
case SyntaxKind.ExportDeclaration:
case SyntaxKind.ExportAssignment:
case SyntaxKind.Parameter:
break;
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.VariableStatement:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.TypeAliasDeclaration:
if (node.modifiers && node.parent.kind !== SyntaxKind.ModuleBlock && node.parent.kind !== SyntaxKind.SourceFile) {
return grammarErrorOnFirstToken(node, Diagnostics.Modifiers_cannot_appear_here);
}
break;
case SyntaxKind.EnumDeclaration:
if (node.modifiers && (node.modifiers.length > 1 || node.modifiers[0].kind !== SyntaxKind.ConstKeyword) &&
node.parent.kind !== SyntaxKind.ModuleBlock && node.parent.kind !== SyntaxKind.SourceFile) {
return grammarErrorOnFirstToken(node, Diagnostics.Modifiers_cannot_appear_here);
}
break;
default:
return false;
}
if (!node.modifiers) {
return;
}
let lastStatic: Node, lastPrivate: Node, lastProtected: Node, lastDeclare: Node;
let flags = 0;
for (let modifier of node.modifiers) {
switch (modifier.kind) {
case SyntaxKind.PublicKeyword:
case SyntaxKind.ProtectedKeyword:
case SyntaxKind.PrivateKeyword:
let text: string;
if (modifier.kind === SyntaxKind.PublicKeyword) {
text = "public";
}
else if (modifier.kind === SyntaxKind.ProtectedKeyword) {
text = "protected";
lastProtected = modifier;
}
else {
text = "private";
lastPrivate = modifier;
}
if (flags & NodeFlags.AccessibilityModifier) {
return grammarErrorOnNode(modifier, Diagnostics.Accessibility_modifier_already_seen);
}
else if (flags & NodeFlags.Static) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, text, "static");
}
else if (node.parent.kind === SyntaxKind.ModuleBlock || node.parent.kind === SyntaxKind.SourceFile) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_module_element, text);
}
flags |= modifierToFlag(modifier.kind);
break;
case SyntaxKind.StaticKeyword:
if (flags & NodeFlags.Static) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "static");
}
else if (node.parent.kind === SyntaxKind.ModuleBlock || node.parent.kind === SyntaxKind.SourceFile) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_module_element, "static");
}
else if (node.kind === SyntaxKind.Parameter) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_parameter, "static");
}
flags |= NodeFlags.Static;
lastStatic = modifier;
break;
case SyntaxKind.ExportKeyword:
if (flags & NodeFlags.Export) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "export");
}
else if (flags & NodeFlags.Ambient) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "export", "declare");
}
else if (node.parent.kind === SyntaxKind.ClassDeclaration) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_class_element, "export");
}
else if (node.kind === SyntaxKind.Parameter) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_parameter, "export");
}
flags |= NodeFlags.Export;
break;
case SyntaxKind.DeclareKeyword:
if (flags & NodeFlags.Ambient) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "declare");
}
else if (node.parent.kind === SyntaxKind.ClassDeclaration) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_class_element, "declare");
}
else if (node.kind === SyntaxKind.Parameter) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_parameter, "declare");
}
else if (isInAmbientContext(node.parent) && node.parent.kind === SyntaxKind.ModuleBlock) {
return grammarErrorOnNode(modifier, Diagnostics.A_declare_modifier_cannot_be_used_in_an_already_ambient_context);
}
flags |= NodeFlags.Ambient;
lastDeclare = modifier
break;
}
}
if (node.kind === SyntaxKind.Constructor) {
if (flags & NodeFlags.Static) {
return grammarErrorOnNode(lastStatic, Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration, "static");
}
else if (flags & NodeFlags.Protected) {
return grammarErrorOnNode(lastProtected, Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration, "protected");
}
else if (flags & NodeFlags.Private) {
return grammarErrorOnNode(lastPrivate, Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration, "private");
}
}
else if ((node.kind === SyntaxKind.ImportDeclaration || node.kind === SyntaxKind.ImportEqualsDeclaration) && flags & NodeFlags.Ambient) {
return grammarErrorOnNode(lastDeclare, Diagnostics.A_declare_modifier_cannot_be_used_with_an_import_declaration, "declare");
}
else if (node.kind === SyntaxKind.Parameter && (flags & NodeFlags.AccessibilityModifier) && isBindingPattern((<ParameterDeclaration>node).name)) {
return grammarErrorOnNode(node, Diagnostics.A_parameter_property_may_not_be_a_binding_pattern);
}
}
function checkGrammarForDisallowedTrailingComma(list: NodeArray<Node>): boolean {
if (list && list.hasTrailingComma) {
let start = list.end - ",".length;
let end = list.end;
let sourceFile = getSourceFileOfNode(list[0]);
return grammarErrorAtPos(sourceFile, start, end - start, Diagnostics.Trailing_comma_not_allowed);
}
}
function checkGrammarTypeParameterList(node: FunctionLikeDeclaration, typeParameters: NodeArray<TypeParameterDeclaration>, file: SourceFile): boolean {
if (checkGrammarForDisallowedTrailingComma(typeParameters)) {
return true;
}
if (typeParameters && typeParameters.length === 0) {
let start = typeParameters.pos - "<".length;
let end = skipTrivia(file.text, typeParameters.end) + ">".length;
return grammarErrorAtPos(file, start, end - start, Diagnostics.Type_parameter_list_cannot_be_empty);
}
}
function checkGrammarParameterList(parameters: NodeArray<ParameterDeclaration>) {
if (checkGrammarForDisallowedTrailingComma(parameters)) {
return true;
}
let seenOptionalParameter = false;
let parameterCount = parameters.length;
for (let i = 0; i < parameterCount; i++) {
let parameter = parameters[i];
if (parameter.dotDotDotToken) {
if (i !== (parameterCount - 1)) {
return grammarErrorOnNode(parameter.dotDotDotToken, Diagnostics.A_rest_parameter_must_be_last_in_a_parameter_list);
}
if (isBindingPattern(parameter.name)) {
return grammarErrorOnNode(parameter.name, Diagnostics.A_rest_element_cannot_contain_a_binding_pattern);
}
if (parameter.questionToken) {
return grammarErrorOnNode(parameter.questionToken, Diagnostics.A_rest_parameter_cannot_be_optional);
}
if (parameter.initializer) {
return grammarErrorOnNode(parameter.name, Diagnostics.A_rest_parameter_cannot_have_an_initializer);
}
}
else if (parameter.questionToken || parameter.initializer) {
seenOptionalParameter = true;
if (parameter.questionToken && parameter.initializer) {
return grammarErrorOnNode(parameter.name, Diagnostics.Parameter_cannot_have_question_mark_and_initializer);
}
}
else {
if (seenOptionalParameter) {
return grammarErrorOnNode(parameter.name, Diagnostics.A_required_parameter_cannot_follow_an_optional_parameter);
}
}
}
}
function checkGrammarFunctionLikeDeclaration(node: FunctionLikeDeclaration): boolean {
// Prevent cascading error by short-circuit
let file = getSourceFileOfNode(node);
return checkGrammarDecorators(node) || checkGrammarModifiers(node) || checkGrammarTypeParameterList(node, node.typeParameters, file) ||
checkGrammarParameterList(node.parameters) || checkGrammarArrowFunction(node, file);
}
function checkGrammarArrowFunction(node: FunctionLikeDeclaration, file: SourceFile): boolean {
if (node.kind === SyntaxKind.ArrowFunction) {
let arrowFunction = <ArrowFunction>node;
let startLine = getLineAndCharacterOfPosition(file, arrowFunction.equalsGreaterThanToken.pos).line;
let endLine = getLineAndCharacterOfPosition(file, arrowFunction.equalsGreaterThanToken.end).line;
if (startLine !== endLine) {
return grammarErrorOnNode(arrowFunction.equalsGreaterThanToken, Diagnostics.Line_terminator_not_permitted_before_arrow);
}
}
return false;
}
function checkGrammarIndexSignatureParameters(node: SignatureDeclaration): boolean {
let parameter = node.parameters[0];
if (node.parameters.length !== 1) {
if (parameter) {
return grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_must_have_exactly_one_parameter);
}
else {
return grammarErrorOnNode(node, Diagnostics.An_index_signature_must_have_exactly_one_parameter);
}
}
if (parameter.dotDotDotToken) {
return grammarErrorOnNode(parameter.dotDotDotToken, Diagnostics.An_index_signature_cannot_have_a_rest_parameter);
}
if (parameter.flags & NodeFlags.Modifier) {
return grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_parameter_cannot_have_an_accessibility_modifier);
}
if (parameter.questionToken) {
return grammarErrorOnNode(parameter.questionToken, Diagnostics.An_index_signature_parameter_cannot_have_a_question_mark);
}
if (parameter.initializer) {
return grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_parameter_cannot_have_an_initializer);
}
if (!parameter.type) {
return grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_parameter_must_have_a_type_annotation);
}
if (parameter.type.kind !== SyntaxKind.StringKeyword && parameter.type.kind !== SyntaxKind.NumberKeyword) {
return grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_parameter_type_must_be_string_or_number);
}
if (!node.type) {
return grammarErrorOnNode(node, Diagnostics.An_index_signature_must_have_a_type_annotation);
}
}
function checkGrammarForIndexSignatureModifier(node: SignatureDeclaration): void {
if (node.flags & NodeFlags.Modifier) {
grammarErrorOnFirstToken(node, Diagnostics.Modifiers_not_permitted_on_index_signature_members);
}
}
function checkGrammarIndexSignature(node: SignatureDeclaration) {
// Prevent cascading error by short-circuit
return checkGrammarDecorators(node) || checkGrammarModifiers(node) || checkGrammarIndexSignatureParameters(node) || checkGrammarForIndexSignatureModifier(node);
}
function checkGrammarForAtLeastOneTypeArgument(node: Node, typeArguments: NodeArray<TypeNode>): boolean {
if (typeArguments && typeArguments.length === 0) {
let sourceFile = getSourceFileOfNode(node);
let start = typeArguments.pos - "<".length;
let end = skipTrivia(sourceFile.text, typeArguments.end) + ">".length;
return grammarErrorAtPos(sourceFile, start, end - start, Diagnostics.Type_argument_list_cannot_be_empty);
}
}
function checkGrammarTypeArguments(node: Node, typeArguments: NodeArray<TypeNode>): boolean {
return checkGrammarForDisallowedTrailingComma(typeArguments) ||
checkGrammarForAtLeastOneTypeArgument(node, typeArguments);
}
function checkGrammarForOmittedArgument(node: CallExpression, arguments: NodeArray<Expression>): boolean {
if (arguments) {
let sourceFile = getSourceFileOfNode(node);
for (let arg of arguments) {
if (arg.kind === SyntaxKind.OmittedExpression) {
return grammarErrorAtPos(sourceFile, arg.pos, 0, Diagnostics.Argument_expression_expected);
}
}
}
}
function checkGrammarArguments(node: CallExpression, arguments: NodeArray<Expression>): boolean {
return checkGrammarForDisallowedTrailingComma(arguments) ||
checkGrammarForOmittedArgument(node, arguments);
}
function checkGrammarHeritageClause(node: HeritageClause): boolean {
let types = node.types;
if (checkGrammarForDisallowedTrailingComma(types)) {
return true;
}
if (types && types.length === 0) {
let listType = tokenToString(node.token);
let sourceFile = getSourceFileOfNode(node);
return grammarErrorAtPos(sourceFile, types.pos, 0, Diagnostics._0_list_cannot_be_empty, listType)
}
}
function checkGrammarClassDeclarationHeritageClauses(node: ClassDeclaration) {
let seenExtendsClause = false;
let seenImplementsClause = false;
if (!checkGrammarDecorators(node) && !checkGrammarModifiers(node) && node.heritageClauses) {
for (let heritageClause of node.heritageClauses) {
if (heritageClause.token === SyntaxKind.ExtendsKeyword) {
if (seenExtendsClause) {
return grammarErrorOnFirstToken(heritageClause, Diagnostics.extends_clause_already_seen)
}
if (seenImplementsClause) {
return grammarErrorOnFirstToken(heritageClause, Diagnostics.extends_clause_must_precede_implements_clause);
}
if (heritageClause.types.length > 1) {
return grammarErrorOnFirstToken(heritageClause.types[1], Diagnostics.Classes_can_only_extend_a_single_class);
}
seenExtendsClause = true;
}
else {
Debug.assert(heritageClause.token === SyntaxKind.ImplementsKeyword);
if (seenImplementsClause) {
return grammarErrorOnFirstToken(heritageClause, Diagnostics.implements_clause_already_seen);
}
seenImplementsClause = true;
}
// Grammar checking heritageClause inside class declaration
checkGrammarHeritageClause(heritageClause);
}
}
}
function checkGrammarInterfaceDeclaration(node: InterfaceDeclaration) {
let seenExtendsClause = false;
if (node.heritageClauses) {
for (let heritageClause of node.heritageClauses) {
if (heritageClause.token === SyntaxKind.ExtendsKeyword) {
if (seenExtendsClause) {
return grammarErrorOnFirstToken(heritageClause, Diagnostics.extends_clause_already_seen);
}
seenExtendsClause = true;
}
else {
Debug.assert(heritageClause.token === SyntaxKind.ImplementsKeyword);
return grammarErrorOnFirstToken(heritageClause, Diagnostics.Interface_declaration_cannot_have_implements_clause);
}
// Grammar checking heritageClause inside class declaration
checkGrammarHeritageClause(heritageClause);
}
}
return false;
}
function checkGrammarComputedPropertyName(node: Node): boolean {
// If node is not a computedPropertyName, just skip the grammar checking
if (node.kind !== SyntaxKind.ComputedPropertyName) {
return false;
}
let computedPropertyName = <ComputedPropertyName>node;
if (computedPropertyName.expression.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>computedPropertyName.expression).operatorToken.kind === SyntaxKind.CommaToken) {
return grammarErrorOnNode(computedPropertyName.expression, Diagnostics.A_comma_expression_is_not_allowed_in_a_computed_property_name);
}
}
function checkGrammarForGenerator(node: FunctionLikeDeclaration) {
if (node.asteriskToken) {
Debug.assert(
node.kind === SyntaxKind.FunctionDeclaration ||
node.kind === SyntaxKind.FunctionExpression ||
node.kind === SyntaxKind.MethodDeclaration);
if (isInAmbientContext(node)) {
return grammarErrorOnNode(node.asteriskToken, Diagnostics.Generators_are_not_allowed_in_an_ambient_context);
}
if (!node.body) {
return grammarErrorOnNode(node.asteriskToken, Diagnostics.An_overload_signature_cannot_be_declared_as_a_generator);
}
if (languageVersion < ScriptTarget.ES6) {
return grammarErrorOnNode(node.asteriskToken, Diagnostics.Generators_are_only_available_when_targeting_ECMAScript_6_or_higher);
}
}
}
function checkGrammarFunctionName(name: Node) {
// It is a SyntaxError if the identifier eval or arguments appears within a FormalParameterList of a strict mode FunctionDeclaration or FunctionExpression (13.1))
return checkGrammarEvalOrArgumentsInStrictMode(name, <Identifier>name);
}
function checkGrammarForInvalidQuestionMark(node: Declaration, questionToken: Node, message: DiagnosticMessage): boolean {
if (questionToken) {
return grammarErrorOnNode(questionToken, message);
}
}
function checkGrammarObjectLiteralExpression(node: ObjectLiteralExpression) {
let seen: Map<SymbolFlags> = {};
let Property = 1;
let GetAccessor = 2;
let SetAccesor = 4;
let GetOrSetAccessor = GetAccessor | SetAccesor;
let inStrictMode = (node.parserContextFlags & ParserContextFlags.StrictMode) !== 0;
for (let prop of node.properties) {
let name = prop.name;
if (prop.kind === SyntaxKind.OmittedExpression ||
name.kind === SyntaxKind.ComputedPropertyName) {
// If the name is not a ComputedPropertyName, the grammar checking will skip it
checkGrammarComputedPropertyName(<ComputedPropertyName>name);
continue;
}
// ECMA-262 11.1.5 Object Initialiser
// If previous is not undefined then throw a SyntaxError exception if any of the following conditions are true
// a.This production is contained in strict code and IsDataDescriptor(previous) is true and
// IsDataDescriptor(propId.descriptor) is true.
// b.IsDataDescriptor(previous) is true and IsAccessorDescriptor(propId.descriptor) is true.
// c.IsAccessorDescriptor(previous) is true and IsDataDescriptor(propId.descriptor) is true.
// d.IsAccessorDescriptor(previous) is true and IsAccessorDescriptor(propId.descriptor) is true
// and either both previous and propId.descriptor have[[Get]] fields or both previous and propId.descriptor have[[Set]] fields
let currentKind: number;
if (prop.kind === SyntaxKind.PropertyAssignment || prop.kind === SyntaxKind.ShorthandPropertyAssignment) {
// Grammar checking for computedPropertName and shorthandPropertyAssignment
checkGrammarForInvalidQuestionMark(prop,(<PropertyAssignment>prop).questionToken, Diagnostics.An_object_member_cannot_be_declared_optional);
if (name.kind === SyntaxKind.NumericLiteral) {
checkGrammarNumericLiteral(<Identifier>name);
}
currentKind = Property;
}
else if ( prop.kind === SyntaxKind.MethodDeclaration) {
currentKind = Property;
}
else if (prop.kind === SyntaxKind.GetAccessor) {
currentKind = GetAccessor;
}
else if (prop.kind === SyntaxKind.SetAccessor) {
currentKind = SetAccesor;
}
else {
Debug.fail("Unexpected syntax kind:" + prop.kind);
}
if (!hasProperty(seen, (<Identifier>name).text)) {
seen[(<Identifier>name).text] = currentKind;
}
else {
let existingKind = seen[(<Identifier>name).text];
if (currentKind === Property && existingKind === Property) {
if (inStrictMode) {
grammarErrorOnNode(name, Diagnostics.An_object_literal_cannot_have_multiple_properties_with_the_same_name_in_strict_mode);
}
}
else if ((currentKind & GetOrSetAccessor) && (existingKind & GetOrSetAccessor)) {
if (existingKind !== GetOrSetAccessor && currentKind !== existingKind) {
seen[(<Identifier>name).text] = currentKind | existingKind;
}
else {
return grammarErrorOnNode(name, Diagnostics.An_object_literal_cannot_have_multiple_get_Slashset_accessors_with_the_same_name);
}
}
else {
return grammarErrorOnNode(name, Diagnostics.An_object_literal_cannot_have_property_and_accessor_with_the_same_name);
}
}
}
}
function checkGrammarForInOrForOfStatement(forInOrOfStatement: ForInStatement | ForOfStatement): boolean {
if (checkGrammarStatementInAmbientContext(forInOrOfStatement)) {
return true;
}
if (forInOrOfStatement.initializer.kind === SyntaxKind.VariableDeclarationList) {
let variableList = <VariableDeclarationList>forInOrOfStatement.initializer;
if (!checkGrammarVariableDeclarationList(variableList)) {
if (variableList.declarations.length > 1) {
let diagnostic = forInOrOfStatement.kind === SyntaxKind.ForInStatement
? Diagnostics.Only_a_single_variable_declaration_is_allowed_in_a_for_in_statement
: Diagnostics.Only_a_single_variable_declaration_is_allowed_in_a_for_of_statement;
return grammarErrorOnFirstToken(variableList.declarations[1], diagnostic);
}
let firstDeclaration = variableList.declarations[0];
if (firstDeclaration.initializer) {
let diagnostic = forInOrOfStatement.kind === SyntaxKind.ForInStatement
? Diagnostics.The_variable_declaration_of_a_for_in_statement_cannot_have_an_initializer
: Diagnostics.The_variable_declaration_of_a_for_of_statement_cannot_have_an_initializer;
return grammarErrorOnNode(firstDeclaration.name, diagnostic);
}
if (firstDeclaration.type) {
let diagnostic = forInOrOfStatement.kind === SyntaxKind.ForInStatement
? Diagnostics.The_left_hand_side_of_a_for_in_statement_cannot_use_a_type_annotation
: Diagnostics.The_left_hand_side_of_a_for_of_statement_cannot_use_a_type_annotation;
return grammarErrorOnNode(firstDeclaration, diagnostic);
}
}
}
return false;
}
function checkGrammarAccessor(accessor: MethodDeclaration): boolean {
let kind = accessor.kind;
if (languageVersion < ScriptTarget.ES5) {
return grammarErrorOnNode(accessor.name, Diagnostics.Accessors_are_only_available_when_targeting_ECMAScript_5_and_higher);
}
else if (isInAmbientContext(accessor)) {
return grammarErrorOnNode(accessor.name, Diagnostics.An_accessor_cannot_be_declared_in_an_ambient_context);
}
else if (accessor.body === undefined) {
return grammarErrorAtPos(getSourceFileOfNode(accessor), accessor.end - 1, ";".length, Diagnostics._0_expected, "{");
}
else if (accessor.typeParameters) {
return grammarErrorOnNode(accessor.name, Diagnostics.An_accessor_cannot_have_type_parameters);
}
else if (kind === SyntaxKind.GetAccessor && accessor.parameters.length) {
return grammarErrorOnNode(accessor.name, Diagnostics.A_get_accessor_cannot_have_parameters);
}
else if (kind === SyntaxKind.SetAccessor) {
if (accessor.type) {
return grammarErrorOnNode(accessor.name, Diagnostics.A_set_accessor_cannot_have_a_return_type_annotation);
}
else if (accessor.parameters.length !== 1) {
return grammarErrorOnNode(accessor.name, Diagnostics.A_set_accessor_must_have_exactly_one_parameter);
}
else {
let parameter = accessor.parameters[0];
if (parameter.dotDotDotToken) {
return grammarErrorOnNode(parameter.dotDotDotToken, Diagnostics.A_set_accessor_cannot_have_rest_parameter);
}
else if (parameter.flags & NodeFlags.Modifier) {
return grammarErrorOnNode(accessor.name, Diagnostics.A_parameter_property_is_only_allowed_in_a_constructor_implementation);
}
else if (parameter.questionToken) {
return grammarErrorOnNode(parameter.questionToken, Diagnostics.A_set_accessor_cannot_have_an_optional_parameter);
}
else if (parameter.initializer) {
return grammarErrorOnNode(accessor.name, Diagnostics.A_set_accessor_parameter_cannot_have_an_initializer);
}
}
}
}
function checkGrammarForNonSymbolComputedProperty(node: DeclarationName, message: DiagnosticMessage) {
if (node.kind === SyntaxKind.ComputedPropertyName && !isWellKnownSymbolSyntactically((<ComputedPropertyName>node).expression)) {
return grammarErrorOnNode(node, message);
}
}
function checkGrammarMethod(node: MethodDeclaration) {
if (checkGrammarDisallowedModifiersInBlockOrObjectLiteralExpression(node) ||
checkGrammarFunctionLikeDeclaration(node) ||
checkGrammarForGenerator(node)) {
return true;
}
if (node.parent.kind === SyntaxKind.ObjectLiteralExpression) {
if (checkGrammarForInvalidQuestionMark(node, node.questionToken, Diagnostics.A_class_member_cannot_be_declared_optional)) {
return true;
}
else if (node.body === undefined) {
return grammarErrorAtPos(getSourceFile(node), node.end - 1, ";".length, Diagnostics._0_expected, "{");
}
}
if (node.parent.kind === SyntaxKind.ClassDeclaration) {
if (checkGrammarForInvalidQuestionMark(node, node.questionToken, Diagnostics.A_class_member_cannot_be_declared_optional)) {
return true;
}
// Technically, computed properties in ambient contexts is disallowed
// for property declarations and accessors too, not just methods.
// However, property declarations disallow computed names in general,
// and accessors are not allowed in ambient contexts in general,
// so this error only really matters for methods.
if (isInAmbientContext(node)) {
return checkGrammarForNonSymbolComputedProperty(node.name, Diagnostics.A_computed_property_name_in_an_ambient_context_must_directly_refer_to_a_built_in_symbol);
}
else if (!node.body) {
return checkGrammarForNonSymbolComputedProperty(node.name, Diagnostics.A_computed_property_name_in_a_method_overload_must_directly_refer_to_a_built_in_symbol);
}
}
else if (node.parent.kind === SyntaxKind.InterfaceDeclaration) {
return checkGrammarForNonSymbolComputedProperty(node.name, Diagnostics.A_computed_property_name_in_an_interface_must_directly_refer_to_a_built_in_symbol);
}
else if (node.parent.kind === SyntaxKind.TypeLiteral) {
return checkGrammarForNonSymbolComputedProperty(node.name, Diagnostics.A_computed_property_name_in_a_type_literal_must_directly_refer_to_a_built_in_symbol);
}
}
function isIterationStatement(node: Node, lookInLabeledStatements: boolean): boolean {
switch (node.kind) {
case SyntaxKind.ForStatement:
case SyntaxKind.ForInStatement:
case SyntaxKind.ForOfStatement:
case SyntaxKind.DoStatement:
case SyntaxKind.WhileStatement:
return true;
case SyntaxKind.LabeledStatement:
return lookInLabeledStatements && isIterationStatement((<LabeledStatement>node).statement, lookInLabeledStatements);
}
return false;
}
function checkGrammarBreakOrContinueStatement(node: BreakOrContinueStatement): boolean {
let current: Node = node;
while (current) {
if (isFunctionLike(current)) {
return grammarErrorOnNode(node, Diagnostics.Jump_target_cannot_cross_function_boundary);
}
switch (current.kind) {
case SyntaxKind.LabeledStatement:
if (node.label && (<LabeledStatement>current).label.text === node.label.text) {
// found matching label - verify that label usage is correct
// continue can only target labels that are on iteration statements
let isMisplacedContinueLabel = node.kind === SyntaxKind.ContinueStatement
&& !isIterationStatement((<LabeledStatement>current).statement, /*lookInLabeledStatement*/ true);
if (isMisplacedContinueLabel) {
return grammarErrorOnNode(node, Diagnostics.A_continue_statement_can_only_jump_to_a_label_of_an_enclosing_iteration_statement);
}
return false;
}
break;
case SyntaxKind.SwitchStatement:
if (node.kind === SyntaxKind.BreakStatement && !node.label) {
// unlabeled break within switch statement - ok
return false;
}
break;
default:
if (isIterationStatement(current, /*lookInLabeledStatement*/ false) && !node.label) {
// unlabeled break or continue within iteration statement - ok
return false;
}
break;
}
current = current.parent;
}
if (node.label) {
let message = node.kind === SyntaxKind.BreakStatement
? Diagnostics.A_break_statement_can_only_jump_to_a_label_of_an_enclosing_statement
: Diagnostics.A_continue_statement_can_only_jump_to_a_label_of_an_enclosing_iteration_statement;
return grammarErrorOnNode(node, message)
}
else {
let message = node.kind === SyntaxKind.BreakStatement
? Diagnostics.A_break_statement_can_only_be_used_within_an_enclosing_iteration_or_switch_statement
: Diagnostics.A_continue_statement_can_only_be_used_within_an_enclosing_iteration_statement;
return grammarErrorOnNode(node, message)
}
}
function checkGrammarBindingElement(node: BindingElement) {
if (node.dotDotDotToken) {
let elements = (<BindingPattern>node.parent).elements;
if (node !== lastOrUndefined(elements)) {
return grammarErrorOnNode(node, Diagnostics.A_rest_element_must_be_last_in_an_array_destructuring_pattern);
}
if (node.name.kind === SyntaxKind.ArrayBindingPattern || node.name.kind === SyntaxKind.ObjectBindingPattern) {
return grammarErrorOnNode(node.name, Diagnostics.A_rest_element_cannot_contain_a_binding_pattern);
}
if (node.initializer) {
// Error on equals token which immediate precedes the initializer
return grammarErrorAtPos(getSourceFileOfNode(node), node.initializer.pos - 1, 1, Diagnostics.A_rest_element_cannot_have_an_initializer);
}
}
// It is a SyntaxError if a VariableDeclaration or VariableDeclarationNoIn occurs within strict code
// and its Identifier is eval or arguments
return checkGrammarEvalOrArgumentsInStrictMode(node, <Identifier>node.name);
}
function checkGrammarVariableDeclaration(node: VariableDeclaration) {
if (node.parent.parent.kind !== SyntaxKind.ForInStatement && node.parent.parent.kind !== SyntaxKind.ForOfStatement) {
if (isInAmbientContext(node)) {
if (node.initializer) {
// Error on equals token which immediate precedes the initializer
let equalsTokenLength = "=".length;
return grammarErrorAtPos(getSourceFileOfNode(node), node.initializer.pos - equalsTokenLength,
equalsTokenLength, Diagnostics.Initializers_are_not_allowed_in_ambient_contexts);
}
}
else if (!node.initializer) {
if (isBindingPattern(node.name) && !isBindingPattern(node.parent)) {
return grammarErrorOnNode(node, Diagnostics.A_destructuring_declaration_must_have_an_initializer);
}
if (isConst(node)) {
return grammarErrorOnNode(node, Diagnostics.const_declarations_must_be_initialized);
}
}
}
let checkLetConstNames = languageVersion >= ScriptTarget.ES6 && (isLet(node) || isConst(node));
// 1. LexicalDeclaration : LetOrConst BindingList ;
// It is a Syntax Error if the BoundNames of BindingList contains "let".
// 2. ForDeclaration: ForDeclaration : LetOrConst ForBinding
// It is a Syntax Error if the BoundNames of ForDeclaration contains "let".
// It is a SyntaxError if a VariableDeclaration or VariableDeclarationNoIn occurs within strict code
// and its Identifier is eval or arguments
return (checkLetConstNames && checkGrammarNameInLetOrConstDeclarations(node.name)) ||
checkGrammarEvalOrArgumentsInStrictMode(node, <Identifier>node.name);
}
function checkGrammarNameInLetOrConstDeclarations(name: Identifier | BindingPattern): boolean {
if (name.kind === SyntaxKind.Identifier) {
if ((<Identifier>name).text === "let") {
return grammarErrorOnNode(name, Diagnostics.let_is_not_allowed_to_be_used_as_a_name_in_let_or_const_declarations);
}
}
else {
let elements = (<BindingPattern>name).elements;
for (let element of elements) {
if (element.kind !== SyntaxKind.OmittedExpression) {
checkGrammarNameInLetOrConstDeclarations(element.name);
}
}
}
}
function checkGrammarVariableDeclarationList(declarationList: VariableDeclarationList): boolean {
let declarations = declarationList.declarations;
if (checkGrammarForDisallowedTrailingComma(declarationList.declarations)) {
return true;
}
if (!declarationList.declarations.length) {
return grammarErrorAtPos(getSourceFileOfNode(declarationList), declarations.pos, declarations.end - declarations.pos, Diagnostics.Variable_declaration_list_cannot_be_empty);
}
}
function allowLetAndConstDeclarations(parent: Node): boolean {
switch (parent.kind) {
case SyntaxKind.IfStatement:
case SyntaxKind.DoStatement:
case SyntaxKind.WhileStatement:
case SyntaxKind.WithStatement:
case SyntaxKind.ForStatement:
case SyntaxKind.ForInStatement:
case SyntaxKind.ForOfStatement:
return false;
case SyntaxKind.LabeledStatement:
return allowLetAndConstDeclarations(parent.parent);
}
return true;
}
function checkGrammarForDisallowedLetOrConstStatement(node: VariableStatement) {
if (!allowLetAndConstDeclarations(node.parent)) {
if (isLet(node.declarationList)) {
return grammarErrorOnNode(node, Diagnostics.let_declarations_can_only_be_declared_inside_a_block);
}
else if (isConst(node.declarationList)) {
return grammarErrorOnNode(node, Diagnostics.const_declarations_can_only_be_declared_inside_a_block);
}
}
}
function isIntegerLiteral(expression: Expression): boolean {
if (expression.kind === SyntaxKind.PrefixUnaryExpression) {
let unaryExpression = <PrefixUnaryExpression>expression;
if (unaryExpression.operator === SyntaxKind.PlusToken || unaryExpression.operator === SyntaxKind.MinusToken) {
expression = unaryExpression.operand;
}
}
if (expression.kind === SyntaxKind.NumericLiteral) {
// Allows for scientific notation since literalExpression.text was formed by
// coercing a number to a string. Sometimes this coercion can yield a string
// in scientific notation.
// We also don't need special logic for hex because a hex integer is converted
// to decimal when it is coerced.
return /^[0-9]+([eE]\+?[0-9]+)?$/.test((<LiteralExpression>expression).text);
}
return false;
}
function checkGrammarEnumDeclaration(enumDecl: EnumDeclaration): boolean {
let enumIsConst = (enumDecl.flags & NodeFlags.Const) !== 0;
let hasError = false;
// skip checks below for const enums - they allow arbitrary initializers as long as they can be evaluated to constant expressions.
// since all values are known in compile time - it is not necessary to check that constant enum section precedes computed enum members.
if (!enumIsConst) {
let inConstantEnumMemberSection = true;
let inAmbientContext = isInAmbientContext(enumDecl);
for (let node of enumDecl.members) {
// Do not use hasDynamicName here, because that returns false for well known symbols.
// We want to perform checkComputedPropertyName for all computed properties, including
// well known symbols.
if (node.name.kind === SyntaxKind.ComputedPropertyName) {
hasError = grammarErrorOnNode(node.name, Diagnostics.Computed_property_names_are_not_allowed_in_enums);
}
else if (inAmbientContext) {
if (node.initializer && !isIntegerLiteral(node.initializer)) {
hasError = grammarErrorOnNode(node.name, Diagnostics.Ambient_enum_elements_can_only_have_integer_literal_initializers) || hasError;
}
}
else if (node.initializer) {
inConstantEnumMemberSection = isIntegerLiteral(node.initializer);
}
else if (!inConstantEnumMemberSection) {
hasError = grammarErrorOnNode(node.name, Diagnostics.Enum_member_must_have_initializer) || hasError;
}
}
}
return hasError;
}
function hasParseDiagnostics(sourceFile: SourceFile): boolean {
return sourceFile.parseDiagnostics.length > 0;
}
function grammarErrorOnFirstToken(node: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): boolean {
let sourceFile = getSourceFileOfNode(node);
if (!hasParseDiagnostics(sourceFile)) {
let span = getSpanOfTokenAtPosition(sourceFile, node.pos);
diagnostics.add(createFileDiagnostic(sourceFile, span.start, span.length, message, arg0, arg1, arg2));
return true;
}
}
function grammarErrorAtPos(sourceFile: SourceFile, start: number, length: number, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): boolean {
if (!hasParseDiagnostics(sourceFile)) {
diagnostics.add(createFileDiagnostic(sourceFile, start, length, message, arg0, arg1, arg2));
return true;
}
}
function grammarErrorOnNode(node: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): boolean {
let sourceFile = getSourceFileOfNode(node);
if (!hasParseDiagnostics(sourceFile)) {
diagnostics.add(createDiagnosticForNode(node, message, arg0, arg1, arg2));
return true;
}
}
function checkGrammarEvalOrArgumentsInStrictMode(contextNode: Node, name: Node): boolean {
if (name && name.kind === SyntaxKind.Identifier) {
let identifier = <Identifier>name;
if (contextNode && (contextNode.parserContextFlags & ParserContextFlags.StrictMode) && isEvalOrArgumentsIdentifier(identifier)) {
let nameText = declarationNameToString(identifier);
// We check first if the name is inside class declaration or class expression; if so give explicit message
// otherwise report generic error message.
// reportGrammarErrorInClassDeclaration only return true if grammar error is successfully reported and false otherwise
let reportErrorInClassDeclaration = reportStrictModeGrammarErrorInClassDeclaration(identifier, Diagnostics.Invalid_use_of_0_Class_definitions_are_automatically_in_strict_mode, nameText);
if (!reportErrorInClassDeclaration){
return grammarErrorOnNode(identifier, Diagnostics.Invalid_use_of_0_in_strict_mode, nameText);
}
return reportErrorInClassDeclaration;
}
}
}
function isEvalOrArgumentsIdentifier(node: Node): boolean {
return node.kind === SyntaxKind.Identifier &&
((<Identifier>node).text === "eval" || (<Identifier>node).text === "arguments");
}
function checkGrammarConstructorTypeParameters(node: ConstructorDeclaration) {
if (node.typeParameters) {
return grammarErrorAtPos(getSourceFileOfNode(node), node.typeParameters.pos, node.typeParameters.end - node.typeParameters.pos, Diagnostics.Type_parameters_cannot_appear_on_a_constructor_declaration);
}
}
function checkGrammarConstructorTypeAnnotation(node: ConstructorDeclaration) {
if (node.type) {
return grammarErrorOnNode(node.type, Diagnostics.Type_annotation_cannot_appear_on_a_constructor_declaration);
}
}
function checkGrammarProperty(node: PropertyDeclaration) {
if (node.parent.kind === SyntaxKind.ClassDeclaration) {
if (checkGrammarForInvalidQuestionMark(node, node.questionToken, Diagnostics.A_class_member_cannot_be_declared_optional) ||
checkGrammarForNonSymbolComputedProperty(node.name, Diagnostics.A_computed_property_name_in_a_class_property_declaration_must_directly_refer_to_a_built_in_symbol)) {
return true;
}
}
else if (node.parent.kind === SyntaxKind.InterfaceDeclaration) {
if (checkGrammarForNonSymbolComputedProperty(node.name, Diagnostics.A_computed_property_name_in_an_interface_must_directly_refer_to_a_built_in_symbol)) {
return true;
}
}
else if (node.parent.kind === SyntaxKind.TypeLiteral) {
if (checkGrammarForNonSymbolComputedProperty(node.name, Diagnostics.A_computed_property_name_in_a_type_literal_must_directly_refer_to_a_built_in_symbol)) {
return true;
}
}
if (isInAmbientContext(node) && node.initializer) {
return grammarErrorOnFirstToken(node.initializer, Diagnostics.Initializers_are_not_allowed_in_ambient_contexts);
}
}
function checkGrammarTopLevelElementForRequiredDeclareModifier(node: Node): boolean {
// A declare modifier is required for any top level .d.ts declaration except export=, export default,
// interfaces and imports categories:
//
// DeclarationElement:
// ExportAssignment
// export_opt InterfaceDeclaration
// export_opt ImportDeclaration
// export_opt ExternalImportDeclaration
// export_opt AmbientDeclaration
//
if (node.kind === SyntaxKind.InterfaceDeclaration ||
node.kind === SyntaxKind.ImportDeclaration ||
node.kind === SyntaxKind.ImportEqualsDeclaration ||
node.kind === SyntaxKind.ExportDeclaration ||
node.kind === SyntaxKind.ExportAssignment ||
(node.flags & NodeFlags.Ambient) ||
(node.flags & (NodeFlags.Export | NodeFlags.Default))) {
return false;
}
return grammarErrorOnFirstToken(node, Diagnostics.A_declare_modifier_is_required_for_a_top_level_declaration_in_a_d_ts_file);
}
function checkGrammarTopLevelElementsForRequiredDeclareModifier(file: SourceFile): boolean {
for (let decl of file.statements) {
if (isDeclaration(decl) || decl.kind === SyntaxKind.VariableStatement) {
if (checkGrammarTopLevelElementForRequiredDeclareModifier(decl)) {
return true;
}
}
}
}
function checkGrammarSourceFile(node: SourceFile): boolean {
return isInAmbientContext(node) && checkGrammarTopLevelElementsForRequiredDeclareModifier(node);
}
function checkGrammarStatementInAmbientContext(node: Node): boolean {
if (isInAmbientContext(node)) {
// An accessors is already reported about the ambient context
if (isAccessor(node.parent.kind)) {
return getNodeLinks(node).hasReportedStatementInAmbientContext = true;
}
// Find containing block which is either Block, ModuleBlock, SourceFile
let links = getNodeLinks(node);
if (!links.hasReportedStatementInAmbientContext && isFunctionLike(node.parent)) {
return getNodeLinks(node).hasReportedStatementInAmbientContext = grammarErrorOnFirstToken(node, Diagnostics.An_implementation_cannot_be_declared_in_ambient_contexts)
}
// We are either parented by another statement, or some sort of block.
// If we're in a block, we only want to really report an error once
// to prevent noisyness. So use a bit on the block to indicate if
// this has already been reported, and don't report if it has.
//
if (node.parent.kind === SyntaxKind.Block || node.parent.kind === SyntaxKind.ModuleBlock || node.parent.kind === SyntaxKind.SourceFile) {
let links = getNodeLinks(node.parent);
// Check if the containing block ever report this error
if (!links.hasReportedStatementInAmbientContext) {
return links.hasReportedStatementInAmbientContext = grammarErrorOnFirstToken(node, Diagnostics.Statements_are_not_allowed_in_ambient_contexts);
}
}
else {
// We must be parented by a statement. If so, there's no need
// to report the error as our parent will have already done it.
// Debug.assert(isStatement(node.parent));
}
}
}
function checkGrammarNumericLiteral(node: Identifier): boolean {
// Grammar checking
if (node.flags & NodeFlags.OctalLiteral) {
if (node.parserContextFlags & ParserContextFlags.StrictMode) {
return grammarErrorOnNode(node, Diagnostics.Octal_literals_are_not_allowed_in_strict_mode);
}
else if (languageVersion >= ScriptTarget.ES5) {
return grammarErrorOnNode(node, Diagnostics.Octal_literals_are_not_available_when_targeting_ECMAScript_5_and_higher);
}
}
}
function grammarErrorAfterFirstToken(node: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): boolean {
let sourceFile = getSourceFileOfNode(node);
if (!hasParseDiagnostics(sourceFile)) {
let span = getSpanOfTokenAtPosition(sourceFile, node.pos);
diagnostics.add(createFileDiagnostic(sourceFile, textSpanEnd(span), /*length*/ 0, message, arg0, arg1, arg2));
return true;
}
}
initializeTypeChecker();
return checker;
}
}