TypeScript/src/compiler/checker.ts
2014-07-12 17:30:19 -07:00

5798 lines
300 KiB
TypeScript
Raw Blame History

/// <reference path="types.ts"/>
/// <reference path="core.ts"/>
/// <reference path="scanner.ts"/>
/// <reference path="parser.ts"/>
/// <reference path="binder.ts"/>
/// <reference path="emitter.ts"/>
module ts {
var nextSymbolId = 1;
var nextNodeId = 1;
var nextMergeId = 1;
export function createTypeChecker(program: Program): TypeChecker {
var Symbol = objectAllocator.getSymbolConstructor();
var Type = objectAllocator.getTypeConstructor();
var Signature = objectAllocator.getSignatureConstructor();
var typeCount = 0;
var emptyArray: any[] = [];
var emptySymbols: SymbolTable = {};
var undefinedSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "undefined");
var argumentsSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "arguments");
var unknownSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "unknown");
var resolvingSymbol = createSymbol(SymbolFlags.Transient, "__resolving__");
var anyType = createIntrinsicType(TypeFlags.Any, "any");
var stringType = createIntrinsicType(TypeFlags.String, "string");
var numberType = createIntrinsicType(TypeFlags.Number, "number");
var booleanType = createIntrinsicType(TypeFlags.Boolean, "boolean");
var voidType = createIntrinsicType(TypeFlags.Void, "void");
var undefinedType = createIntrinsicType(TypeFlags.Undefined, "undefined");
var nullType = createIntrinsicType(TypeFlags.Null, "null");
var unknownType = createIntrinsicType(TypeFlags.Any, "unknown");
var resolvingType = createIntrinsicType(TypeFlags.Any, "__resolving__");
var emptyObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
var anyFunctionType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
var noConstraintType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
var globals: SymbolTable = {};
var globalObjectType: ObjectType;
var globalFunctionType: ObjectType;
var globalArrayType: ObjectType;
var globalStringType: ObjectType;
var globalNumberType: ObjectType;
var globalBooleanType: ObjectType;
var globalRegExpType: ObjectType;
var stringLiteralTypes: Map<StringLiteralType> = {};
var emitExtends = false;
var modulesVerified = false;
var mergedSymbols: Symbol[] = [];
var symbolLinks: SymbolLinks[] = [];
var nodeLinks: NodeLinks[] = [];
var diagnostics: Diagnostic[] = [];
var diagnosticsModified: boolean = false;
var checker: TypeChecker;
function addDiagnostic(diagnostic: Diagnostic) {
diagnostics.push(diagnostic);
diagnosticsModified = true;
}
function error(location: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): void {
var diagnostic = location
? createDiagnosticForNode(location, message, arg0, arg1, arg2)
: createCompilerDiagnostic(message, arg0, arg1, arg2);
addDiagnostic(diagnostic);
}
function createSymbol(flags: SymbolFlags, name: string): Symbol {
return new Symbol(flags, name);
}
function getExcludedSymbolFlags(flags: SymbolFlags): SymbolFlags {
var result: SymbolFlags = 0;
if (flags & SymbolFlags.Variable) result |= SymbolFlags.VariableExcludes;
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.Enum) result |= SymbolFlags.EnumExcludes;
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.Import) result |= SymbolFlags.ImportExcludes;
return result;
}
function recordMergedSymbol(target: Symbol, source: Symbol) {
if (!source.mergeId) source.mergeId = nextMergeId++;
mergedSymbols[source.mergeId] = target;
}
function cloneSymbol(symbol: Symbol): Symbol {
var result = createSymbol(symbol.flags | SymbolFlags.Merged, symbol.name);
result.declarations = symbol.declarations.slice(0);
result.parent = symbol.parent;
if (symbol.valueDeclaration) result.valueDeclaration = symbol.valueDeclaration;
if (symbol.members) result.members = cloneSymbolTable(symbol.members);
if (symbol.exports) result.exports = cloneSymbolTable(symbol.exports);
recordMergedSymbol(result, symbol);
return result;
}
function extendSymbol(target: Symbol, source: Symbol) {
if (!(target.flags & getExcludedSymbolFlags(source.flags))) {
target.flags |= source.flags;
if (!target.valueDeclaration && source.valueDeclaration) target.valueDeclaration = source.valueDeclaration;
forEach(source.declarations, node => {
target.declarations.push(node);
});
if (source.members) {
if (!target.members) target.members = {};
extendSymbolTable(target.members, source.members);
}
if (source.exports) {
if (!target.exports) target.exports = {};
extendSymbolTable(target.exports, source.exports);
}
recordMergedSymbol(target, source);
}
else {
forEach(source.declarations, node => {
error(node.name ? node.name : node, Diagnostics.Duplicate_identifier_0, symbolToString(source));
});
}
}
function cloneSymbolTable(symbolTable: SymbolTable): SymbolTable {
var result: SymbolTable = {};
for (var id in symbolTable) {
if (hasProperty(symbolTable, id)) {
result[id] = symbolTable[id];
}
}
return result;
}
function extendSymbolTable(target: SymbolTable, source: SymbolTable) {
for (var id in source) {
if (hasProperty(source, id)) {
if (!hasProperty(target, id)) {
target[id] = source[id];
}
else {
var symbol = target[id];
if (!(symbol.flags & SymbolFlags.Merged)) {
target[id] = symbol = cloneSymbol(symbol);
}
extendSymbol(symbol, source[id]);
}
}
}
}
function getSymbolLinks(symbol: Symbol): SymbolLinks {
if (symbol.flags & SymbolFlags.Transient) return <TransientSymbol>symbol;
if (!symbol.id) symbol.id = nextSymbolId++;
return symbolLinks[symbol.id] || (symbolLinks[symbol.id] = {});
}
function getNodeLinks(node: Node): NodeLinks {
if (!node.id) node.id = nextNodeId++;
return nodeLinks[node.id] || (nodeLinks[node.id] = {});
}
function getSourceFile(node: Node): SourceFile {
return <SourceFile>getAncestor(node, SyntaxKind.SourceFile);
}
function getSymbol(symbols: SymbolTable, name: string, meaning: SymbolFlags): Symbol {
if (meaning && hasProperty(symbols, name)) {
var symbol = symbols[name];
Debug.assert((symbol.flags & SymbolFlags.Instantiated) === 0, "Should never get an instantiated symbol here.");
if (symbol.flags & meaning) {
return symbol;
}
if (symbol.flags & SymbolFlags.Import) {
var target = resolveImport(symbol);
// unknown symbol will mean that there were reported error during import resolution
// treat it as positive answer to avoid cascading errors
if (target === unknownSymbol || target.flags & meaning) {
return symbol;
}
}
}
// return undefined if we can't find a symbol.
}
function resolveName(location: Node, name: string, meaning: SymbolFlags, nameNotFoundMessage: DiagnosticMessage, nameArg: string): Symbol {
var errorLocation = location;
var result: Symbol;
var lastLocation: Node;
var memberWithInitializerThatReferencesIdentifierFromConstructor: Node;
function returnResolvedSymbol(s: Symbol) {
// we've seen member with initializer that references identifier defined in constructor during the search.
// if this was the only result with given name then just report default 'nameNotFound' message.
// however if we met something else that was 'shadowed' by the identifier in constructor - report more specific error
if (s && memberWithInitializerThatReferencesIdentifierFromConstructor) {
var propertyName = (<PropertyDeclaration>memberWithInitializerThatReferencesIdentifierFromConstructor).name;
error(errorLocation, Diagnostics.Initializer_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor, identifierToString(propertyName), nameArg);
return undefined;
}
if (!s && nameNotFoundMessage) {
error(errorLocation, nameNotFoundMessage, nameArg);
}
return s;
}
while (location) {
// Locals of a source file are not in scope (because they get merged into the global symbol table)
if (location.locals && (location.kind !== SyntaxKind.SourceFile || location.flags & NodeFlags.ExternalModule)) {
if (result = getSymbol(location.locals, name, meaning)) {
return returnResolvedSymbol(result);
}
}
switch (location.kind) {
case SyntaxKind.SourceFile:
if (!(location.flags & NodeFlags.ExternalModule)) break;
case SyntaxKind.ModuleDeclaration:
if (result = getSymbol(getSymbolOfNode(location).exports, name, meaning & SymbolFlags.ModuleMember)) {
return returnResolvedSymbol(result);
}
break;
case SyntaxKind.EnumDeclaration:
if (result = getSymbol(getSymbolOfNode(location).exports, name, meaning & SymbolFlags.EnumMember)) {
return returnResolvedSymbol(result);
}
break;
case SyntaxKind.Property:
// TypeScript 1.0 spec (April 2014): 8.4.1
// Initializer expressions for instance member variables are evaluated in the scope
// of the class constructor body but are not permitted to reference parameters or
// local variables of the constructor.This effectively means that entities from outer scopes
// by the same name as a constructor parameter or local variable are inaccessible
// in initializer expressions for instance member variables.
if (location.parent.kind === SyntaxKind.ClassDeclaration && !(location.flags & NodeFlags.Static)) {
var ctor = findConstructorDeclaration(<ClassDeclaration>location.parent);
if (ctor && ctor.locals) {
if (getSymbol(ctor.locals, name, meaning & SymbolFlags.Value)) {
// save the property node - later it will be used by 'returnResolvedSymbol' to report appropriate error
memberWithInitializerThatReferencesIdentifierFromConstructor = 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;
}
else {
return returnResolvedSymbol(result);
}
}
break;
case SyntaxKind.Method:
case SyntaxKind.Constructor:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.ArrowFunction:
if (name === "arguments") {
return returnResolvedSymbol(argumentsSymbol);
}
break;
case SyntaxKind.FunctionExpression:
if (name === "arguments") {
return returnResolvedSymbol(argumentsSymbol);
}
var id = (<FunctionExpression>location).name;
if (id && name === id.text) {
return returnResolvedSymbol(location.symbol);
}
break;
case SyntaxKind.CatchBlock:
var id = (<CatchBlock>location).variable;
if (name === id.text) {
return returnResolvedSymbol((<CatchBlock>location).variable.symbol);
}
break;
}
lastLocation = location;
location = location.parent;
}
if (result = getSymbol(globals, name, meaning)) {
return returnResolvedSymbol(result);
}
return returnResolvedSymbol(undefined);
}
function resolveImport(symbol: Symbol): Symbol {
Debug.assert((symbol.flags & SymbolFlags.Import) !== 0, "Should only get Imports here.");
var links = getSymbolLinks(symbol);
if (!links.target) {
links.target = resolvingSymbol;
var node = <ImportDeclaration>getDeclarationOfKind(symbol, SyntaxKind.ImportDeclaration);
var target = node.externalModuleName ?
resolveExternalModuleName(node, node.externalModuleName) :
resolveEntityName(node, node.entityName, node.entityName.kind === SyntaxKind.QualifiedName ?
SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace : SymbolFlags.Namespace);
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 getFullyQualifiedName(symbol: Symbol) {
return symbol.parent ? getFullyQualifiedName(symbol.parent) + "." + symbolToString(symbol) : symbolToString(symbol);
}
// Resolves a qualified name and any involved import aliases
function resolveEntityName(location: Node, name: EntityName, meaning: SymbolFlags): Symbol {
if (name.kind === SyntaxKind.Identifier) {
// TODO: Investigate error recovery for symbols not found
var symbol = resolveName(location, (<Identifier>name).text, meaning, Diagnostics.Cannot_find_name_0, identifierToString(<Identifier>name));
if (!symbol) {
return;
}
}
else if (name.kind === SyntaxKind.QualifiedName) {
var namespace = resolveEntityName(location, (<QualifiedName>name).left, SymbolFlags.Namespace);
if (!namespace || namespace === unknownSymbol || (<QualifiedName>name).right.kind === SyntaxKind.Missing) return;
var symbol = getSymbol(namespace.exports, (<QualifiedName>name).right.text, meaning);
if (!symbol) {
error(location, Diagnostics.Module_0_has_no_exported_member_1, getFullyQualifiedName(namespace),
identifierToString((<QualifiedName>name).right));
return;
}
}
else {
// Missing identifier
return;
}
Debug.assert((symbol.flags & SymbolFlags.Instantiated) === 0, "Should never get an instantiated symbol here.");
return symbol.flags & meaning ? symbol : resolveImport(symbol);
}
function isExternalModuleNameRelative(moduleName: string): boolean {
// TypeScript 1.0 spec (April 2014): 11.2.1
// An external module name is "relative" if the first term is "." or "..".
return moduleName.substr(0, 2) === "./" || moduleName.substr(0, 3) === "../" || moduleName.substr(0, 2) === ".\\" || moduleName.substr(0, 3) === "..\\";
}
function resolveExternalModuleName(location: Node, moduleLiteral: LiteralExpression): Symbol {
var searchPath = getDirectoryPath(getSourceFile(location).filename);
var moduleName = moduleLiteral.text;
if (!moduleName) return;
var isRelative = isExternalModuleNameRelative(moduleName);
if (!isRelative) {
var symbol = getSymbol(globals, '"' + moduleName + '"', SymbolFlags.ValueModule);
if (symbol) {
return getResolvedExportSymbol(symbol);
}
}
while (true) {
var filename = normalizePath(combinePaths(searchPath, moduleName));
var sourceFile = program.getSourceFile(filename + ".ts") || program.getSourceFile(filename + ".d.ts");
if (sourceFile || isRelative) break;
var parentPath = getDirectoryPath(searchPath);
if (parentPath === searchPath) break;
searchPath = parentPath;
}
if (sourceFile) {
if (sourceFile.symbol) {
return getResolvedExportSymbol(sourceFile.symbol);
}
error(moduleLiteral, Diagnostics.File_0_is_not_an_external_module, sourceFile.filename);
return;
}
error(moduleLiteral, Diagnostics.Cannot_find_external_module_0, moduleName);
}
function getResolvedExportSymbol(moduleSymbol: Symbol): Symbol {
var symbol = getExportAssignmentSymbol(moduleSymbol);
if (symbol) {
if (symbol.flags & (SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace)) {
return symbol;
}
if (symbol.flags & SymbolFlags.Import) {
return resolveImport(symbol);
}
}
return moduleSymbol;
}
function getExportAssignmentSymbol(symbol: Symbol): Symbol {
if (!symbol.exportAssignSymbol) {
var exportInformation = collectExportInformationForSourceFileOrModule(symbol);
if (exportInformation.exportAssignments.length) {
if (exportInformation.exportAssignments.length > 1) {
// TypeScript 1.0 spec (April 2014): 11.2.4
// It is an error for an external module to contain more than one export assignment.
forEach(exportInformation.exportAssignments, node => error(node, Diagnostics.A_module_cannot_have_more_than_one_export_assignment));
}
var node = exportInformation.exportAssignments[0];
if (exportInformation.hasExportedMember) {
// TypeScript 1.0 spec (April 2014): 11.2.3
// If an external module contains an export assignment it is an error
// for the external module to also contain export declarations.
// The two types of exports are mutually exclusive.
error(node, Diagnostics.An_export_assignment_cannot_be_used_in_a_module_with_other_exported_elements);
}
if (node.exportName.text) {
var meaning = SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace;
var exportSymbol = resolveName(node, node.exportName.text, meaning, Diagnostics.Cannot_find_name_0, identifierToString(node.exportName));
}
}
symbol.exportAssignSymbol = exportSymbol || unknownSymbol;
}
return symbol.exportAssignSymbol === unknownSymbol ? undefined : symbol.exportAssignSymbol;
}
function collectExportInformationForSourceFileOrModule(symbol: Symbol) {
var seenExportedMember = false;
var result: ExportAssignment[] = [];
forEach(symbol.declarations, declaration => {
var block = <Block>(declaration.kind === SyntaxKind.SourceFile ? declaration : (<ModuleDeclaration>declaration).body);
forEach(block.statements, node => {
if (node.kind === SyntaxKind.ExportAssignment) {
result.push(<ExportAssignment>node);
}
else {
seenExportedMember = seenExportedMember || (node.flags & NodeFlags.Export) !== 0;
}
});
});
return {
hasExportedMember: seenExportedMember,
exportAssignments: result
};
}
function getMergedSymbol(symbol: Symbol): Symbol {
var merged: Symbol;
return symbol && symbol.mergeId && (merged = mergedSymbols[symbol.mergeId]) ? merged : symbol;
}
function getSymbolOfNode(node: Node): Symbol {
return getMergedSymbol(node.symbol);
}
function getParentOfSymbol(symbol: Symbol): Symbol {
return getMergedSymbol(symbol.parent);
}
function getExportSymbolOfValueSymbolIfExported(symbol: Symbol): Symbol {
return symbol && (symbol.flags & SymbolFlags.ExportValue) !== 0
? getMergedSymbol(symbol.exportSymbol)
: symbol;
}
function symbolIsValue(symbol: Symbol): boolean {
// If the symbol has the value flag, it is trivially a value.
if (symbol.flags & SymbolFlags.Value) {
return true;
}
// If it is an import, then it is a value if the symbol it resolves to is a value.
if (symbol.flags & SymbolFlags.Import) {
return (resolveImport(symbol).flags & SymbolFlags.Value) !== 0;
}
// If it is an instantiated symbol, then it is a value if hte symbol it is an
// instantiation of is a value.
if (symbol.flags & SymbolFlags.Instantiated) {
return (getSymbolLinks(symbol).target.flags & SymbolFlags.Value) !== 0;
}
return false;
}
function getDeclarationOfKind(symbol: Symbol, kind: SyntaxKind): Declaration {
var declarations = symbol.declarations;
for (var i = 0; i < declarations.length; i++) {
var declaration = declarations[i];
if (declaration.kind === kind) return declaration;
}
}
function findConstructorDeclaration(node: ClassDeclaration): ConstructorDeclaration {
var members = node.members;
for (var i = 0; i < members.length; i++) {
var member = members[i];
if (member.kind === SyntaxKind.Constructor && (<ConstructorDeclaration>member).body) {
return <ConstructorDeclaration>member;
}
}
}
function createType(flags: TypeFlags): Type {
var result = new Type(checker, flags);
result.id = typeCount++;
return result;
}
function createIntrinsicType(kind: TypeFlags, intrinsicName: string): IntrinsicType {
var type = <IntrinsicType>createType(kind);
type.intrinsicName = intrinsicName;
return type;
}
function createObjectType(kind: TypeFlags, symbol?: Symbol): ObjectType {
var type = <ObjectType>createType(kind);
type.symbol = symbol;
return type;
}
// A reserved member name starts with two underscores followed by a non-underscore
function isReservedMemberName(name: string) {
return name.charCodeAt(0) === CharacterCodes._ && name.charCodeAt(1) === CharacterCodes._ && name.charCodeAt(2) !== CharacterCodes._;
}
function getNamedMembers(members: SymbolTable): Symbol[] {
var result: Symbol[];
for (var id in members) {
if (hasProperty(members, id)) {
if (!isReservedMemberName(id)) {
if (!result) result = [];
var symbol = members[id];
if (symbolIsValue(symbol)) {
result.push(symbol);
}
}
}
}
return result || emptyArray;
}
function setObjectTypeMembers(type: ObjectType, members: SymbolTable, callSignatures: Signature[], constructSignatures: Signature[], stringIndexType: Type, numberIndexType: Type): ResolvedObjectType {
(<ResolvedObjectType>type).members = members;
(<ResolvedObjectType>type).properties = getNamedMembers(members);
(<ResolvedObjectType>type).callSignatures = callSignatures;
(<ResolvedObjectType>type).constructSignatures = constructSignatures;
if (stringIndexType) (<ResolvedObjectType>type).stringIndexType = stringIndexType;
if (numberIndexType) (<ResolvedObjectType>type).numberIndexType = numberIndexType;
return <ResolvedObjectType>type;
}
function createAnonymousType(symbol: Symbol, members: SymbolTable, callSignatures: Signature[], constructSignatures: Signature[], stringIndexType: Type, numberIndexType: Type): ResolvedObjectType {
return setObjectTypeMembers(createObjectType(TypeFlags.Anonymous, symbol),
members, callSignatures, constructSignatures, stringIndexType, numberIndexType);
}
// Takes a VariableDeclaration because it could be an exported var from a module (VariableDeclaration),
// a class or object type property (PropertyDeclaration), or a parameter property (ParameterDeclaration)
function isOptionalProperty(propertySymbol: Symbol): boolean {
if (propertySymbol.flags & SymbolFlags.Prototype) {
return false;
}
// class C {
// constructor(public x?) { }
// }
//
// x is an optional parameter, but it is a required property.
return (propertySymbol.valueDeclaration.flags & NodeFlags.QuestionMark) && propertySymbol.valueDeclaration.kind !== SyntaxKind.Parameter;
}
function symbolToString(symbol: Symbol) {
if (symbol.declarations && symbol.declarations.length > 0) {
var declaration = symbol.declarations[0];
if (declaration.name) {
return identifierToString(declaration.name);
}
}
return symbol.name;
}
function typeToString(type: Type, printArrayAsGenericType: boolean): string {
var typeStack: Type[];
return typeToString(type);
function typeToString(type: Type): string {
if (type.flags & TypeFlags.Intrinsic) {
return (<IntrinsicType>type).intrinsicName;
}
if (type.flags & TypeFlags.Reference) {
return typeReferenceToString(<TypeReference>type);
}
if (type.flags & (TypeFlags.Class | TypeFlags.Interface | TypeFlags.Enum | TypeFlags.TypeParameter)) {
return symbolToString(type.symbol);
}
if (type.flags & TypeFlags.Anonymous) {
return anonymousTypeToString(<ObjectType>type);
}
if (type.flags & TypeFlags.StringLiteral) {
return (<StringLiteralType>type).text;
}
// Should never get here
return "{ ... }";
}
function typeReferenceToString(type: TypeReference): string {
if (type.target === globalArrayType && !printArrayAsGenericType) {
return typeToString(type.typeArguments[0]) + "[]";
}
var result = symbolToString(type.target.symbol);
result += "<";
for (var i = 0; i < type.typeArguments.length; i++) {
if (i > 0) result += ", ";
result += typeToString(type.typeArguments[i]);
}
result += ">";
return result;
}
function anonymousTypeToString(type: ObjectType): string {
// Always use 'typeof T' for type of class, enum, and module objects
if (type.symbol && type.symbol.flags & (SymbolFlags.Class | SymbolFlags.Enum | SymbolFlags.ValueModule)) {
return symbolTypeToString(type);
}
// Use 'typeof T' for types of functions and methods that circularly reference themselves
if (type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method)) {
if (typeStack && contains(typeStack, type)) {
return symbolTypeToString(type);
}
}
if (!typeStack) typeStack = [];
typeStack.push(type);
var result = literalTypeToString(type);
typeStack.pop();
return result;
}
function symbolTypeToString(type: ObjectType): string {
return "typeof " + symbolToString(type.symbol);
}
function literalTypeToString(type: ObjectType): string {
var resolved = resolveObjectTypeMembers(type);
if (!resolved.properties.length && !resolved.stringIndexType && !resolved.numberIndexType) {
if (!resolved.callSignatures.length && !resolved.constructSignatures.length) {
return "{}";
}
if (resolved.callSignatures.length === 1 && !resolved.constructSignatures.length) {
return signatureToString(resolved.callSignatures[0], /*arrowStyle*/ true);
}
if (resolved.constructSignatures.length === 1 && !resolved.callSignatures.length) {
return "new " + signatureToString(resolved.constructSignatures[0], /*arrowStyle*/ true);
}
}
var result = "{ ";
for (var i = 0; i < resolved.callSignatures.length; i++) {
result += signatureToString(resolved.callSignatures[i]);
result += "; ";
}
for (var i = 0; i < resolved.constructSignatures.length; i++) {
result += "new ";
result += signatureToString(resolved.constructSignatures[i]);
result += "; ";
}
if (resolved.stringIndexType) {
result += "[x: string]: ";
result += typeToString(resolved.stringIndexType);
result += "; ";
}
if (resolved.numberIndexType) {
result += "[x: number]: ";
result += typeToString(resolved.numberIndexType);
result += "; ";
}
for (var i = 0; i < resolved.properties.length; i++) {
var p = resolved.properties[i];
var t = getTypeOfSymbol(p);
if (p.flags & (SymbolFlags.Function | SymbolFlags.Method) && !getPropertiesOfType(t).length) {
var signatures = getSignaturesOfType(t, SignatureKind.Call);
for (var j = 0; j < signatures.length; j++) {
result += symbolToString(p);
if (isOptionalProperty(p)) {
result += "?";
}
result += signatureToString(signatures[j]);
result += "; ";
}
}
else {
result += symbolToString(p);
if (isOptionalProperty(p)) {
result += "?";
}
result += ": ";
result += typeToString(t);
result += "; ";
}
}
result += "}";
return result;
}
function signatureToString(signature: Signature, arrowStyle?: boolean): string {
var result = "";
if (signature.typeParameters) {
result += "<";
for (var i = 0; i < signature.typeParameters.length; i++) {
if (i > 0) result += ", ";
var tp = signature.typeParameters[i];
result += symbolToString(tp.symbol);
var constraint = getConstraintOfTypeParameter(tp);
if (constraint) {
result += " extends ";
result += typeToString(constraint);
}
}
result += ">";
}
result += "(";
for (var i = 0; i < signature.parameters.length; i++) {
if (i > 0) result += ", ";
var p = signature.parameters[i];
if (getDeclarationFlagsFromSymbol(p) & NodeFlags.Rest) {
result += "...";
}
result += symbolToString(p);
if (p.valueDeclaration.flags & NodeFlags.QuestionMark || (<VariableDeclaration>p.valueDeclaration).initializer) {
result += "?";
}
result += ": ";
result += typeToString(getTypeOfSymbol(p));
}
result += arrowStyle ? ") => " : "): ";
result += typeToString(getReturnTypeOfSignature(signature));
return result;
}
}
function getApparentType(type: Type): ApparentType {
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;
}
return <ApparentType>type;
}
function getTypeOfPrototypeProperty(prototype: Symbol): Type {
// TypeScript 1.0 spec (April 2014): 8.4
// Every class automatically contains a static property member named 'prototype',
// the type of which is an instantiation of the class type with type Any supplied as a type argument for each type parameter.
// It is an error to explicitly declare a static property member with the name 'prototype'.
var classType = <InterfaceType>getDeclaredTypeOfSymbol(prototype.parent);
return classType.typeParameters ? createTypeReference(<GenericType>classType, map(classType.typeParameters, _ => anyType)) : classType;
}
function getTypeOfVariableDeclaration(declaration: VariableDeclaration): Type {
var type: Type;
if (declaration.parent.kind === SyntaxKind.CatchBlock || declaration.parent.kind === SyntaxKind.ForInStatement) {
type = anyType;
}
else if (declaration.type) {
type = 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 neither accessor includes a type annotation, the inferred return type of the get accessor becomes the parameter type of the set accessor.
if (declaration.kind === SyntaxKind.Parameter && declaration.parent.kind === SyntaxKind.SetAccessor) {
var getter = <AccessorDeclaration>getDeclarationOfKind(declaration.parent.symbol, SyntaxKind.GetAccessor);
if (getter) {
//getReturnTypeOfSignature will check both type annotation and return type inferred from body
type = getReturnTypeOfSignature(getSignatureFromDeclaration(getter));
}
}
var unwidenedType: Type;
if (!type) {
if (declaration.initializer) {
unwidenedType = checkAndMarkExpression(declaration.initializer);
type = getWidenedType(unwidenedType);
}
else if (declaration.flags & NodeFlags.Rest) {
type = createArrayType(anyType);
}
else {
type = anyType;
}
}
if (program.getCompilerOptions().noImplicitAny && shouldReportNoImplicitAnyOnVariableOrParameterOrProperty(declaration, type, unwidenedType)) {
reportNoImplicitAnyOnVariableOrParameterOrProperty(declaration, type);
}
}
return type;
function shouldReportNoImplicitAnyOnVariableOrParameterOrProperty(declaration: VariableDeclaration, type: Type, unwidenedType: Type): boolean {
// If we attempted to widen, the resulting type has to be a different.
if (type === unwidenedType) {
return false;
}
// We need to have ended up with 'any', 'any[]', 'any[][]', etc.
if (getInnermostTypeOfNestedArrayTypes(type) !== anyType) {
return false;
}
// Ignore privates within ambient contexts; they exist purely for documentative purposes to avoid name clashing.
// (e.g. privates within .d.ts files do not expose type information)
if (isPrivateWithinAmbient(declaration) || (declaration.kind === SyntaxKind.Parameter && isPrivateWithinAmbient(declaration.parent))) {
return false;
}
return true;
}
function reportNoImplicitAnyOnVariableOrParameterOrProperty(declaration: VariableDeclaration, type: Type): void {
var varName = identifierToString(declaration.name);
var typeName = typeToString(type, /* printArrayAsGeneric */ false);
switch (declaration.kind) {
case SyntaxKind.VariableDeclaration:
error(declaration, Diagnostics.Variable_0_implicitly_has_an_1_type, varName, typeName)
break;
case SyntaxKind.Property:
error(declaration, Diagnostics.Member_0_implicitly_has_an_1_type, varName, typeName)
break;
case SyntaxKind.Parameter:
var funcDeclaration = <FunctionDeclaration>declaration.parent;
// If this is a rest parameter, we should have widened specifically to 'any[]'.
if (declaration.flags & NodeFlags.Rest) {
error(declaration, Diagnostics.Rest_parameter_0_implicitly_has_an_any_type, varName)
}
else {
error(declaration, Diagnostics.Parameter_0_implicitly_has_an_1_type, varName, typeName)
}
break;
default:
Debug.fail("Received a '" + SyntaxKind[declaration.kind] + "', but expected '" +
SyntaxKind[SyntaxKind.VariableDeclaration] + "', '" +
SyntaxKind[SyntaxKind.Property] + "', or '" +
SyntaxKind[SyntaxKind.Parameter] + "'.\r\n");
}
}
}
function getTypeOfVariableOrParameterOrProperty(symbol: Symbol): Type {
var links = getSymbolLinks(symbol);
if (!links.type) {
if (symbol.flags & SymbolFlags.Prototype) {
links.type = getTypeOfPrototypeProperty(symbol);
}
else {
links.type = resolvingType;
var type = getTypeOfVariableDeclaration(<VariableDeclaration>symbol.valueDeclaration);
if (links.type === resolvingType) {
links.type = type;
}
}
}
else if (links.type === resolvingType) {
links.type = anyType;
}
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 {
var setterTypeAnnotation = getSetAccessorTypeAnnotationNode(accessor);
return setterTypeAnnotation && getTypeFromTypeNode(setterTypeAnnotation);
}
}
return undefined;
}
function getTypeOfAccessors(symbol: Symbol): Type {
var links = getSymbolLinks(symbol);
if (!links.type) {
links.type = resolvingType;
var getter = <AccessorDeclaration>getDeclarationOfKind(symbol, SyntaxKind.GetAccessor);
var setter = <AccessorDeclaration>getDeclarationOfKind(symbol, SyntaxKind.SetAccessor);
var type: Type;
// First try to see if the user specified a return type on the get-accessor.
var getterReturnType = getAnnotatedAccessorType(getter);
if (getterReturnType) {
type = getterReturnType;
}
else {
// If the user didn't specify a return type, try to use the set-accessor's parameter type.
var setterParameterType = getAnnotatedAccessorType(setter);
if (setterParameterType) {
type = setterParameterType;
}
else {
// If there are no specified types, try to infer it from the body of the get accessor if it exists.
if (getter) {
type = getReturnTypeFromBody(getter);
}
// Otherwise, fall back to 'any'.
else {
if (program.getCompilerOptions().noImplicitAny) {
error(setter, Diagnostics.Property_0_implicitly_has_type_any_because_its_set_accessor_lacks_a_type_annotation, symbol.name);
}
type = anyType;
}
}
}
if (links.type === resolvingType) {
links.type = type;
}
}
else if (links.type === resolvingType) {
links.type = anyType;
}
return links.type;
}
function getTypeOfFuncClassEnumModule(symbol: Symbol): Type {
var links = getSymbolLinks(symbol);
if (!links.type) {
var type = links.type = createObjectType(TypeFlags.Anonymous, symbol);
}
return links.type;
}
function getTypeOfEnumMember(symbol: Symbol): Type {
var links = getSymbolLinks(symbol);
if (!links.type) {
links.type = getDeclaredTypeOfEnum(getParentOfSymbol(symbol));
}
return links.type;
}
function getTypeOfImport(symbol: Symbol): Type {
var links = getSymbolLinks(symbol);
if (!links.type) {
links.type = getTypeOfSymbol(resolveImport(symbol));
}
return links.type;
}
function getTypeOfInstantiatedSymbol(symbol: Symbol): Type {
var links = getSymbolLinks(symbol);
if (!links.type) {
links.type = instantiateType(getTypeOfSymbol(links.target), links.mapper);
}
return links.type;
}
function getTypeOfSymbol(symbol: Symbol): Type {
if (symbol.flags & (SymbolFlags.Variable | SymbolFlags.Property)) {
return getTypeOfVariableOrParameterOrProperty(symbol);
}
if (symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.Enum | SymbolFlags.ValueModule)) {
return getTypeOfFuncClassEnumModule(symbol);
}
if (symbol.flags & SymbolFlags.EnumMember) {
return getTypeOfEnumMember(symbol);
}
if (symbol.flags & SymbolFlags.Accessor) {
return getTypeOfAccessors(symbol);
}
if (symbol.flags & SymbolFlags.Import) {
return getTypeOfImport(symbol);
}
if (symbol.flags & SymbolFlags.Instantiated) {
return getTypeOfInstantiatedSymbol(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) {
var target = <InterfaceType>getTargetType(type);
return target === checkBase || forEach(target.baseTypes, check);
}
}
// Return combined list of type parameters from all declarations of a class or interface. Elsewhere we check they're all
// the same, but even if they're not we still need the complete list to ensure instantiations supply type arguments
// for all type parameters.
function getTypeParametersOfClassOrInterface(symbol: Symbol): TypeParameter[] {
var result: TypeParameter[];
forEach(symbol.declarations, node => {
if (node.kind === SyntaxKind.InterfaceDeclaration || node.kind === SyntaxKind.ClassDeclaration) {
var declaration = <InterfaceDeclaration>node;
if (declaration.typeParameters && declaration.typeParameters.length) {
forEach(declaration.typeParameters, node => {
var tp = getDeclaredTypeOfTypeParameter(getSymbolOfNode(node));
if (!result) {
result = [tp];
}
else if (!contains(result, tp)) {
result.push(tp);
}
});
}
}
});
return result;
}
function getDeclaredTypeOfClass(symbol: Symbol): InterfaceType {
var links = getSymbolLinks(symbol);
if (!links.declaredType) {
var type = links.declaredType = <InterfaceType>createObjectType(TypeFlags.Class, symbol);
var typeParameters = getTypeParametersOfClassOrInterface(symbol);
if (typeParameters) {
type.flags |= TypeFlags.Reference;
type.typeParameters = typeParameters;
(<GenericType>type).instantiations = {};
(<GenericType>type).instantiations[getTypeListId(type.typeParameters)] = <GenericType>type;
(<GenericType>type).target = <GenericType>type;
(<GenericType>type).typeArguments = type.typeParameters;
}
type.baseTypes = [];
var declaration = <ClassDeclaration>getDeclarationOfKind(symbol, SyntaxKind.ClassDeclaration);
if (declaration.baseType) {
var baseType = getTypeFromTypeReferenceNode(declaration.baseType);
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, /*printArrayAsGenericType*/ false));
}
}
else {
error(declaration.baseType, Diagnostics.A_class_may_only_extend_another_class);
}
}
}
type.declaredProperties = getNamedMembers(symbol.members);
type.declaredCallSignatures = emptyArray;
type.declaredConstructSignatures = emptyArray;
type.declaredStringIndexType = getIndexTypeOfSymbol(symbol, IndexKind.String);
type.declaredNumberIndexType = getIndexTypeOfSymbol(symbol, IndexKind.Number);
}
return <InterfaceType>links.declaredType;
}
function getDeclaredTypeOfInterface(symbol: Symbol): InterfaceType {
var links = getSymbolLinks(symbol);
if (!links.declaredType) {
var type = links.declaredType = <InterfaceType>createObjectType(TypeFlags.Interface, symbol);
var typeParameters = getTypeParametersOfClassOrInterface(symbol);
if (typeParameters) {
type.flags |= TypeFlags.Reference;
type.typeParameters = typeParameters;
(<GenericType>type).instantiations = {};
(<GenericType>type).instantiations[getTypeListId(type.typeParameters)] = <GenericType>type;
(<GenericType>type).target = <GenericType>type;
(<GenericType>type).typeArguments = type.typeParameters;
}
type.baseTypes = [];
forEach(symbol.declarations, declaration => {
if (declaration.kind === SyntaxKind.InterfaceDeclaration && (<InterfaceDeclaration>declaration).baseTypes) {
forEach((<InterfaceDeclaration>declaration).baseTypes, node => {
var baseType = getTypeFromTypeReferenceNode(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, /*printArrayAsGenericType*/ false));
}
}
else {
error(node, Diagnostics.An_interface_may_only_extend_a_class_or_another_interface);
}
}
});
}
});
type.declaredProperties = getNamedMembers(symbol.members);
type.declaredCallSignatures = getSignaturesOfSymbol(symbol.members["__call"]);
type.declaredConstructSignatures = getSignaturesOfSymbol(symbol.members["__new"]);
type.declaredStringIndexType = getIndexTypeOfSymbol(symbol, IndexKind.String);
type.declaredNumberIndexType = getIndexTypeOfSymbol(symbol, IndexKind.Number);
}
return <InterfaceType>links.declaredType;
}
function getDeclaredTypeOfEnum(symbol: Symbol): Type {
var links = getSymbolLinks(symbol);
if (!links.declaredType) {
var type = createType(TypeFlags.Enum);
type.symbol = symbol;
links.declaredType = type;
}
return links.declaredType;
}
function getDeclaredTypeOfTypeParameter(symbol: Symbol): TypeParameter {
var links = getSymbolLinks(symbol);
if (!links.declaredType) {
var type = <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 getDeclaredTypeOfImport(symbol: Symbol): Type {
var links = getSymbolLinks(symbol);
if (!links.declaredType) {
links.declaredType = getDeclaredTypeOfSymbol(resolveImport(symbol));
}
return links.declaredType;
}
function getDeclaredTypeOfSymbol(symbol: Symbol): Type {
if (symbol.flags & SymbolFlags.Class) {
return getDeclaredTypeOfClass(symbol);
}
if (symbol.flags & SymbolFlags.Interface) {
return getDeclaredTypeOfInterface(symbol);
}
if (symbol.flags & SymbolFlags.Enum) {
return getDeclaredTypeOfEnum(symbol);
}
if (symbol.flags & SymbolFlags.TypeParameter) {
return getDeclaredTypeOfTypeParameter(symbol);
}
if (symbol.flags & SymbolFlags.Import) {
return getDeclaredTypeOfImport(symbol);
}
Debug.assert((symbol.flags & SymbolFlags.Instantiated) === 0);
return unknownType;
}
function createSymbolTable(symbols: Symbol[]): SymbolTable {
var result: SymbolTable = {};
for (var i = 0; i < symbols.length; i++) {
var symbol = symbols[i];
result[symbol.name] = symbol;
}
return result;
}
function createInstantiatedSymbolTable(symbols: Symbol[], mapper: TypeMapper): SymbolTable {
var result: SymbolTable = {};
for (var i = 0; i < symbols.length; i++) {
var symbol = symbols[i];
result[symbol.name] = instantiateSymbol(symbol, mapper);
}
return result;
}
function addInheritedMembers(symbols: SymbolTable, baseSymbols: Symbol[]) {
for (var i = 0; i < baseSymbols.length; i++) {
var s = baseSymbols[i];
if (!hasProperty(symbols, s.name)) {
symbols[s.name] = s;
}
}
}
function addInheritedSignatures(signatures: Signature[], baseSignatures: Signature[]) {
if (baseSignatures) {
for (var i = 0; i < baseSignatures.length; i++) {
signatures.push(baseSignatures[i]);
}
}
}
function resolveClassOrInterfaceMembers(type: InterfaceType): void {
var members = type.symbol.members;
var callSignatures = type.declaredCallSignatures;
var constructSignatures = type.declaredConstructSignatures;
var stringIndexType = type.declaredStringIndexType;
var numberIndexType = type.declaredNumberIndexType;
if (type.baseTypes.length) {
members = createSymbolTable(type.declaredProperties);
forEach(type.baseTypes, baseType => {
addInheritedMembers(members, getPropertiesOfType(baseType));
callSignatures = concatenate(callSignatures, getSignaturesOfType(baseType, SignatureKind.Call));
constructSignatures = concatenate(constructSignatures, getSignaturesOfType(baseType, SignatureKind.Construct));
stringIndexType = stringIndexType || getIndexTypeOfType(baseType, IndexKind.String);
numberIndexType = numberIndexType || getIndexTypeOfType(baseType, IndexKind.Number);
});
}
setObjectTypeMembers(type, members, callSignatures, constructSignatures, stringIndexType, numberIndexType);
}
function resolveTypeReferenceMembers(type: TypeReference): void {
var target = type.target;
var mapper = createTypeMapper(target.typeParameters, type.typeArguments);
var members = createInstantiatedSymbolTable(target.declaredProperties, mapper);
var callSignatures = instantiateList(target.declaredCallSignatures, mapper, instantiateSignature);
var constructSignatures = instantiateList(target.declaredConstructSignatures, mapper, instantiateSignature);
var stringIndexType = target.declaredStringIndexType ? instantiateType(target.declaredStringIndexType, mapper) : undefined;
var numberIndexType = target.declaredNumberIndexType ? instantiateType(target.declaredNumberIndexType, mapper) : undefined;
forEach(target.baseTypes, baseType => {
var instantiatedBaseType = instantiateType(baseType, mapper);
addInheritedMembers(members, getPropertiesOfType(instantiatedBaseType));
callSignatures = concatenate(callSignatures, getSignaturesOfType(instantiatedBaseType, SignatureKind.Call));
constructSignatures = concatenate(constructSignatures, getSignaturesOfType(instantiatedBaseType, SignatureKind.Construct));
stringIndexType = stringIndexType || getIndexTypeOfType(instantiatedBaseType, IndexKind.String);
numberIndexType = numberIndexType || getIndexTypeOfType(instantiatedBaseType, IndexKind.Number);
});
setObjectTypeMembers(type, members, callSignatures, constructSignatures, stringIndexType, numberIndexType);
}
function createSignature(declaration: SignatureDeclaration, typeParameters: TypeParameter[], parameters: Symbol[],
resolvedReturnType: Type, minArgumentCount: number, hasRestParameter: boolean, hasStringLiterals: boolean): Signature {
var sig = new Signature(checker);
sig.declaration = declaration;
sig.typeParameters = typeParameters;
sig.parameters = parameters;
sig.resolvedReturnType = resolvedReturnType;
sig.minArgumentCount = minArgumentCount;
sig.hasRestParameter = hasRestParameter;
sig.hasStringLiterals = hasStringLiterals;
return sig;
}
function cloneSignature(sig: Signature): Signature {
return createSignature(sig.declaration, sig.typeParameters, sig.parameters, sig.resolvedReturnType,
sig.minArgumentCount, sig.hasRestParameter, sig.hasStringLiterals);
}
function getDefaultConstructSignatures(classType: InterfaceType): Signature[] {
if (classType.baseTypes.length) {
var baseType = classType.baseTypes[0];
var baseSignatures = getSignaturesOfType(getTypeOfSymbol(baseType.symbol), SignatureKind.Construct);
return map(baseSignatures, baseSignature => {
var signature = baseType.flags & TypeFlags.Reference ?
getSignatureInstantiation(baseSignature, (<TypeReference>baseType).typeArguments) : cloneSignature(baseSignature);
signature.typeParameters = classType.typeParameters;
signature.resolvedReturnType = classType;
return signature;
});
}
return [createSignature(undefined, classType.typeParameters, emptyArray, classType, 0, false, false)];
}
function resolveAnonymousTypeMembers(type: ObjectType) {
var symbol = type.symbol;
var members = emptySymbols;
var callSignatures = emptyArray;
var constructSignatures = emptyArray;
if (symbol.flags & SymbolFlags.HasExports) {
members = symbol.exports;
}
if (symbol.flags & (SymbolFlags.Function | SymbolFlags.Method)) {
callSignatures = getSignaturesOfSymbol(symbol);
}
if (symbol.flags & SymbolFlags.Class) {
var classType = getDeclaredTypeOfClass(symbol);
constructSignatures = getSignaturesOfSymbol(symbol.members["__constructor"]);
if (!constructSignatures.length) constructSignatures = getDefaultConstructSignatures(classType);
if (classType.baseTypes.length) {
var members = createSymbolTable(getNamedMembers(members));
addInheritedMembers(members, getPropertiesOfType(getTypeOfSymbol(classType.baseTypes[0].symbol)));
}
}
var numberIndexType = (symbol.flags & SymbolFlags.Enum) ? stringType : undefined;
setObjectTypeMembers(type, members, callSignatures, constructSignatures, /* stringIndexType */ undefined, numberIndexType);
}
function resolveObjectTypeMembers(type: ObjectType): ResolvedObjectType {
if (!(<ResolvedObjectType>type).members) {
if (type.flags & (TypeFlags.Class | TypeFlags.Interface)) {
resolveClassOrInterfaceMembers(<InterfaceType>type);
}
else if (type.flags & TypeFlags.Anonymous) {
resolveAnonymousTypeMembers(<ObjectType>type);
}
else {
resolveTypeReferenceMembers(<TypeReference>type);
}
}
return <ResolvedObjectType>type;
}
function getPropertiesOfType(type: Type): Symbol[] {
if (type.flags & TypeFlags.ObjectType) {
return resolveObjectTypeMembers(<ObjectType>type).properties;
}
return emptyArray;
}
function getPropertyOfType(type: Type, name: string): Symbol {
if (type.flags & TypeFlags.ObjectType) {
var resolved = resolveObjectTypeMembers(<ObjectType>type);
if (hasProperty(resolved.members, name)) {
var symbol = resolved.members[name];
if (symbolIsValue(symbol)) {
return symbol;
}
}
}
}
function getPropertyOfApparentType(type: ApparentType, name: string): Symbol {
if (type.flags & TypeFlags.ObjectType) {
var resolved = resolveObjectTypeMembers(<ObjectType>type);
if (hasProperty(resolved.members, name)) {
var symbol = resolved.members[name];
if (symbolIsValue(symbol)) {
return symbol;
}
}
if (resolved === anyFunctionType || resolved.callSignatures.length || resolved.constructSignatures.length) {
var symbol = getPropertyOfType(globalFunctionType, name);
if (symbol) return symbol;
}
return getPropertyOfType(globalObjectType, name);
}
}
function getSignaturesOfType(type: Type, kind: SignatureKind): Signature[] {
if (type.flags & TypeFlags.ObjectType) {
var resolved = resolveObjectTypeMembers(<ObjectType>type);
return kind === SignatureKind.Call ? resolved.callSignatures : resolved.constructSignatures;
}
return emptyArray;
}
function getIndexTypeOfType(type: Type, kind: IndexKind): Type {
if (type.flags & TypeFlags.ObjectType) {
var resolved = resolveObjectTypeMembers(<ObjectType>type);
return kind === IndexKind.String ? resolved.stringIndexType : resolved.numberIndexType;
}
}
// Return list of type parameters with duplicates removed (duplicate identifier errors are generated in the actual
// type checking functions).
function getTypeParametersFromDeclaration(typeParameterDeclarations: TypeParameterDeclaration[]): TypeParameter[] {
var result: TypeParameter[] = [];
forEach(typeParameterDeclarations, node => {
var tp = getDeclaredTypeOfTypeParameter(node.symbol);
if (!contains(result, tp)) {
result.push(tp);
}
});
return result;
}
function getSignatureFromDeclaration(declaration: SignatureDeclaration): Signature {
var links = getNodeLinks(declaration);
if (!links.resolvedSignature) {
var classType = declaration.kind === SyntaxKind.Constructor ? getDeclaredTypeOfClass((<ClassDeclaration>declaration.parent).symbol) : undefined;
var typeParameters = classType ? classType.typeParameters :
declaration.typeParameters ? getTypeParametersFromDeclaration(declaration.typeParameters) : undefined;
var parameters: Symbol[] = [];
var hasStringLiterals = false;
var minArgumentCount = -1;
for (var i = 0, n = declaration.parameters.length; i < n; i++) {
var param = declaration.parameters[i];
parameters.push(param.symbol);
if (param.type && param.type.kind === SyntaxKind.StringLiteral) {
hasStringLiterals = true;
}
if (minArgumentCount < 0) {
if (param.initializer || param.flags & (NodeFlags.QuestionMark | NodeFlags.Rest)) {
minArgumentCount = i;
}
}
}
if (minArgumentCount < 0) {
minArgumentCount = declaration.parameters.length;
}
var returnType: Type;
if (classType) {
returnType = classType;
}
else if (declaration.type) {
returnType = getTypeFromTypeNode(declaration.type);
}
else {
// TypeScript 1.0 spec (April 2014):
// If only one accessor includes a type annotation, the other behaves as if it had the same type annotation.
if (declaration.kind === SyntaxKind.GetAccessor) {
var setter = <AccessorDeclaration>getDeclarationOfKind(declaration.symbol, SyntaxKind.SetAccessor);
returnType = getAnnotatedAccessorType(setter);
}
if (!returnType && !(<FunctionDeclaration>declaration).body) {
returnType = anyType;
}
}
links.resolvedSignature = createSignature(declaration, typeParameters, parameters, returnType,
minArgumentCount, hasRestParameters(declaration), hasStringLiterals);
}
return links.resolvedSignature;
}
function getSignaturesOfSymbol(symbol: Symbol): Signature[] {
if (!symbol) return emptyArray;
var result: Signature[] = [];
for (var i = 0, len = symbol.declarations.length; i < len; i++) {
var node = symbol.declarations[i];
switch (node.kind) {
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.Method:
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 && (<FunctionDeclaration>node).body) {
var previous = symbol.declarations[i - 1];
if (node.parent === previous.parent && node.kind === previous.kind && node.pos === previous.end) {
break;
}
}
result.push(getSignatureFromDeclaration(<SignatureDeclaration>node));
}
}
return result;
}
function getReturnTypeOfSignature(signature: Signature): Type {
if (!signature.resolvedReturnType) {
signature.resolvedReturnType = resolvingType;
if (signature.target) {
var type = instantiateType(getReturnTypeOfSignature(signature.target), signature.mapper);
}
else {
var type = getReturnTypeFromBody(<FunctionDeclaration>signature.declaration);
}
if (signature.resolvedReturnType === resolvingType) {
signature.resolvedReturnType = type;
}
}
else if (signature.resolvedReturnType === resolvingType) {
signature.resolvedReturnType = anyType;
}
return signature.resolvedReturnType;
}
function getRestTypeOfSignature(signature: Signature): Type {
if (signature.hasRestParameter) {
var type = getTypeOfSymbol(signature.parameters[signature.parameters.length - 1]);
if (type.flags & TypeFlags.Reference && (<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) {
var isConstructor = signature.declaration.kind === SyntaxKind.Constructor || signature.declaration.kind === SyntaxKind.ConstructSignature;
var type = <ResolvedObjectType>createObjectType(TypeFlags.Anonymous | TypeFlags.FromSignature);
type.members = emptySymbols;
type.properties = emptyArray;
type.callSignatures = !isConstructor ? [signature] : emptyArray;
type.constructSignatures = isConstructor ? [signature] : emptyArray;
signature.isolatedSignatureType = type;
}
return signature.isolatedSignatureType;
}
function getIndexSymbol(symbol: Symbol): Symbol {
return symbol.members["__index"];
}
function getIndexDeclarationOfSymbol(symbol: Symbol, kind: IndexKind): SignatureDeclaration {
var syntaxKind = kind === IndexKind.Number ? SyntaxKind.NumberKeyword : SyntaxKind.StringKeyword;
var indexSymbol = getIndexSymbol(symbol);
if (indexSymbol) {
var len = indexSymbol.declarations.length;
for (var i = 0; i < len; i++) {
var node = <SignatureDeclaration>indexSymbol.declarations[i];
if (node.parameters.length === 1) {
var parameter = node.parameters[0];
if (parameter && parameter.type && parameter.type.kind === syntaxKind) {
return node;
}
}
}
}
return undefined;
}
function getIndexTypeOfSymbol(symbol: Symbol, kind: IndexKind): Type {
var declaration = getIndexDeclarationOfSymbol(symbol, kind);
return declaration
? declaration.type ? getTypeFromTypeNode(declaration.type) : anyType
: undefined;
}
function getConstraintOfTypeParameter(type: TypeParameter): Type {
if (!type.constraint) {
if (type.target) {
var targetConstraint = getConstraintOfTypeParameter(type.target);
type.constraint = targetConstraint ? instantiateType(targetConstraint, type.mapper) : noConstraintType;
}
else {
type.constraint = getTypeFromTypeNode((<TypeParameterDeclaration>getDeclarationOfKind(type.symbol, SyntaxKind.TypeParameter)).constraint);
}
}
return type.constraint === noConstraintType ? undefined : type.constraint;
}
function getTypeListId(types: Type[]) {
switch (types.length) {
case 1:
return "" + types[0].id;
case 2:
return types[0].id + "," + types[1].id;
default:
var result = "";
for (var i = 0; i < types.length; i++) {
if (i > 0) result += ",";
result += types[i].id;
}
return result;
}
}
function createTypeReference(target: GenericType, typeArguments: Type[]): TypeReference {
var id = getTypeListId(typeArguments);
var type = target.instantiations[id];
if (!type) {
type = target.instantiations[id] = <TypeReference>createObjectType(TypeFlags.Reference, target.symbol);
type.target = target;
type.typeArguments = typeArguments;
}
return type;
}
function isTypeParameterReferenceIllegalInConstraint(typeReferenceNode: TypeReferenceNode, typeParameterSymbol: Symbol): boolean {
var links = getNodeLinks(typeReferenceNode);
if (links.isIllegalTypeReferenceInConstraint !== undefined) {
return links.isIllegalTypeReferenceInConstraint;
}
// bubble up to the declaration
var currentNode: Node = typeReferenceNode;
// forEach === exists
while (!forEach(typeParameterSymbol.declarations, d => d.parent === currentNode.parent)) {
currentNode = currentNode.parent;
}
// if last step was made from the type parameter this means that path has started somewhere in constraint which is illegal
links.isIllegalTypeReferenceInConstraint = currentNode.kind === SyntaxKind.TypeParameter;
return links.isIllegalTypeReferenceInConstraint;
}
function checkTypeParameterHasIllegalReferencesInConstraint(typeParameter: TypeParameterDeclaration): void {
var typeParameterSymbol: Symbol;
function check(n: Node): void {
if (n.kind === SyntaxKind.TypeReference && (<TypeReferenceNode>n).typeName.kind === SyntaxKind.Identifier) {
var links = getNodeLinks(n);
if (links.isIllegalTypeReferenceInConstraint === undefined) {
var 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 getTypeFromTypeReferenceNode(node: TypeReferenceNode): Type {
var links = getNodeLinks(node);
if (!links.resolvedType) {
var symbol = resolveEntityName(node, node.typeName, SymbolFlags.Type);
if (symbol) {
var type: Type;
if ((symbol.flags & SymbolFlags.TypeParameter) && isTypeParameterReferenceIllegalInConstraint(node, symbol)) {
// TypeScript 1.0 spec (April 2014): 3.4.1
// Type parameters declared in a particular type parameter list
// may not be referenced in constraints in that type parameter list
// Implementation: such type references are resolved to 'unknown' type that usually denotes error
type = unknownType;
}
else {
type = getDeclaredTypeOfSymbol(symbol);
if (type.flags & (TypeFlags.Class | TypeFlags.Interface) && type.flags & TypeFlags.Reference) {
var typeParameters = (<InterfaceType>type).typeParameters;
if (node.typeArguments && node.typeArguments.length === typeParameters.length) {
type = createTypeReference(<GenericType>type, map(node.typeArguments, t => getTypeFromTypeNode(t)));
}
else {
error(node, Diagnostics.Generic_type_0_requires_1_type_argument_s, typeToString(type, /*printArrayAsGenericType*/ true), typeParameters.length);
type = undefined;
}
}
else {
if (node.typeArguments) {
error(node, Diagnostics.Type_0_is_not_generic, typeToString(type, /*printArrayAsGenericType*/ false));
type = undefined;
}
}
}
}
links.resolvedType = type || unknownType;
}
return links.resolvedType;
}
function getTypeFromTypeQueryNode(node: TypeQueryNode): Type {
var links = getNodeLinks(node);
if (!links.resolvedType) {
// TypeScript 1.0 spec (April 2014): 3.6.3
// The expression is processed as an identifier expression (section 4.3)
// or property access expression(section 4.10),
// the widened type(section 3.9) of which becomes the result.
links.resolvedType = getWidenedType(checkExpression(node.exprName));
}
return links.resolvedType;
}
function getGlobalType(name: string, arity: number = 0): ObjectType {
function getTypeDeclaration(symbol: Symbol): Declaration {
var declarations = symbol.declarations;
for (var i = 0; i < declarations.length; i++) {
var declaration = declarations[i];
switch (declaration.kind) {
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.EnumDeclaration:
case SyntaxKind.TypeLiteral:
case SyntaxKind.FunctionDeclaration:
return declaration;
}
}
}
var symbol = resolveName(undefined, name, SymbolFlags.Type, Diagnostics.Cannot_find_global_type_0, name);
if (!symbol) {
return emptyObjectType;
}
var type = getDeclaredTypeOfSymbol(symbol);
if (!(type.flags & TypeFlags.ObjectType)) {
error(getTypeDeclaration(symbol), Diagnostics.Global_type_0_must_be_a_class_or_interface_type, name);
return emptyObjectType;
}
if (((<InterfaceType>type).typeParameters ? (<InterfaceType>type).typeParameters.length : 0) !== arity) {
error(getTypeDeclaration(symbol), Diagnostics.Global_type_0_must_have_1_type_parameter_s, name, arity);
return emptyObjectType;
}
return <ObjectType>type;
}
function createArrayType(elementType: Type): Type {
return globalArrayType !== emptyObjectType ? createTypeReference(<GenericType>globalArrayType, [elementType]) : emptyObjectType;
}
function getTypeFromArrayTypeNode(node: ArrayTypeNode): Type {
var links = getNodeLinks(node);
if (!links.resolvedType) {
links.resolvedType = createArrayType(getTypeFromTypeNode(node.elementType));
}
return links.resolvedType;
}
function getTypeFromTypeLiteralNode(node: TypeLiteralNode): Type {
var links = getNodeLinks(node);
if (!links.resolvedType) {
var symbol = node.symbol;
var members = symbol.members;
var callSignatures = getSignaturesOfSymbol(members["__call"]);
var constructSignatures = getSignaturesOfSymbol(members["__new"]);
var stringIndexType = getIndexTypeOfSymbol(symbol, IndexKind.String);
var numberIndexType = getIndexTypeOfSymbol(symbol, IndexKind.Number);
links.resolvedType = createAnonymousType(symbol, members, callSignatures, constructSignatures, stringIndexType, numberIndexType);
}
return links.resolvedType;
}
function getStringLiteralType(node: StringLiteralTypeNode): StringLiteralType {
if (hasProperty(stringLiteralTypes, node.text)) return stringLiteralTypes[node.text];
var type = stringLiteralTypes[node.text] = <StringLiteralType>createType(TypeFlags.StringLiteral);
type.text = getSourceTextOfNode(node);
return type;
}
function getTypeFromStringLiteral(node: StringLiteralTypeNode): Type {
var 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.VoidKeyword:
return voidType;
case SyntaxKind.StringLiteral:
return getTypeFromStringLiteral(<StringLiteralTypeNode>node);
case SyntaxKind.TypeReference:
return getTypeFromTypeReferenceNode(<TypeReferenceNode>node);
case SyntaxKind.TypeQuery:
return getTypeFromTypeQueryNode(<TypeQueryNode>node);
case SyntaxKind.ArrayType:
return getTypeFromArrayTypeNode(<ArrayTypeNode>node);
case SyntaxKind.TypeLiteral:
return getTypeFromTypeLiteralNode(<TypeLiteralNode>node);
default:
return unknownType;
}
}
function instantiateList<T>(items: T[], mapper: TypeMapper, instantiator: (item: T, mapper: TypeMapper) => T): T[] {
if (items && items.length) {
var result: T[] = [];
for (var i = 0; i < items.length; i++) {
result.push(instantiator(items[i], mapper));
}
return result;
}
return items;
}
function createUnaryTypeMapper(source: Type, target: Type): TypeMapper {
return t => t === source ? target : t;
}
function createBinaryTypeMapper(source1: Type, target1: Type, source2: Type, target2: Type): TypeMapper {
return t => t === source1 ? target1 : t === source2 ? target2 : t;
}
function createTypeMapper(sources: Type[], targets: Type[]): TypeMapper {
switch (sources.length) {
case 1: return createUnaryTypeMapper(sources[0], targets[0]);
case 2: return createBinaryTypeMapper(sources[0], targets[0], sources[1], targets[1]);
}
return t => {
for (var i = 0; i < sources.length; i++) {
if (t === sources[i]) return targets[i];
}
return t;
};
}
function createUnaryTypeEraser(source: Type): TypeMapper {
return t => t === source ? anyType : t;
}
function createBinaryTypeEraser(source1: Type, source2: Type): TypeMapper {
return t => t === source1 || t === source2 ? anyType : t;
}
function createTypeEraser(sources: Type[]): TypeMapper {
switch (sources.length) {
case 1: return createUnaryTypeEraser(sources[0]);
case 2: return createBinaryTypeEraser(sources[0], sources[1]);
}
return t => {
for (var i = 0; i < sources.length; i++) {
if (t === sources[i]) return anyType;
}
return t;
};
}
function createInferenceMapper(context: InferenceContext): TypeMapper {
return t => {
for (var i = 0; i < context.typeParameters.length; i++) {
if (t === context.typeParameters[i]) {
return getInferredType(context, i);
}
}
return t;
}
}
function identityMapper(type: Type): Type {
return type;
}
function combineTypeMappers(mapper1: TypeMapper, mapper2: TypeMapper): TypeMapper {
return t => mapper2(mapper1(t));
}
function instantiateTypeParameter(typeParameter: TypeParameter, mapper: TypeMapper): TypeParameter {
var result = <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 {
if (signature.typeParameters && !eraseTypeParameters) {
var freshTypeParameters = instantiateList(signature.typeParameters, mapper, instantiateTypeParameter);
mapper = combineTypeMappers(createTypeMapper(signature.typeParameters, freshTypeParameters), mapper);
}
var result = createSignature(signature.declaration, freshTypeParameters,
instantiateList(signature.parameters, mapper, instantiateSymbol),
signature.resolvedReturnType ? instantiateType(signature.resolvedReturnType, mapper) : undefined,
signature.minArgumentCount, signature.hasRestParameter, signature.hasStringLiterals);
result.target = signature;
result.mapper = mapper;
return result;
}
function instantiateSymbol(symbol: Symbol, mapper: TypeMapper): Symbol {
if (symbol.flags & SymbolFlags.Instantiated) {
var links = getSymbolLinks(symbol);
// If symbol being instantiated is itself a instantiation, fetch the original target and combine the
// type mappers. This ensures that original type identities are properly preserved and that aliases
// always reference a non-aliases.
symbol = links.target;
mapper = combineTypeMappers(links.mapper, mapper);
}
// Keep the flags from the symbol we're instantiating. Mark that is instantiated, and
// also transient so that we can just store data on it directly.
var result = <TransientSymbol>createSymbol(SymbolFlags.Instantiated | SymbolFlags.Transient, symbol.name);
result.declarations = symbol.declarations;
result.parent = symbol.parent;
result.target = symbol;
result.mapper = mapper;
if (symbol.valueDeclaration) {
result.valueDeclaration = symbol.valueDeclaration;
}
return result;
}
function instantiateAnonymousType(type: ObjectType, mapper: TypeMapper): ObjectType {
var result = <ResolvedObjectType>createObjectType(TypeFlags.Anonymous, type.symbol);
result.properties = instantiateList(getPropertiesOfType(type), mapper, instantiateSymbol);
result.members = createSymbolTable(result.properties);
result.callSignatures = instantiateList(getSignaturesOfType(type, SignatureKind.Call), mapper, instantiateSignature);
result.constructSignatures = instantiateList(getSignaturesOfType(type, SignatureKind.Construct), mapper, instantiateSignature);
var stringIndexType = getIndexTypeOfType(type, IndexKind.String);
var numberIndexType = getIndexTypeOfType(type, IndexKind.Number);
if (stringIndexType) result.stringIndexType = instantiateType(stringIndexType, mapper);
if (numberIndexType) result.numberIndexType = instantiateType(numberIndexType, mapper);
return result;
}
function instantiateType(type: Type, mapper: TypeMapper): Type {
if (type.flags & TypeFlags.TypeParameter) {
return mapper(type);
}
if (type.flags & TypeFlags.Anonymous) {
return type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) ?
instantiateAnonymousType(<ObjectType>type, mapper) : type;
}
if (type.flags & TypeFlags.Reference) {
return createTypeReference((<TypeReference>type).target, instantiateList((<TypeReference>type).typeArguments, mapper, instantiateType));
}
return type;
}
// Returns true if the given expression contains (at any level of nesting) a function or arrow expression
// that is subject to contextual typing.
function isContextSensitiveExpression(node: Expression): boolean {
switch (node.kind) {
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
return !(<FunctionExpression>node).typeParameters && !forEach((<FunctionExpression>node).parameters, p => p.type);
case SyntaxKind.ObjectLiteral:
return forEach((<ObjectLiteral>node).properties, p =>
p.kind === SyntaxKind.PropertyAssignment && isContextSensitiveExpression((<PropertyDeclaration>p).initializer));
case SyntaxKind.ArrayLiteral:
return forEach((<ArrayLiteral>node).elements, e => isContextSensitiveExpression(e));
case SyntaxKind.ConditionalExpression:
return isContextSensitiveExpression((<ConditionalExpression>node).whenTrue) ||
isContextSensitiveExpression((<ConditionalExpression>node).whenFalse);
case SyntaxKind.BinaryExpression:
return (<BinaryExpression>node).operator === SyntaxKind.BarBarToken &&
(isContextSensitiveExpression((<BinaryExpression>node).left) || isContextSensitiveExpression((<BinaryExpression>node).right));
}
return false;
}
function getTypeWithoutConstructors(type: Type): Type {
if (type.flags & TypeFlags.ObjectType) {
var resolved = resolveObjectTypeMembers(<ObjectType>type);
if (resolved.constructSignatures.length) {
var result = <ResolvedObjectType>createObjectType(TypeFlags.Anonymous, type.symbol);
result.members = resolved.members;
result.properties = resolved.properties;
result.callSignatures = resolved.callSignatures;
result.constructSignatures = emptyArray;
type = result;
}
}
return type;
}
// TYPE CHECKING
var subtypeRelation: Map<boolean> = {};
var assignableRelation: Map<boolean> = {};
var identityRelation: Map<boolean> = {};
function isTypeIdenticalTo(source: Type, target: Type): boolean {
return checkTypeRelatedTo(source, target, identityRelation, /*errorNode*/ undefined, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined);
}
function isTypeSubtypeOf(source: Type, target: Type): boolean {
return checkTypeSubtypeOf(source, target, /*errorNode*/ undefined, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined);
}
function checkTypeSubtypeOf(source: Type, target: Type, errorNode: Node, chainedMessage: DiagnosticMessage, terminalMessage: DiagnosticMessage): boolean {
return checkTypeRelatedTo(source, target, subtypeRelation, errorNode, chainedMessage, terminalMessage);
}
function isTypeAssignableTo(source: Type, target: Type): boolean {
return checkTypeAssignableTo(source, target, /*errorNode*/ undefined, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined);
}
function checkTypeAssignableTo(source: Type, target: Type, errorNode: Node, chainedMessage: DiagnosticMessage, terminalMessage: DiagnosticMessage): boolean {
return checkTypeRelatedTo(source, target, assignableRelation, errorNode, chainedMessage, terminalMessage);
}
function isTypeRelatedTo(source: Type, target: Type, relation: Map<boolean>): boolean {
return checkTypeRelatedTo(source, target, relation, /*errorNode*/ undefined, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined);
}
function isSignatureAssignableTo(source: Signature, target: Signature): boolean {
var sourceType = getOrCreateTypeFromSignature(source);
var targetType = getOrCreateTypeFromSignature(target);
return checkTypeRelatedTo(sourceType, targetType, assignableRelation, /*errorNode*/ undefined, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined);
}
function isPropertyIdenticalTo(sourceProp: Symbol, targetProp: Symbol): boolean {
return isPropertyIdenticalToRecursive(sourceProp, targetProp, /*reportErrors*/ false, (s, t, _reportErrors) => isTypeIdenticalTo(s, t));
}
function checkInheritedPropertiesAreIdentical(type: InterfaceType, typeNode: Node): boolean {
if (!type.baseTypes.length) {
return true;
}
var seen: Map<{ prop: Symbol; containingType: Type }> = {};
forEach(type.declaredProperties, p => { seen[p.name] = { prop: p, containingType: type }; });
var ok = true;
for (var i = 0, len = type.baseTypes.length; i < len; ++i) {
var base = type.baseTypes[i];
var properties = getPropertiesOfType(base);
for (var j = 0, proplen = properties.length; j < proplen; ++j) {
var prop = properties[j];
if (!hasProperty(seen, prop.name)) {
seen[prop.name] = { prop: prop, containingType: base };
}
else {
var existing = seen[prop.name];
var isInheritedProperty = existing.containingType !== type;
if (isInheritedProperty && !isPropertyIdenticalTo(existing.prop, prop)) {
ok = false;
var typeName1 = typeToString(existing.containingType, /*printArrayAsGenericType*/ false);
var typeName2 = typeToString(base, /*printArrayAsGenericType*/ false);
var errorInfo = chainDiagnosticMessages(undefined, Diagnostics.Named_properties_0_of_types_1_and_2_are_not_identical, prop.name, typeName1, typeName2);
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Interface_0_cannot_simultaneously_extend_types_1_and_2_Colon, typeToString(type, /*printArrayAsGenericType*/ false), typeName1, typeName2);
addDiagnostic(createDiagnosticForNodeFromMessageChain(typeNode, errorInfo));
}
}
}
}
return ok;
}
function isPropertyIdenticalToRecursive(sourceProp: Symbol, targetProp: Symbol, reportErrors: boolean, relate: (source: Type, target: Type, reportErrors: boolean) => boolean): boolean {
Debug.assert(sourceProp);
if (!targetProp) {
return false;
}
// Two members are considered identical when
// - they are public properties with identical names, optionality, and types,
// - they are private properties originating in the same declaration and having identical types
var sourcePropIsPrivate = getDeclarationFlagsFromSymbol(sourceProp) & NodeFlags.Private;
var targetPropIsPrivate = getDeclarationFlagsFromSymbol(targetProp) & NodeFlags.Private;
if (sourcePropIsPrivate !== targetPropIsPrivate) {
return false;
}
if (sourcePropIsPrivate) {
return (getTargetSymbol(sourceProp).parent === getTargetSymbol(targetProp).parent) && relate(getTypeOfSymbol(sourceProp), getTypeOfSymbol(targetProp), reportErrors);
}
else {
return isOptionalProperty(sourceProp) === isOptionalProperty(targetProp) && relate(getTypeOfSymbol(sourceProp), getTypeOfSymbol(targetProp), reportErrors);
}
}
function checkTypeRelatedTo(source: Type, target: Type, relation: Map<boolean>, errorNode: Node, chainedMessage: DiagnosticMessage, terminalMessage: DiagnosticMessage): boolean {
var errorInfo: DiagnosticMessageChain;
var sourceStack: ObjectType[];
var targetStack: ObjectType[];
var expandingFlags: number;
var depth = 0;
var overflow = false;
Debug.assert(relation !== identityRelation || !errorNode, "no error reporting in identity checking");
var result = isRelatedToWithCustomErrors(source, target, errorNode !== undefined, chainedMessage, terminalMessage);
if (overflow) {
error(errorNode, Diagnostics.Excessive_stack_depth_comparing_types_0_and_1, typeToString(source, /*printArrayAsGenericType*/ false), typeToString(target, /*printArrayAsGenericType*/ false));
}
else if (errorInfo) {
addDiagnostic(createDiagnosticForNodeFromMessageChain(errorNode, errorInfo));
}
return result;
function reportError(message: DiagnosticMessage, arg0?: string, arg1?: string): void {
errorInfo = chainDiagnosticMessages(errorInfo, message, arg0, arg1);
}
function isRelatedTo(source: Type, target: Type, reportErrors: boolean): boolean {
return isRelatedToWithCustomErrors(source, target, reportErrors, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined);
}
function isRelatedToWithCustomErrors(source: Type, target: Type, reportErrors: boolean, chainedMessage: DiagnosticMessage, terminalMessage: DiagnosticMessage): boolean {
if (relation === identityRelation) {
// 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 true;
}
else {
if (source === target) return true;
if (target.flags & TypeFlags.Any) return true;
if (source === undefinedType) return true;
if (source === nullType && target !== undefinedType) return true;
if (source.flags & TypeFlags.Enum && target === numberType) return true;
if (source.flags & TypeFlags.StringLiteral && target === stringType) return true;
if (relation === assignableRelation) {
if (source.flags & TypeFlags.Any) return true;
if (source === numberType && target.flags & TypeFlags.Enum) return true;
}
}
if (source.flags & TypeFlags.TypeParameter && target.flags & TypeFlags.TypeParameter) {
if (typeParameterRelatedTo(<TypeParameter>source, <TypeParameter>target, reportErrors)) {
return true;
}
}
else {
var saveErrorInfo = errorInfo;
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 (typesRelatedTo((<TypeReference>source).typeArguments, (<TypeReference>target).typeArguments, reportErrors)) {
return true;
}
}
// Even if relationship doesn't hold for type arguments, it may hold in a structural comparison
// Report structural errors only if we haven't reported any errors yet
var reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo;
// identity relation does not use apparent type
var sourceOrApparentType = relation === identityRelation ? source : getApparentType(source);
if (sourceOrApparentType.flags & TypeFlags.ObjectType && target.flags & TypeFlags.ObjectType &&
objectTypeRelatedTo(sourceOrApparentType, <ObjectType>target, reportStructuralErrors)) {
errorInfo = saveErrorInfo;
return true;
}
}
if (reportErrors) {
// The error should end in a period when this is the deepest error in the chain
// (when errorInfo is undefined). Otherwise, it has a colon before the nested
// error.
chainedMessage = chainedMessage || Diagnostics.Type_0_is_not_assignable_to_type_1_Colon;
terminalMessage = terminalMessage || Diagnostics.Type_0_is_not_assignable_to_type_1;
var diagnosticKey = errorInfo ? chainedMessage : terminalMessage;
Debug.assert(diagnosticKey);
reportError(diagnosticKey, typeToString(source, /*printArrayAsGenericType*/ false), typeToString(target, /*printArrayAsGenericType*/ false));
}
return false;
}
function typesRelatedTo(sources: Type[], targets: Type[], reportErrors: boolean): boolean {
for (var i = 0, len = sources.length; i < len; i++) {
if (!isRelatedTo(sources[i], targets[i], reportErrors)) return false;
}
return true;
}
function typeParameterRelatedTo(source: TypeParameter, target: TypeParameter, reportErrors: boolean): boolean {
if (relation === identityRelation) {
if (source.symbol.name !== target.symbol.name) {
return false;
}
// covers case when both type parameters does not have constraint (both equal to noConstraintType)
if (source.constraint === target.constraint) {
return true;
}
if (source.constraint === noConstraintType || target.constraint === noConstraintType) {
return false;
}
return isRelatedTo(source.constraint, target.constraint, reportErrors);
}
else {
while (true) {
var constraint = getConstraintOfTypeParameter(source);
if (constraint === target) return true;
if (!(constraint && constraint.flags & TypeFlags.TypeParameter)) break;
source = <TypeParameter>constraint;
}
return 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): boolean {
if (overflow) return false;
var result: boolean;
var id = source.id + "," + target.id;
if ((result = relation[id]) !== undefined) return result;
if (depth > 0) {
for (var i = 0; i < depth; i++) {
if (source === sourceStack[i] && target === targetStack[i]) return true;
}
if (depth === 100) {
overflow = true;
return false;
}
}
else {
sourceStack = [];
targetStack = [];
expandingFlags = 0;
}
sourceStack[depth] = source;
targetStack[depth] = target;
depth++;
var saveExpandingFlags = expandingFlags;
if (!(expandingFlags & 1) && isDeeplyNestedGeneric(source, sourceStack)) expandingFlags |= 1;
if (!(expandingFlags & 2) && isDeeplyNestedGeneric(target, targetStack)) expandingFlags |= 2;
result = expandingFlags === 3 ||
propertiesRelatedTo(source, target, reportErrors) &&
signaturesRelatedTo(source, target, SignatureKind.Call, reportErrors) &&
signaturesRelatedTo(source, target, SignatureKind.Construct, reportErrors) &&
stringIndexTypesRelatedTo(source, target, reportErrors) &&
numberIndexTypesRelatedTo(source, target, reportErrors);
expandingFlags = saveExpandingFlags;
depth--;
if (depth === 0) {
relation[id] = result;
}
return result;
}
// Return true if the given type is part of a deeply nested chain of generic instantiations. We consider this to be the case
// when structural type comparisons have been started for 10 or more instantiations of the same generic type. It is possible,
// though highly unlikely, for this test to be true in a situation where a chain of instantiations is not infinitely expanding.
// Effectively, we will generate a false positive when two types are structurally equal to at least 10 levels, but unequal at
// some level beyond that.
function isDeeplyNestedGeneric(type: ObjectType, stack: ObjectType[]): boolean {
if (type.flags & TypeFlags.Reference && depth >= 10) {
var target = (<TypeReference>type).target;
var count = 0;
for (var i = 0; i < depth; i++) {
var 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): boolean {
if (relation === identityRelation) {
return propertiesAreIdenticalTo(source, target, reportErrors);
}
else {
return propertiesAreSubtypeOrAssignableTo(<ApparentType>source, target, reportErrors);
}
}
function propertiesAreIdenticalTo(source: ObjectType, target: ObjectType, reportErrors: boolean): boolean {
if (source === target) {
return true;
}
var sourceProperties = getPropertiesOfType(source);
var targetProperties = getPropertiesOfType(target);
if (sourceProperties.length !== targetProperties.length) {
return false;
}
for (var i = 0, len = sourceProperties.length; i < len; ++i) {
var sourceProp = sourceProperties[i];
var targetProp = getPropertyOfType(target, sourceProp.name);
if (!isPropertyIdenticalToRecursive(sourceProp, targetProp, reportErrors, isRelatedTo)) {
return false;
}
}
return true;
}
function propertiesAreSubtypeOrAssignableTo(source: ApparentType, target: ObjectType, reportErrors: boolean): boolean {
var properties = getPropertiesOfType(target);
for (var i = 0; i < properties.length; i++) {
var targetProp = properties[i];
var sourceProp = getPropertyOfApparentType(source, targetProp.name);
if (sourceProp === targetProp) {
continue;
}
var targetPropIsOptional = isOptionalProperty(targetProp);
if (!sourceProp) {
if (!targetPropIsOptional) {
if (reportErrors) {
reportError(Diagnostics.Property_0_is_missing_in_type_1, symbolToString(targetProp), typeToString(source, /*printArrayAsGenericType*/ false));
}
return false;
}
}
else if (sourceProp !== targetProp) {
if (targetProp.flags & SymbolFlags.Prototype) {
continue;
}
if (getDeclarationFlagsFromSymbol(sourceProp) & NodeFlags.Private || getDeclarationFlagsFromSymbol(targetProp) & NodeFlags.Private) {
if (reportErrors) {
reportError(Diagnostics.Private_property_0_cannot_be_reimplemented, symbolToString(targetProp));
}
return false;
}
if (!isRelatedTo(getTypeOfSymbol(sourceProp), getTypeOfSymbol(targetProp), reportErrors)) {
if (reportErrors) {
reportError(Diagnostics.Types_of_property_0_are_incompatible_Colon, symbolToString(targetProp));
}
return false;
}
else if (isOptionalProperty(sourceProp) && !targetPropIsOptional) {
// 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.Required_property_0_cannot_be_reimplemented_with_optional_property_in_1, targetProp.name, typeToString(source, /*printArrayAsGenericType*/ false));
}
return false;
}
}
}
return true;
}
function signaturesRelatedTo(source: ObjectType, target: ObjectType, kind: SignatureKind, reportErrors: boolean): boolean {
if (relation === identityRelation) {
return areSignaturesIdenticalTo(source, target, kind, reportErrors);
}
else {
return areSignaturesSubtypeOrAssignableTo(source, target, kind, reportErrors);
}
}
function areSignaturesIdenticalTo(source: ObjectType, target: ObjectType, kind: SignatureKind, reportErrors: boolean): boolean {
var sourceSignatures = getSignaturesOfType(source, kind);
var targetSignatures = getSignaturesOfType(target, kind);
if (sourceSignatures.length !== targetSignatures.length) {
return false;
}
for (var i = 0, len = sourceSignatures.length; i < len; ++i) {
if (!isSignatureIdenticalTo(sourceSignatures[i], targetSignatures[i], reportErrors)) {
return false;
}
}
return true;
}
function isSignatureIdenticalTo(source: Signature, target: Signature, reportErrors: boolean): boolean {
if (source === target) {
return true;
}
if (source.hasRestParameter !== target.hasRestParameter) {
return false;
}
if (source.parameters.length !== target.parameters.length) {
return false;
}
if (source.minArgumentCount !== target.minArgumentCount) {
return false;
}
if (source.typeParameters && target.typeParameters) {
if (source.typeParameters.length !== target.typeParameters.length) {
return false;
}
for (var i = 0, len = source.typeParameters.length; i < len; ++i) {
if (!isRelatedTo(source.typeParameters[i], target.typeParameters[i], reportErrors)) {
return false;
}
}
}
else if (source.typeParameters || source.typeParameters) {
return false;
}
// Spec 1.0 Section 3.8.3 & 3.8.4:
// M and N (the signatures) are instantiated using type Any as the type argument for all type parameters declared by M and N
source = getErasedSignature(source);
target = getErasedSignature(target);
for (var i = 0, len = source.parameters.length; i < len; i++) {
var s = source.hasRestParameter && i === len - 1 ? getRestTypeOfSignature(source) : getTypeOfSymbol(source.parameters[i]);
var t = target.hasRestParameter && i === len - 1 ? getRestTypeOfSignature(target) : getTypeOfSymbol(target.parameters[i]);
if (!isRelatedTo(s, t, reportErrors)) {
return false;
}
}
var t = getReturnTypeOfSignature(target);
var s = getReturnTypeOfSignature(source);
return isRelatedTo(s, t, reportErrors);
}
function areSignaturesSubtypeOrAssignableTo(source: ObjectType, target: ObjectType, kind: SignatureKind, reportErrors: boolean): boolean {
if (target === anyFunctionType || source === anyFunctionType) return true;
var sourceSignatures = getSignaturesOfType(source, kind);
var targetSignatures = getSignaturesOfType(target, kind);
var saveErrorInfo = errorInfo;
outer: for (var i = 0; i < targetSignatures.length; i++) {
var t = targetSignatures[i];
if (!t.hasStringLiterals || target.flags & TypeFlags.FromSignature) {
var localErrors = reportErrors;
for (var j = 0; j < sourceSignatures.length; j++) {
var s = sourceSignatures[j];
if (!s.hasStringLiterals || source.flags & TypeFlags.FromSignature) {
if (isSignatureSubtypeOrAssignableTo(s, t, localErrors)) {
errorInfo = saveErrorInfo;
continue outer;
}
// Only report errors from the first failure
localErrors = false;
}
}
return false;
}
}
return true;
}
function isSignatureSubtypeOrAssignableTo(source: Signature, target: Signature, reportErrors: boolean): boolean {
if (source === target) {
return true;
}
if (!target.hasRestParameter && source.minArgumentCount > target.parameters.length) {
return false;
}
var sourceMax = source.parameters.length;
var targetMax = target.parameters.length;
var checkCount: number;
if (source.hasRestParameter && target.hasRestParameter) {
checkCount = sourceMax > targetMax ? sourceMax : targetMax;
sourceMax--;
targetMax--;
}
else if (source.hasRestParameter) {
sourceMax--;
checkCount = targetMax;
}
else if (target.hasRestParameter) {
targetMax--;
checkCount = sourceMax;
}
else {
checkCount = sourceMax < targetMax ? sourceMax : targetMax;
}
// Spec 1.0 Section 3.8.3 & 3.8.4:
// M and N (the signatures) are instantiated using type Any as the type argument for all type parameters declared by M and N
source = getErasedSignature(source);
target = getErasedSignature(target);
for (var i = 0; i < checkCount; i++) {
var s = i < sourceMax ? getTypeOfSymbol(source.parameters[i]) : getRestTypeOfSignature(source);
var t = i < targetMax ? getTypeOfSymbol(target.parameters[i]) : getRestTypeOfSignature(target);
var saveErrorInfo = errorInfo;
if (!isRelatedTo(s, t, reportErrors)) {
if (!isRelatedTo(t, s, false)) {
if (reportErrors) {
reportError(Diagnostics.Types_of_parameters_0_and_1_are_incompatible_Colon,
source.parameters[i < sourceMax ? i : sourceMax].name,
target.parameters[i < targetMax ? i : targetMax].name);
}
return false;
}
errorInfo = saveErrorInfo;
}
}
var t = getReturnTypeOfSignature(target);
if (t === voidType) return true;
var s = getReturnTypeOfSignature(source);
return isRelatedTo(s, t, reportErrors);
}
function stringIndexTypesRelatedTo(source: ObjectType, target: ObjectType, reportErrors: boolean): boolean {
if (relation === identityRelation) {
return areIndexTypesIdenticalTo(IndexKind.String, source, target, reportErrors);
}
else {
var targetType = getIndexTypeOfType(target, IndexKind.String);
if (targetType) {
var sourceType = getIndexTypeOfType(source, IndexKind.String);
if (!sourceType) {
if (reportErrors) {
reportError(Diagnostics.Index_signature_is_missing_in_type_0, typeToString(source, /*printArrayAsGenericType*/ false));
}
return false;
}
if (!isRelatedTo(sourceType, targetType, reportErrors)) {
if (reportErrors) {
reportError(Diagnostics.Index_signatures_are_incompatible_Colon);
}
return false;
}
}
return true;
}
}
function numberIndexTypesRelatedTo(source: ObjectType, target: ObjectType, reportErrors: boolean): boolean {
if (relation === identityRelation) {
return areIndexTypesIdenticalTo(IndexKind.Number, source, target, reportErrors);
}
else {
var targetType = getIndexTypeOfType(target, IndexKind.Number);
if (targetType) {
var sourceStringType = getIndexTypeOfType(source, IndexKind.String);
var sourceNumberType = getIndexTypeOfType(source, IndexKind.Number);
if (!(sourceStringType || sourceNumberType)) {
if (reportErrors) {
reportError(Diagnostics.Index_signature_is_missing_in_type_0, typeToString(source, /*printArrayAsGenericType*/ false));
}
return false;
}
if (sourceStringType && sourceNumberType) {
// If we know for sure we're testing both string and numeric index types then only report errors from the second one
var compatible = isRelatedTo(sourceStringType, targetType, false) || isRelatedTo(sourceNumberType, targetType, reportErrors);
}
else {
var compatible = isRelatedTo(sourceStringType || sourceNumberType, targetType, reportErrors);
}
if (!compatible) {
if (reportErrors) {
reportError(Diagnostics.Index_signatures_are_incompatible_Colon);
}
return false;
}
}
return true;
}
}
function areIndexTypesIdenticalTo(indexKind: IndexKind, source: ObjectType, target: ObjectType, reportErrors: boolean): boolean {
var targetType = getIndexTypeOfType(target, indexKind);
var sourceType = getIndexTypeOfType(source, indexKind);
return (!sourceType && !targetType) || (sourceType && targetType && isRelatedTo(sourceType, targetType, reportErrors));
}
}
function isSupertypeOfEach(candidate: Type, types: Type[]): boolean {
for (var i = 0, len = types.length; i < len; i++) {
if (candidate !== types[i] && !isTypeSubtypeOf(types[i], candidate)) return false;
}
return true;
}
function getBestCommonType(types: Type[], contextualType?: Type, candidatesOnly?: boolean): Type {
if (contextualType && isSupertypeOfEach(contextualType, types)) return contextualType;
return forEach(types, t => isSupertypeOfEach(t, types) ? t : undefined) || (candidatesOnly ? undefined : emptyObjectType);
}
function isTypeOfObjectLiteral(type: Type): boolean {
return (type.flags & TypeFlags.Anonymous) && type.symbol && (type.symbol.flags & SymbolFlags.ObjectLiteral) ? true : false;
}
function getWidenedTypeOfObjectLiteral(type: Type): Type {
var properties = getPropertiesOfType(type);
if (properties.length) {
var widenedTypes: Type[] = [];
var propTypeWasWidened: boolean = false;
forEach(properties, p => {
var propType = getTypeOfSymbol(p);
var widenedType = getWidenedType(propType);
if (propType !== widenedType) {
propTypeWasWidened = true;
if (program.getCompilerOptions().noImplicitAny && getInnermostTypeOfNestedArrayTypes(widenedType) === anyType) {
error(p.valueDeclaration, Diagnostics.Object_literal_s_property_0_implicitly_has_an_1_type, p.name, typeToString(widenedType, /* printArrayAsGeneric */ false));
}
}
widenedTypes.push(widenedType);
});
if (propTypeWasWidened) {
var members: SymbolTable = {};
var index = 0;
forEach(properties, p => {
var symbol = <TransientSymbol>createSymbol(SymbolFlags.Property | SymbolFlags.Transient, p.name);
symbol.declarations = p.declarations;
symbol.parent = p.parent;
symbol.type = widenedTypes[index++];
if (p.valueDeclaration) symbol.valueDeclaration = p.valueDeclaration;
members[symbol.name] = symbol;
});
var stringIndexType = getIndexTypeOfType(type, IndexKind.String);
var numberIndexType = getIndexTypeOfType(type, IndexKind.Number);
if (stringIndexType) stringIndexType = getWidenedType(stringIndexType);
if (numberIndexType) numberIndexType = getWidenedType(numberIndexType);
type = createAnonymousType(type.symbol, members, emptyArray, emptyArray, stringIndexType, numberIndexType);
}
}
return type;
}
function isArrayType(type: Type): boolean {
return type.flags & TypeFlags.Reference && (<TypeReference>type).target === globalArrayType;
}
function getInnermostTypeOfNestedArrayTypes(type: Type): Type {
while (isArrayType(type)) {
type = (<GenericType>type).typeArguments[0];
}
return type;
}
function getWidenedTypeOfArrayLiteral(type: Type): Type {
var elementType = (<TypeReference>type).typeArguments[0];
var widenedType = getWidenedType(elementType);
type = elementType !== widenedType ? createArrayType(widenedType) : type;
return type;
}
/* If we are widening on a literal, then we may need to the 'node' parameter for reporting purposes */
function getWidenedType(type: Type): Type {
if (type.flags & (TypeFlags.Undefined | TypeFlags.Null)) {
return anyType;
}
if (isTypeOfObjectLiteral(type)) {
return getWidenedTypeOfObjectLiteral(type);
}
if (isArrayType(type)) {
return getWidenedTypeOfArrayLiteral(type);
}
return type;
}
function createInferenceContext(typeParameters: TypeParameter[]): InferenceContext {
var inferences: Type[][] = [];
for (var i = 0; i < typeParameters.length; i++) inferences.push([]);
return {
typeParameters: typeParameters,
inferences: inferences,
inferredTypes: new Array(typeParameters.length),
};
}
function inferTypes(context: InferenceContext, source: Type, target: Type) {
var sourceStack: Type[];
var targetStack: Type[];
var depth = 0;
inferFromTypes(source, target);
function isInProcess(source: Type, target: Type) {
for (var i = 0; i < depth; i++) {
if (source === sourceStack[i] && target === targetStack[i]) return true;
}
return false;
}
function isWithinDepthLimit(type: Type, stack: Type[]) {
if (depth >= 5) {
var target = (<TypeReference>type).target;
var count = 0;
for (var i = 0; i < depth; i++) {
var 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 (target.flags & TypeFlags.TypeParameter) {
// If target is a type parameter, make an inference
var typeParameters = context.typeParameters;
for (var i = 0; i < typeParameters.length; i++) {
if (target === typeParameters[i]) {
var inferences = context.inferences[i];
if (!contains(inferences, source)) inferences.push(source);
break;
}
}
}
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
var sourceTypes = (<TypeReference>source).typeArguments;
var targetTypes = (<TypeReference>target).typeArguments;
for (var i = 0; i < sourceTypes.length; i++) {
inferFromTypes(sourceTypes[i], targetTypes[i]);
}
}
else if (source.flags & TypeFlags.ObjectType && (target.flags & TypeFlags.Reference || (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, 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);
inferFromIndexTypes(source, target, IndexKind.Number);
depth--;
}
}
}
function inferFromProperties(source: Type, target: Type) {
var properties = getPropertiesOfType(target);
for (var i = 0; i < properties.length; i++) {
var targetProp = properties[i];
var sourceProp = getPropertyOfType(source, targetProp.name);
if (sourceProp) {
inferFromTypes(getTypeOfSymbol(sourceProp), getTypeOfSymbol(targetProp));
}
}
}
function inferFromSignatures(source: Type, target: Type, kind: SignatureKind) {
var sourceSignatures = getSignaturesOfType(source, kind);
var targetSignatures = getSignaturesOfType(target, kind);
var sourceLen = sourceSignatures.length;
var targetLen = targetSignatures.length;
var len = sourceLen < targetLen ? sourceLen : targetLen;
for (var i = 0; i < len; i++) {
inferFromParameters(getErasedSignature(sourceSignatures[sourceLen - len + i]), getErasedSignature(targetSignatures[targetLen - len + i]));
}
}
function inferFromParameters(source: Signature, target: Signature) {
var sourceMax = source.parameters.length;
var targetMax = target.parameters.length;
var checkCount: number;
if (!source.hasRestParameter && !target.hasRestParameter) {
checkCount = sourceMax < targetMax ? sourceMax : targetMax;
}
else if (source.hasRestParameter) {
sourceMax--;
checkCount = targetMax;
}
else if (target.hasRestParameter) {
targetMax--;
checkCount = sourceMax;
}
else {
checkCount = sourceMax > targetMax ? sourceMax : targetMax;
sourceMax--;
targetMax--;
}
for (var i = 0; i < checkCount; i++) {
var s = i < sourceMax ? getTypeOfSymbol(source.parameters[i]) : getRestTypeOfSignature(source);
var t = i < targetMax ? getTypeOfSymbol(target.parameters[i]) : getRestTypeOfSignature(target);
inferFromTypes(s, t);
}
inferFromTypes(getReturnTypeOfSignature(source), getReturnTypeOfSignature(target));
}
function inferFromIndexTypes(source: Type, target: Type, kind: IndexKind) {
var targetIndexType = getIndexTypeOfType(target, kind);
if (targetIndexType) {
var sourceIndexType = getIndexTypeOfType(source, kind);
if (sourceIndexType) {
inferFromTypes(sourceIndexType, targetIndexType);
}
}
}
}
function getInferredType(context: InferenceContext, index: number): Type {
var result = context.inferredTypes[index];
if (!result) {
var commonType = getWidenedType(getBestCommonType(context.inferences[index]));
var constraint = getConstraintOfTypeParameter(context.typeParameters[index]);
var result = constraint && !isTypeAssignableTo(commonType, constraint) ? constraint : commonType;
context.inferredTypes[index] = result;
}
return result;
}
function getInferredTypes(context: InferenceContext): Type[] {
for (var i = 0; i < context.inferredTypes.length; i++) {
getInferredType(context, i);
}
context.inferences = undefined;
return context.inferredTypes;
}
function hasAncestor(node: Node, kind: SyntaxKind): boolean {
return getAncestor(node, kind) !== undefined;
}
function getAncestor(node: Node, kind: SyntaxKind): Node {
switch (kind) {
// special-cases that can be come first
case SyntaxKind.ClassDeclaration:
while (node) {
switch (node.kind) {
case SyntaxKind.ClassDeclaration:
return <ClassDeclaration>node;
case SyntaxKind.EnumDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.ModuleDeclaration:
case SyntaxKind.ImportDeclaration:
// early exit cases - declarations cannot be nested in classes
return undefined;
default:
node = node.parent;
continue;
}
}
break;
default:
while (node) {
if (node.kind === kind) {
return node;
}
else {
node = node.parent;
}
}
break;
}
return undefined;
}
// EXPRESSION TYPE CHECKING
function checkIdentifier(node: Identifier): Type {
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");
}
var symbol = resolveName(node, node.text, SymbolFlags.Value | SymbolFlags.ExportValue, Diagnostics.Cannot_find_name_0, identifierToString(node));
if (!symbol) {
symbol = unknownSymbol;
}
if (symbol.flags & SymbolFlags.Import) {
// Mark the import as referenced so that we emit it in the final .js file.
// exception: identifiers that appear in type queries
getSymbolLinks(symbol).referenced = !isInTypeQuery(node);
}
getNodeLinks(node).resolvedSymbol = symbol;
checkCollisionWithCapturedSuperVariable(node, node);
checkCollisionWithIndexVariableInGeneratedCode(node, node);
return getTypeOfSymbol(getExportSymbolOfValueSymbolIfExported(symbol));
}
function getThisContainer(node: Node): Node {
while (true) {
node = node.parent;
if (!node) {
return node;
}
switch (node.kind) {
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.FunctionExpression:
case SyntaxKind.ModuleDeclaration:
case SyntaxKind.Property:
case SyntaxKind.Method:
case SyntaxKind.Constructor:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.EnumDeclaration:
case SyntaxKind.SourceFile:
case SyntaxKind.ArrowFunction:
return node;
}
}
}
function captureLexicalThis(node: Node, container: Node): void {
var classNode = container.parent && container.parent.kind === SyntaxKind.ClassDeclaration ? container.parent : undefined;
getNodeLinks(node).flags |= NodeCheckFlags.LexicalThis;
if (container.kind === SyntaxKind.Property || container.kind === SyntaxKind.Constructor) {
getNodeLinks(classNode).flags |= NodeCheckFlags.CaptureThis;
}
else {
getNodeLinks(container).flags |= NodeCheckFlags.CaptureThis;
}
}
function checkThisExpression(node: Node): Type {
var container = getThisContainer(node);
var needToCaptureLexicalThis = false;
// skip arrow functions
while (container.kind === SyntaxKind.ArrowFunction) {
container = getThisContainer(container);
needToCaptureLexicalThis = true;
}
switch (container.kind) {
case SyntaxKind.ModuleDeclaration:
error(node, Diagnostics.this_cannot_be_referenced_in_a_module_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.Property:
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;
}
if (needToCaptureLexicalThis) {
captureLexicalThis(node, container);
}
var classNode = container.parent && container.parent.kind === SyntaxKind.ClassDeclaration ? container.parent : undefined;
if (classNode) {
var symbol = getSymbolOfNode(classNode);
return container.flags & NodeFlags.Static ? getTypeOfSymbol(symbol) : getDeclaredTypeOfSymbol(symbol);
}
return anyType;
}
function getSuperContainer(node: Node): Node {
while (true) {
node = node.parent;
if (!node) return node;
switch (node.kind) {
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
case SyntaxKind.Property:
case SyntaxKind.Method:
case SyntaxKind.Constructor:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
return node;
}
}
}
function isInConstructorArgumentInitializer(node: Node, constructorDecl: Node): boolean {
for (var n = node; n && n !== constructorDecl; n = n.parent) {
if (n.kind === SyntaxKind.Parameter) {
return true;
}
}
return false;
}
function checkSuperExpression(node: Node, isCallExpression: boolean): Type {
var enclosingClass = <ClassDeclaration>getAncestor(node, SyntaxKind.ClassDeclaration);
var baseClass: Type;
if (enclosingClass && enclosingClass.baseType) {
var classType = <InterfaceType>getDeclaredTypeOfSymbol(getSymbolOfNode(enclosingClass));
baseClass = classType.baseTypes.length && classType.baseTypes[0];
}
if (!baseClass) {
error(node, Diagnostics.super_can_only_be_referenced_in_a_derived_class);
return unknownType;
}
var container = getSuperContainer(node);
if (container) {
var canUseSuperExpression = false;
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
var needToCaptureLexicalThis = false;
while (container && container.kind === SyntaxKind.ArrowFunction) {
container = getSuperContainer(container);
needToCaptureLexicalThis = true;
}
// 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.Method ||
container.kind === SyntaxKind.GetAccessor ||
container.kind === SyntaxKind.SetAccessor;
}
else {
canUseSuperExpression =
container.kind === SyntaxKind.Method ||
container.kind === SyntaxKind.GetAccessor ||
container.kind === SyntaxKind.SetAccessor ||
container.kind === SyntaxKind.Property ||
container.kind === SyntaxKind.Constructor;
}
}
}
if (canUseSuperExpression) {
var 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 (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;
}
// 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;
}
function checkArrayLiteral(node: ArrayLiteral, contextualType?: Type, contextualMapper?: TypeMapper): Type {
var contextualElementType = contextualType && getIndexTypeOfType(contextualType, IndexKind.Number);
var elementTypes: Type[] = [];
forEach(node.elements, element => {
if (element.kind !== SyntaxKind.OmittedExpression) {
var type = checkExpression(element, contextualElementType, contextualMapper);
if (!contains(elementTypes, type)) elementTypes.push(type);
}
});
var elementType = getBestCommonType(elementTypes, isInferentialContext(contextualMapper) ? undefined : contextualElementType, true);
if (!elementType) elementType = elementTypes.length ? emptyObjectType : undefinedType;
return createArrayType(elementType);
}
function isNumericName(name: string) {
return !isNaN(<number><any>name);
}
function getContextualTypeForProperty(type: Type, name: string) {
var prop = getPropertyOfType(type, name);
if (prop) return getTypeOfSymbol(prop);
return isNumericName(name) && getIndexTypeOfType(type, IndexKind.Number) || getIndexTypeOfType(type, IndexKind.String);
}
function checkObjectLiteral(node: ObjectLiteral, contextualType?: Type, contextualMapper?: TypeMapper): Type {
var members = node.symbol.members;
var properties: SymbolTable = {};
for (var id in members) {
if (hasProperty(members, id)) {
var member = members[id];
if (member.flags & SymbolFlags.Property) {
var contextualPropType = contextualType && getContextualTypeForProperty(contextualType, member.name);
var type = checkExpression((<PropertyDeclaration>member.declarations[0]).initializer, contextualPropType, contextualMapper);
var prop = <TransientSymbol>createSymbol(SymbolFlags.Property | SymbolFlags.Transient, member.name);
prop.declarations = member.declarations;
prop.parent = member.parent;
if (member.valueDeclaration) prop.valueDeclaration = member.valueDeclaration;
prop.type = type;
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.
var getAccessor = <AccessorDeclaration>getDeclarationOfKind(member, SyntaxKind.GetAccessor);
if (getAccessor) {
checkAccessorDeclaration(getAccessor);
}
var setAccessor = <AccessorDeclaration>getDeclarationOfKind(member, SyntaxKind.SetAccessor);
if (setAccessor) {
checkAccessorDeclaration(setAccessor);
}
}
properties[member.name] = member;
}
}
var stringIndexType = getIndexType(properties, IndexKind.String);
var numberIndexType = getIndexType(properties, IndexKind.Number);
return createAnonymousType(node.symbol, properties, emptyArray, emptyArray, stringIndexType, numberIndexType);
function getIndexType(properties: SymbolTable, kind: IndexKind) {
if (contextualType) {
var indexType = getIndexTypeOfType(contextualType, kind);
if (indexType) {
var propTypes: Type[] = [];
for (var id in properties) {
if (hasProperty(properties, id)) {
if (kind === IndexKind.String || isNumericName(id)) {
var type = getTypeOfSymbol(properties[id]);
if (!contains(propTypes, type)) propTypes.push(type);
}
}
}
return getBestCommonType(propTypes, isInferentialContext(contextualMapper) ? undefined : indexType);
}
}
}
}
function getDeclarationKindFromSymbol(s: Symbol) {
return s.flags & SymbolFlags.Prototype ? SyntaxKind.Property : s.valueDeclaration.kind;
}
function getDeclarationFlagsFromSymbol(s: Symbol) {
return s.flags & SymbolFlags.Prototype ? NodeFlags.Public | NodeFlags.Static : s.valueDeclaration.flags;
}
function checkPropertyAccess(node: PropertyAccess) {
var type = checkExpression(node.left);
if (type === unknownType) return type;
if (type !== anyType) {
var apparentType = getApparentType(getWidenedType(type));
if (<Type>apparentType === unknownType) {
// handle cases when type is Type parameter with invalid constraint
return unknownType;
}
var prop = getPropertyOfApparentType(apparentType, node.right.text);
if (!prop) {
if (node.right.text) {
error(node.right, Diagnostics.Property_0_does_not_exist_on_type_1, identifierToString(node.right), typeToString(type, /*printArrayAsGenericType*/ false));
}
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 (node.left.kind === SyntaxKind.SuperKeyword && getDeclarationKindFromSymbol(prop) !== SyntaxKind.Method) {
error(node.right, Diagnostics.Only_public_methods_of_the_base_class_are_accessible_via_the_super_keyword);
}
else if (getDeclarationFlagsFromSymbol(prop) & NodeFlags.Private) {
var classDeclaration = getAncestor(node, SyntaxKind.ClassDeclaration);
if (!classDeclaration || !contains(prop.parent.declarations, classDeclaration)) {
error(node, Diagnostics.Property_0_is_inaccessible, getFullyQualifiedName(prop));
}
}
}
return getTypeOfSymbol(prop);
}
return anyType;
}
function checkIndexedAccess(node: IndexedAccess): Type {
var objectType = checkExpression(node.object);
var indexType = checkExpression(node.index);
if (objectType === unknownType) 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.
var apparentType = getApparentType(objectType);
if (<Type>apparentType === unknownType) {
// handle cases when objectType is type parameter with invalid type
return unknownType;
}
if (node.index.kind === SyntaxKind.StringLiteral || node.index.kind === SyntaxKind.NumericLiteral) {
var name = (<LiteralExpression>node.index).text;
var prop = getPropertyOfApparentType(apparentType, name);
if (prop) {
return getTypeOfSymbol(prop);
}
}
// Check for compatible indexer types.
if (indexType.flags & (TypeFlags.Any | TypeFlags.StringLike | TypeFlags.NumberLike)) {
// Try to use a number indexer.
if (indexType.flags & (TypeFlags.Any | TypeFlags.NumberLike)) {
var numberIndexType = getIndexTypeOfType(apparentType, IndexKind.Number);
if (numberIndexType) {
return numberIndexType;
}
}
// Try to use string indexing.
var stringIndexType = getIndexTypeOfType(apparentType, IndexKind.String);
if (stringIndexType) {
return stringIndexType;
}
// Fall back to any.
if (program.getCompilerOptions().noImplicitAny && 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_or_any);
return unknownType;
}
function checkUntypedCall(node: CallExpression): Type {
forEach(node.arguments, argument => { checkExpression(argument); });
return anyType;
}
function checkErrorCall(node: CallExpression): Type {
checkUntypedCall(node);
return unknownType;
}
function isCandidateSignature(node: CallExpression, signature: Signature) {
var args = node.arguments || emptyArray;
return args.length >= signature.minArgumentCount &&
(signature.hasRestParameter || args.length <= signature.parameters.length) &&
(!node.typeArguments || signature.typeParameters && node.typeArguments.length === signature.typeParameters.length);
}
// The candidate list is in reverse order of declaration, except that groups of signatures with the same parent are
// kept in declaration order.
function collectCandidates(node: CallExpression, signatures: Signature[]): Signature[] {
var result: Signature[] = [];
var lastParent: Node;
var pos: number;
for (var i = 0; i < signatures.length; i++) {
var signature = signatures[i];
if (isCandidateSignature(node, signature)) {
var parent = signature.declaration ? signature.declaration.parent : undefined;
if (lastParent && parent === lastParent) {
pos++;
}
else {
lastParent = parent;
pos = 0;
}
for (var j = result.length; j > pos; j--) {
result[j] = result[j - 1];
}
result[pos] = signature;
}
}
return result;
}
function inferTypeArguments(signature: Signature, args: Expression[], excludeArgument?: boolean[]): Type[] {
var typeParameters = signature.typeParameters;
var context = createInferenceContext(typeParameters);
var mapper = createInferenceMapper(context);
// First infer from arguments that are not context sensitive
for (var i = 0; i < args.length; i++) {
if (!excludeArgument || excludeArgument[i] === undefined) {
var parameterType = getTypeAtPosition(signature, i);
inferTypes(context, checkExpression(args[i], parameterType, mapper), parameterType);
}
}
// Next, infer from those context sensitive arguments that are no longer excluded
if (excludeArgument) {
for (var i = 0; i < args.length; i++) {
if (excludeArgument[i] === false) {
var parameterType = getTypeAtPosition(signature, i);
inferTypes(context, checkExpression(args[i], parameterType, mapper), parameterType);
}
}
}
return getInferredTypes(context);
}
function checkTypeArguments(signature: Signature, typeArguments: TypeNode[]): Type[] {
var typeParameters = signature.typeParameters;
var result: Type[] = [];
for (var i = 0; i < typeParameters.length; i++) {
var typeArgNode = typeArguments[i];
var typeArgument = getTypeFromTypeNode(typeArgNode);
var constraint = getConstraintOfTypeParameter(typeParameters[i]);
if (constraint) {
checkTypeAssignableTo(typeArgument, constraint, typeArgNode, Diagnostics.Type_0_does_not_satisfy_the_constraint_1_Colon, Diagnostics.Type_0_does_not_satisfy_the_constraint_1);
}
result.push(typeArgument);
}
return result;
}
function isApplicableSignature(node: CallExpression, signature: Signature, relation: Map<boolean>, excludeArgument: boolean[]) {
if (node.arguments) {
for (var i = 0; i < node.arguments.length; i++) {
var arg = node.arguments[i];
var paramType = getTypeAtPosition(signature, i);
var argType = arg.kind === SyntaxKind.StringLiteral ?
getStringLiteralType(<LiteralExpression>arg) :
checkExpression(arg, paramType, excludeArgument && excludeArgument[i] ? identityMapper : undefined);
if (!isTypeRelatedTo(argType, paramType, relation)) return false;
}
}
return true;
}
function checkCall(node: CallExpression, signatures: Signature[]): Type {
forEach(node.typeArguments, checkSourceElement);
var candidates = collectCandidates(node, signatures);
if (!candidates.length) {
error(node, Diagnostics.Supplied_parameters_do_not_match_any_signature_of_call_target);
return checkErrorCall(node);
}
var args = node.arguments || emptyArray;
var excludeArgument: boolean[];
for (var i = 0; i < args.length; i++) {
if (isContextSensitiveExpression(args[i])) {
if (!excludeArgument) excludeArgument = new Array(args.length);
excludeArgument[i] = true;
}
}
var relation = candidates.length === 1 ? assignableRelation : subtypeRelation;
while (true) {
for (var i = 0; i < candidates.length; i++) {
while (true) {
var candidate = candidates[i];
if (candidate.typeParameters) {
var typeArguments = node.typeArguments ?
checkTypeArguments(candidate, node.typeArguments) :
inferTypeArguments(candidate, args, excludeArgument);
candidate = getSignatureInstantiation(candidate, typeArguments);
}
if (!isApplicableSignature(node, candidate, relation, excludeArgument)) break;
var index = excludeArgument ? indexOf(excludeArgument, true) : -1;
if (index < 0) {
return getReturnTypeOfSignature(candidate);
}
excludeArgument[index] = false;
}
}
if (relation === assignableRelation) break;
relation = assignableRelation;
}
error(node, Diagnostics.Supplied_parameters_do_not_match_any_signature_of_call_target);
return checkErrorCall(node);
}
function checkCallExpression(node: CallExpression): Type {
if (node.func.kind === SyntaxKind.SuperKeyword) {
var superType = checkSuperExpression(node.func, true);
if (superType !== unknownType) {
checkCall(node, getSignaturesOfType(superType, SignatureKind.Construct));
}
else {
checkUntypedCall(node);
}
// TS 1.0 spec: 4.8.1
// The type of a super call expression is Void.
return voidType;
}
var funcType = checkExpression(node.func);
if (funcType === unknownType) {
// Another error has already been reported
return checkErrorCall(node);
}
var apparentType = getApparentType(funcType)
if (<Type>apparentType === unknownType) {
// handler cases when funcType is type parameter with invalid constraint
// Another error was already reported
return checkErrorCall(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.
var signatures = getSignaturesOfType(apparentType, SignatureKind.Call);
var 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 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.
// NOTE (not in spec yet): permit untyped call only if type has no both call and construct signatures
if ((funcType === anyType) || (!signatures.length && !constructSignatures.length && isTypeAssignableTo(funcType, globalFunctionType))) {
if (node.typeArguments) {
error(node, Diagnostics.Untyped_function_calls_may_not_accept_type_arguments);
}
return checkUntypedCall(node);
}
// If FuncExpr<70>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 (!signatures.length) {
if (constructSignatures.length) {
error(node, Diagnostics.Value_of_type_0_is_not_callable_Did_you_mean_to_include_new, typeToString(funcType, /*printArrayAsGenericType*/ false));
}
else {
error(node, Diagnostics.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature);
}
return checkErrorCall(node);
}
return checkCall(node, signatures);
}
function checkNewExpression(node: NewExpression): Type {
var expressionType = checkExpression(node.func);
if (expressionType === unknownType) {
// Another error has already been reported
return checkErrorCall(node);
}
// 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 checkUntypedCall(node);
}
// If ConstructExpr<70>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 (<Type>expressionType === unknownType) {
// handler cases when original expressionType is a type parameter with invalid constraint
// another error has already been reported
return checkErrorCall(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.
var constructSignatures = getSignaturesOfType(expressionType, SignatureKind.Construct);
if (constructSignatures.length) {
return checkCall(node, constructSignatures);
}
// If ConstructExpr<70>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.
var callSignatures = getSignaturesOfType(expressionType, SignatureKind.Call);
if (callSignatures.length) {
var type = checkCall(node, callSignatures);
if (type !== voidType) {
error(node, Diagnostics.Only_a_void_function_can_be_called_with_the_new_keyword);
}
// Since we found no constructor signature, we (implicitly) return any.
if (program.getCompilerOptions().noImplicitAny) {
error(node, Diagnostics.new_expression_whose_target_lacks_a_construct_signature_implicitly_has_an_any_type);
}
return anyType;
}
error(node, Diagnostics.Cannot_use_new_with_an_expression_whose_type_lacks_a_call_or_construct_signature);
return checkErrorCall(node);
}
function checkTypeAssertion(node: TypeAssertion): Type {
var targetType = getTypeFromTypeNode(node.type);
if (targetType === unknownType) return unknownType;
var exprType = checkExpression(node.operand, targetType);
var widenedType = getWidenedType(exprType);
if (!(isTypeAssignableTo(exprType, targetType) || isTypeAssignableTo(targetType, widenedType))) {
checkTypeAssignableTo(targetType, widenedType, node, Diagnostics.Neither_type_0_nor_type_1_is_assignable_to_the_other_Colon, Diagnostics.Neither_type_0_nor_type_1_is_assignable_to_the_other);
}
return targetType;
}
function getContextualSignature(contextualType: Type) {
if (contextualType) {
var signatures = getSignaturesOfType(contextualType, SignatureKind.Call);
if (signatures.length === 1) {
var signature = signatures[0];
if (!signature.typeParameters) {
return signature;
}
}
}
}
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) {
var len = signature.parameters.length - (signature.hasRestParameter ? 1 : 0);
for (var i = 0; i < len; i++) {
var parameter = signature.parameters[i];
var links = getSymbolLinks(parameter);
if (!links.type) {
links.type = instantiateType(getTypeAtPosition(context, i), mapper);
}
}
if (signature.hasRestParameter && context.hasRestParameter && signature.parameters.length >= context.parameters.length) {
var parameter = signature.parameters[signature.parameters.length - 1];
var links = getSymbolLinks(parameter);
if (!links.type) {
links.type = instantiateType(getTypeOfSymbol(context.parameters[context.parameters.length - 1]), mapper);
}
}
}
function getReturnTypeFromBody(func: FunctionDeclaration, contextualType?: Type, contextualMapper?: TypeMapper): Type {
if (func.body.kind !== SyntaxKind.FunctionBlock) {
var unwidenedType = checkAndMarkExpression(func.body, contextualType, contextualMapper);
var widenedType = getWidenedType(unwidenedType);
if (program.getCompilerOptions().noImplicitAny && widenedType !== unwidenedType && getInnermostTypeOfNestedArrayTypes(widenedType) === anyType) {
error(func, Diagnostics.Function_expression_which_lacks_return_type_annotation_implicitly_has_an_0_return_type, typeToString(widenedType, /* printArrayAsGeneric */ false));
}
return widenedType;
}
// Aggregate the types of expressions within all the return statements.
var types: Type[] = [];
checkAndAggregateReturnExpressionTypes(func.body);
// Try to return the best common type if we have any return expressions.
if (types.length) {
var commonType = getBestCommonType(types, /*contextualType:*/ undefined, /*candidatesOnly:*/ true);
if (!commonType) {
error(func, Diagnostics.No_best_common_type_exists_among_return_expressions);
return unknownType;
}
var widenedType = getWidenedType(commonType);
// Check and report for noImplicitAny if the best common type implicitly gets widened to an 'any'/arrays-of-'any' type.
if (program.getCompilerOptions().noImplicitAny && widenedType !== commonType && getInnermostTypeOfNestedArrayTypes(widenedType) === anyType) {
var typeName = typeToString(widenedType, /* printArrayAsGeneric */ false);
if (func.name) {
error(func, Diagnostics._0_which_lacks_return_type_annotation_implicitly_has_an_1_return_type, identifierToString(func.name), typeName);
}
else {
error(func, Diagnostics.Function_expression_which_lacks_return_type_annotation_implicitly_has_an_0_return_type, typeName);
}
}
return widenedType;
}
return voidType;
function checkAndAggregateReturnExpressionTypes(node: Node) {
switch (node.kind) {
case SyntaxKind.ReturnStatement:
var expr = (<ReturnStatement>node).expression;
if (expr) {
var type = checkAndMarkExpression(expr, contextualType, contextualMapper);
if (!contains(types, type)) types.push(type);
}
break;
case SyntaxKind.Block:
case SyntaxKind.FunctionBlock:
case SyntaxKind.IfStatement:
case SyntaxKind.DoStatement:
case SyntaxKind.WhileStatement:
case SyntaxKind.ForStatement:
case SyntaxKind.ForInStatement:
case SyntaxKind.WithStatement:
case SyntaxKind.SwitchStatement:
case SyntaxKind.CaseClause:
case SyntaxKind.DefaultClause:
case SyntaxKind.LabelledStatement:
case SyntaxKind.TryStatement:
case SyntaxKind.TryBlock:
case SyntaxKind.CatchBlock:
case SyntaxKind.FinallyBlock:
forEachChild(node, checkAndAggregateReturnExpressionTypes);
break;
}
}
}
function checkFunctionExpression(node: FunctionExpression, contextualType?: Type, contextualMapper?: TypeMapper): Type {
// The identityMapper object is used to indicate that function expressions are wildcards
if (contextualMapper === identityMapper) return anyFunctionType;
var type = getTypeOfSymbol(node.symbol);
var links = getNodeLinks(node);
if (!(links.flags & NodeCheckFlags.TypeChecked)) {
var signature = getSignaturesOfType(type, SignatureKind.Call)[0];
var contextualSignature = getContextualSignature(contextualType);
if (contextualSignature) {
if (!node.typeParameters && !forEach(node.parameters, p => p.type)) {
assignContextualParameterTypes(signature, contextualSignature, contextualMapper || identityMapper);
}
if (!node.type) {
signature.resolvedReturnType = resolvingType;
var returnType = getReturnTypeFromBody(node, getReturnTypeOfSignature(contextualSignature), contextualMapper);
if (signature.resolvedReturnType === resolvingType) {
signature.resolvedReturnType = returnType;
}
}
}
checkSignatureDeclaration(node);
if (node.body.kind === SyntaxKind.FunctionBlock) {
checkSourceElement(node.body);
}
else {
var returnType = getReturnTypeOfSignature(signature);
if (node.type) {
// Use default error messages for this check
checkTypeAssignableTo(checkExpression(node.body, returnType), returnType, node.body, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined);
}
}
links.flags |= NodeCheckFlags.TypeChecked;
}
return type;
}
function checkArithmeticOperandType(operand: Node, type: Type, diagnostic: DiagnosticMessage): boolean {
if (!(type.flags & (TypeFlags.Any | TypeFlags.NumberLike))) {
error(operand, diagnostic);
return false;
}
return true;
}
function checkReferenceExpression(n: Node, message: DiagnosticMessage): boolean {
function findSymbol(n: Node): Symbol {
var 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:
var 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.PropertyAccess:
var 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.IndexedAccess:
// old compiler doesn't check indexed assess
return true;
case SyntaxKind.ParenExpression:
return isReferenceOrErrorExpression((<ParenExpression>n).expression);
default:
return false;
}
}
if (!isReferenceOrErrorExpression(n)) {
error(n, message);
return false;
}
return true;
}
function checkPrefixExpression(node: UnaryExpression): Type {
var operandType = checkExpression(node.operand);
switch (node.operator) {
case SyntaxKind.PlusToken:
case SyntaxKind.MinusToken:
case SyntaxKind.TildeToken:
return numberType;
case SyntaxKind.ExclamationToken:
case SyntaxKind.DeleteKeyword:
return booleanType;
case SyntaxKind.TypeOfKeyword:
return stringType;
case SyntaxKind.VoidKeyword:
return undefinedType;
case SyntaxKind.PlusPlusToken:
case SyntaxKind.MinusMinusToken:
var 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);
}
return numberType;
}
return unknownType;
}
function checkPostfixExpression(node: UnaryExpression): Type {
var operandType = checkExpression(node.operand);
var 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);
}
return numberType;
}
function isTypeAnyTypeObjectTypeOrTypeParameter(type: Type): boolean {
return type === anyType || ((type.flags & (TypeFlags.ObjectType | TypeFlags.TypeParameter)) !== 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.
if (!isTypeAnyTypeObjectTypeOrTypeParameter(leftType)) {
error(node.left, Diagnostics.The_left_hand_side_of_an_instanceof_expression_must_be_of_type_any_an_object_type_or_a_type_parameter);
}
if (rightType !== anyType && !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 (leftType !== anyType && leftType !== stringType && leftType !== numberType) {
error(node.left, Diagnostics.The_left_hand_side_of_an_in_expression_must_be_of_types_any_string_or_number);
}
if (!isTypeAnyTypeObjectTypeOrTypeParameter(rightType)) {
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 checkBinaryExpression(node: BinaryExpression, contextualType?: Type, contextualMapper?: TypeMapper) {
var operator = node.operator;
var leftContextualType = operator === SyntaxKind.BarBarToken ? contextualType : undefined
var leftType = checkExpression(node.left, leftContextualType, contextualMapper);
var rightContextualType = operator >= SyntaxKind.FirstAssignment && operator <= SyntaxKind.LastAssignment ? leftType :
operator === SyntaxKind.BarBarToken ? contextualType || leftType : undefined;
var rightType = checkExpression(node.right, rightContextualType, 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;
var leftOk = checkArithmeticOperandType(node.left, leftType, Diagnostics.The_left_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_or_an_enum_type);
var 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;
var resultType: Type;
if (leftType.flags & TypeFlags.NumberLike && rightType.flags & 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 (leftType.flags & TypeFlags.StringLike || rightType.flags & 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 || leftType === unknownType || rightType.flags & TypeFlags.Any || rightType === unknownType) {
// 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;
}
if (!resultType) {
reportOperatorError();
return anyType;
}
if (operator === SyntaxKind.PlusEqualsToken) {
checkAssignmentOperator(resultType);
}
return resultType;
case SyntaxKind.EqualsEqualsToken:
case SyntaxKind.ExclamationEqualsToken:
case SyntaxKind.EqualsEqualsEqualsToken:
case SyntaxKind.ExclamationEqualsEqualsToken:
case SyntaxKind.LessThanToken:
case SyntaxKind.GreaterThanToken:
case SyntaxKind.LessThanEqualsToken:
case SyntaxKind.GreaterThanEqualsToken:
if (!isTypeSubtypeOf(leftType, rightType) && !isTypeSubtypeOf(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 getBestCommonType([leftType, rightType], isInferentialContext(contextualMapper) ? undefined : contextualType);
case SyntaxKind.EqualsToken:
checkAssignmentOperator(rightType);
return rightType;
case SyntaxKind.CommaToken:
return rightType;
}
function checkAssignmentOperator(valueType: Type): void {
if (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.
var ok = checkReferenceExpression(node.left, Diagnostics.Invalid_left_hand_side_of_assignment_expression);
// Use default messages
if (ok) {
// to avoid cascading errors check assignability only if 'isReference' check succeded and no errors were reported
checkTypeAssignableTo(valueType, leftType, node.left, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined);
}
}
}
function reportOperatorError() {
error(node, Diagnostics.Operator_0_cannot_be_applied_to_types_1_and_2, tokenToString(node.operator), typeToString(leftType, /*printArrayAsGenericType*/ false), typeToString(rightType, /*printArrayAsGenericType*/ false));
}
}
function checkConditionalExpression(node: ConditionalExpression, contextualType?: Type, contextualMapper?: TypeMapper): Type {
checkExpression(node.condition);
var type1 = checkExpression(node.whenTrue, contextualType, contextualMapper);
var type2 = checkExpression(node.whenFalse, contextualType, contextualMapper);
var resultType = getBestCommonType([type1, type2], isInferentialContext(contextualMapper) ? undefined : contextualType, true);
if (!resultType) {
if (contextualType && !isInferentialContext(contextualMapper)) {
error(node, Diagnostics.No_best_common_type_exists_between_0_1_and_2, typeToString(contextualType, /*printArrayAsGenericType*/ false), typeToString(type1, /*printArrayAsGenericType*/ false), typeToString(type2, /*printArrayAsGenericType*/ false));
}
else {
error(node, Diagnostics.No_best_common_type_exists_between_0_and_1, typeToString(type1, /*printArrayAsGenericType*/ false), typeToString(type2, /*printArrayAsGenericType*/ false));
}
resultType = emptyObjectType;
}
return resultType;
}
function checkAndMarkExpression(node: Expression, contextualType?: Type, contextualMapper?: TypeMapper): Type {
var result = checkExpression(node, contextualType, contextualMapper);
getNodeLinks(node).flags |= NodeCheckFlags.TypeChecked;
return result;
}
// Checks an expression and returns its type. The contextualType parameter provides a contextual type for
// the check or is undefined if there is no contextual type. The contextualMapper parameter serves two
// purposes: When contextualMapper is not undefined and not equal to the identityMapper function object
// it provides a type mapper to use during inferential typing (the contextual type is then a generic 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 checkExpression(node: Expression, contextualType?: Type, 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, false);
case SyntaxKind.NullKeyword:
return nullType;
case SyntaxKind.TrueKeyword:
case SyntaxKind.FalseKeyword:
return booleanType;
case SyntaxKind.NumericLiteral:
return numberType;
case SyntaxKind.StringLiteral:
return stringType;
case SyntaxKind.RegularExpressionLiteral:
return globalRegExpType;
case SyntaxKind.QualifiedName:
return checkPropertyAccess(<QualifiedName>node);
case SyntaxKind.ArrayLiteral:
return checkArrayLiteral(<ArrayLiteral>node, contextualType, contextualMapper);
case SyntaxKind.ObjectLiteral:
return checkObjectLiteral(<ObjectLiteral>node, contextualType, contextualMapper);
case SyntaxKind.PropertyAccess:
return checkPropertyAccess(<PropertyAccess>node);
case SyntaxKind.IndexedAccess:
return checkIndexedAccess(<IndexedAccess>node);
case SyntaxKind.CallExpression:
return checkCallExpression(<CallExpression>node);
case SyntaxKind.NewExpression:
return checkNewExpression(<NewExpression>node);
case SyntaxKind.TypeAssertion:
return checkTypeAssertion(<TypeAssertion>node);
case SyntaxKind.ParenExpression:
return checkExpression((<ParenExpression>node).expression);
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
return checkFunctionExpression(<FunctionExpression>node, contextualType, contextualMapper);
case SyntaxKind.PrefixOperator:
return checkPrefixExpression(<UnaryExpression>node);
case SyntaxKind.PostfixOperator:
return checkPostfixExpression(<UnaryExpression>node);
case SyntaxKind.BinaryExpression:
return checkBinaryExpression(<BinaryExpression>node, contextualType, contextualMapper);
case SyntaxKind.ConditionalExpression:
return checkConditionalExpression(<ConditionalExpression>node, contextualType, contextualMapper);
}
return unknownType;
}
// DECLARATION AND STATEMENT TYPE CHECKING
function checkTypeParameter(node: TypeParameterDeclaration) {
checkNameIsReserved(node.name, Diagnostics.Type_parameter_name_cannot_be_0);
checkSourceElement(node.constraint);
checkTypeParameterHasIllegalReferencesInConstraint(node);
// TODO: Check multiple declarations are identical
}
function checkParameter(parameterDeclaration: ParameterDeclaration) {
checkVariableDeclaration(parameterDeclaration);
checkCollisionWithIndexVariableInGeneratedCode(parameterDeclaration, parameterDeclaration.name);
if (parameterDeclaration.flags & (NodeFlags.Public | NodeFlags.Private) && !(parameterDeclaration.parent.kind === SyntaxKind.Constructor && (<ConstructorDeclaration>parameterDeclaration.parent).body)) {
error(parameterDeclaration, Diagnostics.A_parameter_property_is_only_allowed_in_a_constructor_implementation);
}
if (parameterDeclaration.flags & NodeFlags.Rest) {
if (!isArrayType(getTypeOfSymbol(parameterDeclaration.symbol))) {
error(parameterDeclaration, Diagnostics.A_rest_parameter_must_be_of_an_array_type);
}
}
else {
if (parameterDeclaration.initializer && !(<FunctionDeclaration>parameterDeclaration.parent).body) {
error(parameterDeclaration, Diagnostics.A_parameter_initializer_is_only_allowed_in_a_function_or_constructor_implementation);
}
}
function checkReferencesInInitializer(n: Node): void {
if (n.kind === SyntaxKind.Identifier) {
var referencedSymbol = getNodeLinks(n).resolvedSymbol;
// check FunctionDeclaration.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(parameterDeclaration.parent.locals, referencedSymbol.name, SymbolFlags.Value) === referencedSymbol) {
if (referencedSymbol.valueDeclaration.kind === SyntaxKind.Parameter) {
if (referencedSymbol.valueDeclaration === parameterDeclaration) {
error(n, Diagnostics.Parameter_0_cannot_be_referenced_in_its_initializer, identifierToString(parameterDeclaration.name));
return;
}
var enclosingOrReferencedParameter =
forEach((<FunctionDeclaration>parameterDeclaration.parent).parameters, p => p === parameterDeclaration || p === referencedSymbol.valueDeclaration ? p : undefined);
if (enclosingOrReferencedParameter === referencedSymbol.valueDeclaration) {
// legal case - parameter initializer references some parameter strictly on left of current parameter declaration
return;
}
// fallthrough to error reporting
}
error(n, Diagnostics.Initializer_of_parameter_0_cannot_reference_identifier_1_declared_after_it, identifierToString(parameterDeclaration.name), identifierToString(<Identifier>n));
}
}
else {
forEachChild(n, checkReferencesInInitializer);
}
}
if (parameterDeclaration.initializer) {
checkReferencesInInitializer(parameterDeclaration.initializer);
}
}
function checkSignatureDeclaration(node: SignatureDeclaration) {
checkTypeParameters(node.typeParameters);
forEach(node.parameters, checkParameter);
if (node.type) {
checkSourceElement(node.type);
}
checkCollisionWithCapturedSuperVariable(node, node.name);
checkCollisionWithArgumentsInGeneratedCode(node);
if (program.getCompilerOptions().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;
}
}
checkSpecializedSignatureDeclaration(node);
}
function checkTypeForDuplicateIndexSignatures(node: Node) {
if (node.kind === SyntaxKind.InterfaceDeclaration) {
var 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
var indexSymbol = getIndexSymbol(getSymbolOfNode(node));
if (indexSymbol) {
var seenNumericIndexer = false;
var seenStringIndexer = false;
for (var i = 0, len = indexSymbol.declarations.length; i < len; ++i) {
var declaration = <SignatureDeclaration>indexSymbol.declarations[i];
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) {
// TODO
checkVariableDeclaration(node);
}
function checkMethodDeclaration(node: MethodDeclaration) {
// TODO
checkFunctionDeclaration(node);
}
function checkConstructorDeclaration(node: ConstructorDeclaration) {
// TODO
checkDeclarationModifiers(node);
checkSignatureDeclaration(node);
checkSourceElement(node.body);
var symbol = getSymbolOfNode(node);
var symbolLinks = getSymbolLinks(symbol);
var type = getTypeOfSymbol(symbol.parent);
if (!(symbolLinks.typeChecked)) {
checkFunctionOrConstructorSymbol(symbol);
symbolLinks.typeChecked = true;
}
// exit early in the case of signature - super checks are not relevant to them
if (!node.body) {
return;
}
function isSuperCallExpression(n: Node): boolean {
return n.kind === SyntaxKind.CallExpression && (<CallExpression>n).func.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.ObjectLiteral: 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.Property &&
!(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 ((<ClassDeclaration>node.parent).baseType) {
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.
var superCallShouldBeFirst =
forEach((<ClassDeclaration>node.parent).members, isInstancePropertyWithInitializer) ||
forEach(node.parameters, p => p.flags & (NodeFlags.Public | NodeFlags.Private));
if (superCallShouldBeFirst) {
var 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) {
function checkGetterContainsSingleThrowStatement(node: AccessorDeclaration): boolean {
var block = <Block>node.body;
return block.statements.length === 1 && block.statements[0].kind === SyntaxKind.ThrowStatement;
}
function checkGetterReturnsValue(n: Node): boolean {
switch (n.kind) {
case SyntaxKind.ReturnStatement:
return true;
// do not dive into function-like things - return statements there don't count
case SyntaxKind.FunctionExpression:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.ArrowFunction:
case SyntaxKind.ObjectLiteral:
return false;
default:
return forEachChild(n, checkGetterReturnsValue);
}
}
if (node.kind === SyntaxKind.GetAccessor) {
if (!isDeclarationContext(node) && node.body && !(checkGetterContainsSingleThrowStatement(node) || checkGetterReturnsValue(node))) {
error(node.name, Diagnostics.Getters_must_return_a_value);
}
}
// TypeScript 1.0 spec (April 2014): 8.4.3
// Accessors for the same member name must specify the same accessibility.
var otherKind = node.kind === SyntaxKind.GetAccessor ? SyntaxKind.SetAccessor : SyntaxKind.GetAccessor;
var otherAccessor = <AccessorDeclaration>getDeclarationOfKind(node.symbol, otherKind);
if (otherAccessor) {
var visibilityFlags = NodeFlags.Private | NodeFlags.Public;
if (((node.flags & visibilityFlags) !== (otherAccessor.flags & visibilityFlags))) {
error(node.name, Diagnostics.Getter_and_setter_accessors_do_not_agree_in_visibility);
}
var thisType = getAnnotatedAccessorType(node);
var otherType = getAnnotatedAccessorType(otherAccessor);
// TypeScript 1.0 spec (April 2014): 4.5
// If both accessors include type annotations, the specified types must be identical.
if (thisType && otherType) {
if (!isTypeIdenticalTo(thisType, otherType)) {
error(node, Diagnostics.get_and_set_accessor_must_have_the_same_type);
}
}
}
checkFunctionDeclaration(node);
}
function checkTypeReference(node: TypeReferenceNode) {
var type = getTypeFromTypeReferenceNode(node);
if (type !== unknownType && node.typeArguments) {
// Do type argument local checks only if referenced type is successfully resolved
var len = node.typeArguments.length;
for (var i = 0; i < len; i++) {
checkSourceElement(node.typeArguments[i]);
var constraint = getConstraintOfTypeParameter((<TypeReference>type).target.typeParameters[i]);
if (constraint) {
var typeArgument = (<TypeReference>type).typeArguments[i];
checkTypeAssignableTo(typeArgument, constraint, node, Diagnostics.Type_0_does_not_satisfy_the_constraint_1_Colon, Diagnostics.Type_0_does_not_satisfy_the_constraint_1);
}
}
}
}
function checkTypeQuery(node: TypeQueryNode) {
getTypeFromTypeQueryNode(node);
}
function checkTypeLiteral(node: TypeLiteralNode) {
forEach(node.members, checkSourceElement);
var type = getTypeFromTypeLiteralNode(node);
checkIndexConstraints(type);
checkTypeForDuplicateIndexSignatures(node);
}
function checkArrayType(node: ArrayTypeNode) {
getTypeFromArrayTypeNode(node);
}
function isPrivateWithinAmbient(node: Node): boolean {
return (node.flags & NodeFlags.Private) && isAmbientContext(node);
}
function isAmbientContext(node: Node): boolean {
while (node) {
if (node.flags & NodeFlags.Ambient) return true;
node = node.parent;
}
return false;
}
function isDeclarationContext(node: Node): boolean {
while (node) {
if (node.flags & (NodeFlags.Ambient | NodeFlags.DeclarationFile)) return true;
node = node.parent;
}
return false;
}
function checkDeclarationModifiers(node: Node) {
if (node.flags & NodeFlags.Export && node.parent.kind === SyntaxKind.SourceFile) {
checkModulesEnabled(node);
}
}
function checkSpecializedSignatureDeclaration(signatureDeclarationNode: SignatureDeclaration): void {
var 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 ((<FunctionDeclaration>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
var signaturesOfSymbol = getSignaturesOfSymbol(getSymbolOfNode(signatureDeclarationNode));
for (var i = 0; i < signaturesOfSymbol.length; i++) {
var otherSignature = signaturesOfSymbol[i];
if (!otherSignature.hasStringLiterals && isSignatureAssignableTo(signature, otherSignature)) {
return;
}
}
error(signatureDeclarationNode, Diagnostics.Specialized_overload_signature_is_not_assignable_to_any_non_specialized_signature);
}
function checkFunctionOrConstructorSymbol(symbol: Symbol) {
function getEffectiveFlagsForFunctionCheck(n: Node) {
var flags = n.flags;
// We want to determine if an overload is effectively ambient, which can happen if it
// is nested in an ambient context. However, do not treat members of interfaces differently
// based on whether the interface itself is in an ambient context. Interfaces should never
// be considered ambient for purposes of comparing overload attributes.
if (n.parent.kind !== SyntaxKind.InterfaceDeclaration && isDeclarationContext(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 checkFlagAgreementBetweenOverloads(overloads: Declaration[], implementation: FunctionDeclaration, 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
var someButNotAllOverloadFlags = someOverloadFlags ^ allOverloadFlags;
if (someButNotAllOverloadFlags !== 0) {
// 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
var implementationSharesContainerWithFirstOverload = implementation !== undefined && implementation.parent === overloads[0].parent;
var canonicalFlags = implementationSharesContainerWithFirstOverload
? getEffectiveFlagsForFunctionCheck(implementation)
: getEffectiveFlagsForFunctionCheck(overloads[0]);
forEach(overloads, o => {
var deviation = getEffectiveFlagsForFunctionCheck(o) ^ 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) {
error(o.name, Diagnostics.Overload_signatures_must_all_be_public_or_private);
}
else if (deviation & NodeFlags.QuestionMark) {
error(o.name, Diagnostics.Overload_signatures_must_all_be_optional_or_required);
}
});
}
}
var flagsToCheck: NodeFlags = NodeFlags.Export | NodeFlags.Ambient | NodeFlags.Private | NodeFlags.QuestionMark;
var someNodeFlags: NodeFlags = 0;
var allNodeFlags = flagsToCheck;
var hasOverloads = false;
var bodyDeclaration: FunctionDeclaration;
var lastSeenNonAmbientDeclaration: FunctionDeclaration;
var declarations = symbol.declarations;
var isConstructor = (symbol.flags & SymbolFlags.Constructor) !== 0;
for (var i = 0; i < declarations.length; i++) {
var node = <FunctionDeclaration>declarations[i];
if (node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.Method || node.kind === SyntaxKind.Constructor) {
var currentNodeFlags = getEffectiveFlagsForFunctionCheck(node);
someNodeFlags |= currentNodeFlags;
allNodeFlags &= currentNodeFlags;
var inAmbientContext = isDeclarationContext(node);
var inAmbientContextOrInterface = node.parent.kind === SyntaxKind.InterfaceDeclaration || node.parent.kind === SyntaxKind.TypeLiteral || inAmbientContext;
if (!inAmbientContextOrInterface) {
lastSeenNonAmbientDeclaration = node;
}
if (node.body) {
if (bodyDeclaration) {
if (isConstructor) {
error(node, Diagnostics.Multiple_constructor_implementations_are_not_allowed);
}
else {
error(node, Diagnostics.Duplicate_function_implementation);
}
}
else {
bodyDeclaration = node;
}
}
else {
hasOverloads = true;
}
}
}
if (lastSeenNonAmbientDeclaration && !lastSeenNonAmbientDeclaration.body) {
if (isConstructor) {
error(lastSeenNonAmbientDeclaration, Diagnostics.Constructor_implementation_expected);
}
else {
error(lastSeenNonAmbientDeclaration, Diagnostics.Function_implementation_expected);
}
}
if (hasOverloads) {
checkFlagAgreementBetweenOverloads(declarations, bodyDeclaration, flagsToCheck, someNodeFlags, allNodeFlags);
if (bodyDeclaration) {
var signatures = getSignaturesOfSymbol(symbol);
var 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 (var i = 0, len = signatures.length; i < len; ++i) {
if (!signatures[i].hasStringLiterals && !isSignatureAssignableTo(bodySignature, signatures[i])) {
error(signatures[i].declaration, Diagnostics.Overload_signature_is_not_compatible_with_function_implementation);
break;
}
}
}
}
}
// TODO: Check at least one return statement in non-void/any function (except single throw)
}
function checkFunctionDeclaration(node: FunctionDeclaration) {
checkDeclarationModifiers(node);
checkSignatureDeclaration(node);
var symbol = getSymbolOfNode(node);
var symbolLinks = getSymbolLinks(symbol);
var type = getTypeOfSymbol(symbol);
if (!(symbolLinks.typeChecked)) {
checkFunctionOrConstructorSymbol(symbol);
symbolLinks.typeChecked = true;
}
checkSourceElement(node.body);
// If there is no body and no explicit return type, then report an error.
if (program.getCompilerOptions().noImplicitAny && !node.body && !node.type) {
// Ignore privates within ambient contexts; they exist purely for documentative purposes to avoid name clashing.
// (e.g. privates within .d.ts files do not expose type information)
if (!isPrivateWithinAmbient(node)) {
var typeName = typeToString(anyType, /* printArrayAsGeneric */ false);
if (node.name) {
error(node, Diagnostics._0_which_lacks_return_type_annotation_implicitly_has_an_1_return_type, identifierToString(node.name), typeName);
}
else {
error(node, Diagnostics.Function_expression_which_lacks_return_type_annotation_implicitly_has_an_0_return_type, typeName);
}
}
}
}
function checkBlock(node: Block) {
forEach(node.statements, checkSourceElement);
}
function checkCollisionWithArgumentsInGeneratedCode(node: SignatureDeclaration) {
// no rest parameters \ declaration context \ overload - no codegen impact
if (!hasRestParameters(node) || isDeclarationContext(node) || !(<FunctionDeclaration>node).body) {
return;
}
forEach(node.parameters, p => {
if (p.name && p.name.text === argumentsSymbol.name) {
error(p, Diagnostics.Duplicate_identifier_arguments_Compiler_uses_arguments_to_initialize_rest_parameters);
}
});
}
function checkCollisionWithIndexVariableInGeneratedCode(node: Node, name: Identifier) {
if (!(name && name.text === "_i")) {
return;
}
if (node.kind === SyntaxKind.Parameter) {
// report error if parameter has name '_i' when:
// - function has implementation (not a signature)
// - function has rest parameters
// - context is not ambient (otherwise no codegen impact)
if ((<FunctionDeclaration>node.parent).body && hasRestParameters(<FunctionDeclaration>node.parent) && !isDeclarationContext(node)) {
error(node, Diagnostics.Duplicate_identifier_i_Compiler_uses_i_to_initialize_rest_parameter);
}
return;
}
var symbol = getNodeLinks(node).resolvedSymbol;
if (symbol === unknownSymbol) {
return;
}
// we would like to discover cases like one below:
//
// var _i = "!";
// function foo(...a) {
// function bar() {
// var x = { get baz() { return _i; } }
// }
// }
//
// at runtime '_i' referenced in getter will be resolved to the generated index variable '_i' used to initialize rest parameters.
// legitimate case: when '_i' is defined inside the function declaration with rest parameters.
//
// function foo(...a) {
// var _i = "!";
// function bar() {
// var x = { get baz() { return _i; } }
// }
// }
//// if resolved symbol for node has more than one declaration - this is definitely an error
//// (there is nothing value-like in the language that can be nested in function and consists of multiple declarations)
//if (symbol.declarations.length > 1) {
// error(node, Diagnostics.Expression_resolves_to_variable_declaration_i_that_compiler_uses_to_initialize_rest_parameter);
// return;
//}
// short gist of the check:
// - otherwise
// - walk to the top of the tree starting from the 'node'
// - at every step check if 'current' node contains any declaration of original node
// yes - return
// no - check if current declaration is function with rest parameters
// yes - report error since '_i' from this function will shadow '_i' defined in the outer scope
// no - go up to the next level
var current = node;
while (current) {
var definedOnCurrentLevel = forEach(symbol.declarations, d => d.parent === current ? d : undefined);
if (definedOnCurrentLevel) {
return;
}
switch (current.kind) {
// all kinds that might have rest parameters
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.FunctionExpression:
case SyntaxKind.Method:
case SyntaxKind.ArrowFunction:
case SyntaxKind.Constructor:
if (hasRestParameters(<FunctionDeclaration>current)) {
error(node, Diagnostics.Expression_resolves_to_variable_declaration_i_that_compiler_uses_to_initialize_rest_parameter);
return;
}
break;
}
current = current.parent;
}
}
function checkCollisionWithCapturedSuperVariable(node: Node, name: Identifier) {
if (!(name && name.text === "_super")) {
return;
}
if (node.kind === SyntaxKind.Property ||
node.kind === SyntaxKind.Method ||
node.kind === SyntaxKind.GetAccessor ||
node.kind === SyntaxKind.SetAccessor) {
// it is ok to have member named '_super' - member access is always qualified
return;
}
if (node.kind === SyntaxKind.Parameter && !(<FunctionDeclaration>node.parent).body) {
// just an overload - no codegen impact
return;
}
// bubble up and find containing type
var enclosingClass = <ClassDeclaration>getAncestor(node, SyntaxKind.ClassDeclaration);
// if containing type was not found or it is ambient - exit (no codegen)
if (!enclosingClass || isDeclarationContext(enclosingClass)) {
return;
}
if (enclosingClass.baseType) {
var 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 checkVariableDeclaration(node: VariableDeclaration) {
checkSourceElement(node.type);
var symbol = getSymbolOfNode(node);
var typeOfValueDeclaration = getTypeOfVariableOrParameterOrProperty(symbol);
var type: Type;
var useTypeFromValueDeclaration = node === symbol.valueDeclaration;
if (useTypeFromValueDeclaration) {
type = typeOfValueDeclaration;
}
else {
type = getTypeOfVariableDeclaration(node);
}
if (node.initializer) {
if (!(getNodeLinks(node.initializer).flags & NodeCheckFlags.TypeChecked)) {
// Use default messages
checkTypeAssignableTo(checkAndMarkExpression(node.initializer, type), type, node, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined);
}
}
checkCollisionWithCapturedSuperVariable(node, node.name);
if (!useTypeFromValueDeclaration) {
// TypeScript 1.0 spec (April 2014): 5.1
// Multiple declarations for the same variable name in the same declaration space are permitted,
// provided that each declaration associates the same type with the variable.
if (typeOfValueDeclaration !== unknownType && type !== unknownType && !isTypeIdenticalTo(typeOfValueDeclaration, type)) {
error(node.name, Diagnostics.Subsequent_variable_declarations_must_have_the_same_type_Variable_0_must_be_of_type_1_but_here_has_type_2, identifierToString(node.name), typeToString(typeOfValueDeclaration, /*printArrayAsGenericType*/ false), typeToString(type, /*printArrayAsGenericType*/ false));
}
}
}
function checkVariableStatement(node: VariableStatement) {
checkDeclarationModifiers(node);
forEach(node.declarations, checkVariableDeclaration);
}
function checkExpressionStatement(node: ExpressionStatement) {
checkExpression(node.expression);
}
function checkIfStatement(node: IfStatement) {
checkExpression(node.expression);
checkSourceElement(node.thenStatement);
checkSourceElement(node.elseStatement);
}
function checkDoStatement(node: DoStatement) {
checkSourceElement(node.statement);
checkExpression(node.expression);
}
function checkWhileStatement(node: WhileStatement) {
checkExpression(node.expression);
checkSourceElement(node.statement);
}
function checkForStatement(node: ForStatement) {
if (node.declarations) forEach(node.declarations, checkVariableDeclaration);
if (node.initializer) checkExpression(node.initializer);
if (node.condition) checkExpression(node.condition);
if (node.iterator) checkExpression(node.iterator);
checkSourceElement(node.statement);
}
function checkForInStatement(node: ForInStatement) {
// TypeScript 1.0 spec (April 2014): 5.4
// In a 'for-in' statement of the form
// for (var 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.declaration) {
checkVariableDeclaration(node.declaration);
if (node.declaration.type) {
error(node.declaration, Diagnostics.Variable_declarations_of_a_for_statement_cannot_use_a_type_annotation);
}
}
// 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.
if (node.variable) {
var exprType = checkExpression(node.variable);
if (exprType !== anyType && exprType !== stringType) {
error(node.variable, Diagnostics.Variable_declarations_of_a_for_statement_must_be_of_types_string_or_any);
}
else {
// run check only former check succeeded to avoid cascading errors
checkReferenceExpression(node.variable, Diagnostics.Invalid_left_hand_side_in_for_in_statement);
}
}
var exprType = 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 (!isTypeAnyTypeObjectTypeOrTypeParameter(exprType) && exprType !== unknownType) {
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 checkBreakOrContinueStatement(node: BreakOrContinueStatement) {
// TODO: Check that target label is valid
}
function getContainingFunction(node: Node): SignatureDeclaration {
while (true) {
node = node.parent;
if (!node || node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.FunctionExpression ||
node.kind === SyntaxKind.ArrowFunction || node.kind === SyntaxKind.Method || node.kind === SyntaxKind.Constructor ||
node.kind === SyntaxKind.GetAccessor || node.kind === SyntaxKind.SetAccessor) {
return <SignatureDeclaration>node;
}
}
}
function checkReturnStatement(node: ReturnStatement) {
if (node.expression && !(getNodeLinks(node.expression).flags & NodeCheckFlags.TypeChecked)) {
var func = getContainingFunction(node);
if (func) {
if (func.kind === SyntaxKind.SetAccessor) {
if (node.expression) {
error(node.expression, Diagnostics.Setters_cannot_return_a_value);
}
}
else {
var returnType = getReturnTypeOfSignature(getSignatureFromDeclaration(func));
// do assignability check only if we short circuited in determining return type
// - function has explicit type annotation
// - function is getter with no type annotation and setter parameter type is used
// - function is a constructor (will be special cased below)
var checkAssignability =
func.type ||
(func.kind === SyntaxKind.GetAccessor && getSetAccessorTypeAnnotationNode(<AccessorDeclaration>getDeclarationOfKind(func.symbol, SyntaxKind.SetAccessor)));
if (checkAssignability) {
checkTypeAssignableTo(checkExpression(node.expression, returnType), returnType, node.expression, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined);
}
else if (func.kind == SyntaxKind.Constructor) {
// constructor doesn't have explicit return type annontation and yet its return type is known - declaring type
// handle constructors and issue specialized error message for them.
if (!isTypeAssignableTo(checkExpression(node.expression, returnType), returnType)) {
error(node.expression, Diagnostics.Return_type_of_constructor_signature_must_be_assignable_to_the_instance_type_of_the_class);
}
}
}
}
else {
error(node, Diagnostics.return_statement_has_no_containing_function);
}
}
}
function checkWithStatement(node: WithStatement) {
checkExpression(node.expression);
checkSourceElement(node.statement);
}
function checkSwitchStatement(node: SwitchStatement) {
var expressionType = checkExpression(node.expression);
forEach(node.clauses, clause => {
if (clause.expression) {
// 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.
var caseType = checkExpression(clause.expression);
if (!isTypeAssignableTo(expressionType, caseType)) {
// check 'expressionType isAssignableTo caseType' failed, try the reversed check and report errors if it fails
checkTypeAssignableTo(caseType, expressionType, clause.expression, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined);
}
}
checkBlock(clause);
});
}
function checkLabelledStatement(node: LabelledStatement) {
checkSourceElement(node.statement);
}
function checkThrowStatement(node: ThrowStatement) {
checkExpression(node.expression);
}
function checkTryStatement(node: TryStatement) {
checkBlock(node.tryBlock);
if (node.catchBlock) checkBlock(node.catchBlock);
if (node.finallyBlock) checkBlock(node.finallyBlock);
}
function checkIndexConstraints(type: Type) {
function checkIndexConstraintForProperty(prop: Symbol, propertyType: 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.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
var errorNode: Node;
if (prop.parent === type.symbol) {
errorNode = prop.valueDeclaration;
}
else if (indexDeclaration) {
errorNode = indexDeclaration;
}
else if (type.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
var someBaseClassHasBothPropertyAndIndexer = forEach((<InterfaceType>type).baseTypes, (base) => getPropertyOfType(base, prop.name) && getIndexTypeOfType(base, indexKind));
errorNode = someBaseClassHasBothPropertyAndIndexer ? undefined : type.symbol.declarations[0];
}
if (errorNode && !isTypeAssignableTo(propertyType, indexType)) {
var 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, /*printArrayAsGenericType*/ false), typeToString(indexType, /*printArrayAsGenericType*/ false));
}
}
var declaredNumberIndexer = getIndexDeclarationOfSymbol(type.symbol, IndexKind.Number);
var declaredStringIndexer = getIndexDeclarationOfSymbol(type.symbol, IndexKind.String);
var stringIndexType = getIndexTypeOfType(type, IndexKind.String);
var numberIndexType = getIndexTypeOfType(type, IndexKind.Number);
if (stringIndexType || numberIndexType) {
forEach(getPropertiesOfType(type), prop => {
var propType = getTypeOfSymbol(prop);
checkIndexConstraintForProperty(prop, propType, declaredStringIndexer, stringIndexType, IndexKind.String);
checkIndexConstraintForProperty(prop, propType, declaredNumberIndexer, numberIndexType, IndexKind.Number);
});
}
var 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)) {
var someBaseTypeHasBothIndexers = forEach((<InterfaceType>type).baseTypes, 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, /*printArrayAsGenericType*/ false), typeToString(stringIndexType, /*printArrayAsGenericType*/ false));
}
}
function checkNameIsReserved(name: Identifier, message: DiagnosticMessage): boolean {
// 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 (name.text) {
case "any":
case "number":
case "boolean":
case "string":
case "void":
error(name, message, name.text);
return true;
}
}
// Check each type parameter and check that list has no duplicate type parameter declarations
function checkTypeParameters(typeParameterDeclarations: TypeParameterDeclaration[]) {
if (typeParameterDeclarations) {
for (var i = 0; i < typeParameterDeclarations.length; i++) {
var node = typeParameterDeclarations[i];
checkTypeParameter(node);
for (var j = 0; j < i; j++) {
if (typeParameterDeclarations[j].symbol === node.symbol) {
error(node.name, Diagnostics.Duplicate_identifier_0, identifierToString(node.name));
}
}
}
}
}
function checkClassDeclaration(node: ClassDeclaration) {
checkDeclarationModifiers(node);
checkNameIsReserved(node.name, Diagnostics.Class_name_cannot_be_0);
checkTypeParameters(node.typeParameters);
var symbol = getSymbolOfNode(node);
var type = <InterfaceType>getDeclaredTypeOfSymbol(symbol);
var staticType = <ObjectType>getTypeOfSymbol(symbol);
if (node.baseType) {
emitExtends = emitExtends || !isDeclarationContext(node);
checkTypeReference(node.baseType);
}
if (type.baseTypes.length) {
var baseType = type.baseTypes[0];
checkTypeAssignableTo(type, baseType, node.name, Diagnostics.Class_0_incorrectly_extends_base_class_1_Colon, Diagnostics.Class_0_incorrectly_extends_base_class_1);
var staticBaseType = getTypeOfSymbol(baseType.symbol);
checkTypeAssignableTo(staticType, getTypeWithoutConstructors(staticBaseType), node.name,
Diagnostics.Class_static_side_0_incorrectly_extends_base_class_static_side_1_Colon, Diagnostics.Class_static_side_0_incorrectly_extends_base_class_static_side_1);
if (baseType.symbol !== resolveEntityName(node, node.baseType.typeName, SymbolFlags.Value)) {
error(node.baseType, Diagnostics.Type_name_0_in_extends_clause_does_not_reference_constructor_function_for_0, typeToString(baseType, /*printArrayAsGenericType*/ false));
}
// Check that base type can be evaluated as expression
checkExpression(node.baseType.typeName);
checkKindsOfPropertyMemberOverrides(type, baseType);
}
if (node.implementedTypes) {
forEach(node.implementedTypes, typeRefNode => {
checkTypeReference(typeRefNode);
var t = getTypeFromTypeReferenceNode(typeRefNode);
if (t !== unknownType) {
var declaredType = (t.flags & TypeFlags.Reference) ? (<TypeReference>t).target : t;
if (declaredType.flags & (TypeFlags.Class | TypeFlags.Interface)) {
checkTypeAssignableTo(type, t, node.name, Diagnostics.Class_0_incorrectly_implements_interface_1_Colon, Diagnostics.Class_0_incorrectly_implements_interface_1);
}
else {
error(typeRefNode, Diagnostics.A_class_may_only_implement_another_class_or_interface);
}
}
});
}
checkIndexConstraints(type);
forEach(node.members, checkSourceElement);
checkTypeForDuplicateIndexSignatures(node);
}
function getTargetSymbol(s: Symbol) {
// if symbol is instantiated it's 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
var baseProperties = getPropertiesOfType(baseType);
for (var i = 0, len = baseProperties.length; i < len; ++i) {
var base = getTargetSymbol(baseProperties[i]);
if (base.flags & SymbolFlags.Prototype) {
continue;
}
var derived = getTargetSymbol(getPropertyOfType(type, base.name));
if (derived) {
var baseDeclarationFlags = getDeclarationFlagsFromSymbol(base);
var 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;
}
var 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);
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);
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);
Debug.assert(derived.flags & SymbolFlags.Method);
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, /* printArrayAsGenericType*/ false), symbolToString(base), typeToString(type, /* printArrayAsGenericType*/ false));
}
}
}
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 (var i = 0, len = list1.length; i < len; i++) {
var tp1 = list1[i];
var 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 checkInterfaceDeclaration(node: InterfaceDeclaration) {
checkDeclarationModifiers(node);
checkNameIsReserved(node.name, Diagnostics.Interface_name_cannot_be_0);
checkTypeParameters(node.typeParameters);
var symbol = getSymbolOfNode(node);
if (symbol.declarations.length > 1) {
var firstInterfaceDecl = <InterfaceDeclaration>getDeclarationOfKind(symbol, SyntaxKind.InterfaceDeclaration);
if (node !== firstInterfaceDecl && !areTypeParametersIdentical(firstInterfaceDecl.typeParameters, node.typeParameters)) {
error(node.name, Diagnostics.All_declarations_of_an_interface_must_have_identical_type_parameters);
}
}
var links = getSymbolLinks(symbol);
if (!links.typeChecked) {
var type = <InterfaceType>getDeclaredTypeOfSymbol(symbol);
// run subsequent checks only if first set succeeded
if (checkInheritedPropertiesAreIdentical(type, node.name)) {
forEach(type.baseTypes, baseType => {
checkTypeAssignableTo(type, baseType, node.name, Diagnostics.Interface_0_incorrectly_extends_interface_1_Colon, Diagnostics.Interface_0_incorrectly_extends_interface_1);
});
checkIndexConstraints(type);
}
links.typeChecked = true;
}
forEach(node.baseTypes, checkTypeReference);
forEach(node.members, checkSourceElement);
checkTypeForDuplicateIndexSignatures(node);
}
function getConstantValue(node: Expression): number {
var isNegative = false;
if (node.kind === SyntaxKind.PrefixOperator) {
var unaryExpression = <UnaryExpression>node;
if (unaryExpression.operator === SyntaxKind.MinusToken || unaryExpression.operator === SyntaxKind.PlusToken) {
node = unaryExpression.operand;
isNegative = unaryExpression.operator === SyntaxKind.MinusToken;
}
}
if (node.kind === SyntaxKind.NumericLiteral) {
var literalText = (<LiteralExpression>node).text;
return isNegative ? -literalText : +literalText;
}
return undefined;
}
function checkEnumDeclaration(node: EnumDeclaration) {
checkDeclarationModifiers(node);
checkNameIsReserved(node.name, Diagnostics.Enum_name_cannot_be_0);
var enumType = getDeclaredTypeOfSymbol(getSymbolOfNode(node));
var autoValue = 0;
var ambient = isDeclarationContext(node);
forEach(node.members, member => {
var initializer = member.initializer;
if (initializer) {
autoValue = getConstantValue(initializer);
if (autoValue === undefined && !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, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined);
}
}
else if (ambient) {
autoValue = undefined;
}
if (autoValue !== undefined) {
getNodeLinks(member).enumMemberValue = autoValue++;
}
});
// TODO: Only one enum declaration can omit the initial value
}
function checkModuleDeclaration(node: ModuleDeclaration) {
checkDeclarationModifiers(node);
if (node.name.kind === SyntaxKind.StringLiteral) {
if (!isDeclarationContext(node)) {
error(node, Diagnostics.Ambient_external_modules_require_a_declare_modifier);
}
if (node.parent.kind !== SyntaxKind.SourceFile || node.parent.flags & NodeFlags.ExternalModule) {
error(node, Diagnostics.Ambient_external_modules_cannot_be_nested_in_other_modules);
}
if (isExternalModuleNameRelative(node.name.text)) {
error(node, Diagnostics.Ambient_external_module_declaration_cannot_specify_relative_module_name);
}
var symbol = getSymbolOfNode(node);
var links = getSymbolLinks(symbol);
if (!links.typeChecked) {
getExportAssignmentSymbol(symbol);
links.typeChecked = true;
}
}
checkSourceElement(node.body);
}
function checkImportDeclaration(node: ImportDeclaration) {
checkDeclarationModifiers(node);
checkNameIsReserved(node.name, Diagnostics.Import_name_cannot_be_0);
var symbol = getSymbolOfNode(node);
var target: Symbol;
if (node.entityName) {
target = resolveImport(symbol);
// Import declaration for an internal module
if (target !== unknownSymbol && target.flags & SymbolFlags.Value) {
// Target is a value symbol, check that it can be evaluated as an expression
checkExpression(node.entityName);
}
}
else {
// Import declaration for an external module
if (node.parent.kind === SyntaxKind.SourceFile) {
// Parent is a source file, check that external modules are enabled
checkModulesEnabled(node);
target = resolveImport(symbol);
}
else if (node.parent.kind === SyntaxKind.ModuleBlock && (<ModuleDeclaration>node.parent.parent).name.kind === SyntaxKind.StringLiteral) {
// 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.
if (isExternalModuleNameRelative(node.externalModuleName.text)) {
error(node, Diagnostics.Import_declaration_in_an_ambient_external_module_declaration_cannot_reference_external_module_through_relative_external_module_name);
target = unknownSymbol;
}
else {
target = resolveImport(symbol);
}
}
else {
// Parent is an internal module (syntax error is already reported)
target = unknownSymbol;
}
}
if (target !== unknownSymbol) {
var 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) {
error(node, Diagnostics.Import_declaration_conflicts_with_local_declaration_of_0, symbolToString(symbol));
}
}
}
function checkModulesEnabled(node: Node) {
if (!modulesVerified) {
if (!program.getCompilerOptions().module) {
error(node, Diagnostics.Cannot_compile_external_modules_unless_the_module_flag_is_provided);
}
modulesVerified = true;
}
}
function checkExportAssignment(node: ExportAssignment) {
var container = node.parent;
if (container.kind === SyntaxKind.SourceFile) {
checkModulesEnabled(node);
}
}
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.Property:
return checkPropertyDeclaration(<PropertyDeclaration>node);
case SyntaxKind.CallSignature:
case SyntaxKind.ConstructSignature:
case SyntaxKind.IndexSignature:
return checkSignatureDeclaration(<SignatureDeclaration>node);
case SyntaxKind.Method:
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 checkTypeReference(<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.FunctionDeclaration:
return checkFunctionDeclaration(<FunctionDeclaration>node);
case SyntaxKind.Block:
case SyntaxKind.FunctionBlock:
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.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.LabelledStatement:
return checkLabelledStatement(<LabelledStatement>node);
case SyntaxKind.ThrowStatement:
return checkThrowStatement(<ThrowStatement>node);
case SyntaxKind.TryStatement:
return checkTryStatement(<TryStatement>node);
case SyntaxKind.VariableDeclaration:
return Debug.fail("Checker encountered variable declaration");
case SyntaxKind.ClassDeclaration:
return checkClassDeclaration(<ClassDeclaration>node);
case SyntaxKind.InterfaceDeclaration:
return checkInterfaceDeclaration(<InterfaceDeclaration>node);
case SyntaxKind.EnumDeclaration:
return checkEnumDeclaration(<EnumDeclaration>node);
case SyntaxKind.ModuleDeclaration:
return checkModuleDeclaration(<ModuleDeclaration>node);
case SyntaxKind.ImportDeclaration:
return checkImportDeclaration(<ImportDeclaration>node);
case SyntaxKind.ExportAssignment:
return checkExportAssignment(<ExportAssignment>node);
}
}
function checkSourceFile(node: SourceFile) {
var links = getNodeLinks(node);
if (!(links.flags & NodeCheckFlags.TypeChecked)) {
emitExtends = false;
forEach(node.statements, checkSourceElement);
if (node.flags & NodeFlags.ExternalModule) {
var symbol = getExportAssignmentSymbol(node.symbol);
if (symbol && symbol.flags & SymbolFlags.Import) {
// Mark the import as referenced so that we emit it in the final .js file.
getSymbolLinks(symbol).referenced = true;
}
}
if (emitExtends) links.flags |= NodeCheckFlags.EmitExtends;
links.flags |= NodeCheckFlags.TypeChecked;
}
}
function checkProgram() {
forEach(program.getSourceFiles(), checkSourceFile);
}
function getSortedDiagnostics(): Diagnostic[] {
if (diagnosticsModified) {
diagnostics.sort(compareDiagnostics);
diagnostics = deduplicateSortedDiagnostics(diagnostics);
diagnosticsModified = false;
}
return diagnostics;
}
function getDiagnostics(sourceFile?: SourceFile): Diagnostic[] {
if (sourceFile) {
checkSourceFile(sourceFile);
return filter(getSortedDiagnostics(), d => d.file === sourceFile);
}
checkProgram();
return getSortedDiagnostics();
}
function getGlobalDiagnostics(): Diagnostic[] {
return filter(getSortedDiagnostics(), d => !d.file);
}
// Language service support
function getNodeAtPosition(sourceFile: SourceFile, position: number): Node {
function findChildAtPosition(parent: Node) {
var child = forEachChild(parent, node => {
if (position >= node.pos && position <= node.end && position >= getTokenPosOfNode(node)) {
return findChildAtPosition(node);
}
});
return child || parent;
}
if (position < sourceFile.pos) position = sourceFile.pos;
if (position > sourceFile.end) position = sourceFile.end;
return findChildAtPosition(sourceFile);
}
function getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[] {
var symbols: SymbolTable = {};
var memberFlags: NodeFlags = 0;
function copySymbol(symbol: Symbol, meaning: SymbolFlags) {
if (symbol.flags & meaning) {
var id = symbol.name;
if (!isReservedMemberName(id) && !hasProperty(symbols, id)) {
symbols[id] = symbol;
}
}
}
function copySymbols(source: SymbolTable, meaning: SymbolFlags) {
if (meaning) {
for (var id in source) {
if (hasProperty(source, id)) {
copySymbol(source[id], meaning);
}
}
}
}
while (location) {
if (location.locals && (location.kind !== SyntaxKind.SourceFile || location.flags & NodeFlags.ExternalModule)) {
copySymbols(location.locals, meaning);
}
switch (location.kind) {
case SyntaxKind.SourceFile:
if (!(location.flags & NodeFlags.ExternalModule)) 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;
case SyntaxKind.CatchBlock:
if ((<CatchBlock>location).variable.text) {
copySymbol(location.symbol, meaning);
}
break;
}
memberFlags = location.flags;
location = location.parent;
}
copySymbols(globals, meaning);
return mapToArray(symbols);
}
// True if the given identifier is the identifier of a declaration node
function isDeclarationIdentifier(identifier: Identifier): boolean {
if (identifier.parent) {
switch (identifier.parent.kind) {
case SyntaxKind.TypeParameter:
case SyntaxKind.Parameter:
case SyntaxKind.VariableDeclaration:
case SyntaxKind.Property:
case SyntaxKind.PropertyAssignment:
case SyntaxKind.EnumMember:
case SyntaxKind.Method:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.FunctionExpression:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.EnumDeclaration:
case SyntaxKind.ModuleDeclaration:
case SyntaxKind.ImportDeclaration:
return (<Declaration>identifier.parent).name === identifier;
case SyntaxKind.CatchBlock:
return (<CatchBlock>identifier.parent).variable === identifier;
}
}
return false;
}
// True if the given identifier is part of a type reference
function isTypeReferenceIdentifier(identifier: Identifier): boolean {
var node: Node = identifier;
while (node.parent && node.parent.kind === SyntaxKind.QualifiedName) node = node.parent;
return node.parent && node.parent.kind === SyntaxKind.TypeReference;
}
function isExpression(node: Node): boolean {
switch (node.kind) {
case SyntaxKind.ThisKeyword:
case SyntaxKind.SuperKeyword:
case SyntaxKind.NullKeyword:
case SyntaxKind.TrueKeyword:
case SyntaxKind.FalseKeyword:
case SyntaxKind.RegularExpressionLiteral:
case SyntaxKind.ArrayLiteral:
case SyntaxKind.ObjectLiteral:
case SyntaxKind.PropertyAccess:
case SyntaxKind.IndexedAccess:
case SyntaxKind.CallExpression:
case SyntaxKind.NewExpression:
case SyntaxKind.TypeAssertion:
case SyntaxKind.ParenExpression:
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
case SyntaxKind.PrefixOperator:
case SyntaxKind.PostfixOperator:
case SyntaxKind.BinaryExpression:
case SyntaxKind.ConditionalExpression:
return true;
case SyntaxKind.QualifiedName:
while (node.parent && node.parent.kind === SyntaxKind.QualifiedName) node = node.parent;
return node.parent && node.parent.kind === SyntaxKind.TypeQuery;
case SyntaxKind.Identifier:
case SyntaxKind.NumericLiteral:
case SyntaxKind.StringLiteral:
var parent = node.parent;
if (parent) {
if (isExpression(parent)) return true;
switch (parent.kind) {
case SyntaxKind.VariableDeclaration:
case SyntaxKind.Parameter:
case SyntaxKind.Property:
case SyntaxKind.EnumMember:
return (<VariableDeclaration>parent).initializer === node;
case SyntaxKind.ExpressionStatement:
case SyntaxKind.IfStatement:
case SyntaxKind.DoStatement:
case SyntaxKind.WhileStatement:
case SyntaxKind.ReturnStatement:
case SyntaxKind.WithStatement:
case SyntaxKind.SwitchStatement:
case SyntaxKind.CaseClause:
case SyntaxKind.ThrowStatement:
case SyntaxKind.SwitchStatement:
return (<ExpressionStatement>parent).expression === node;
case SyntaxKind.ForStatement:
return (<ForStatement>parent).initializer === node || (<ForStatement>parent).condition === node ||
(<ForStatement>parent).iterator === node;
case SyntaxKind.ForInStatement:
return (<ForInStatement>parent).variable === node || (<ForInStatement>parent).expression === node;
}
}
}
return false;
}
function getSymbolOfIdentifier(identifier: Identifier) {
if (isExpression(identifier)) {
if (isRightSideOfQualifiedName()) {
// TODO
}
return resolveEntityName(identifier, identifier, SymbolFlags.Value);
}
if (isDeclarationIdentifier(identifier)) {
return getSymbolOfNode(identifier.parent);
}
if (isTypeReferenceIdentifier(identifier)) {
var entityName = isRightSideOfQualifiedName() ? identifier.parent : identifier;
var meaning = entityName.parent.kind === SyntaxKind.TypeReference ? SymbolFlags.Type : SymbolFlags.Namespace;
return resolveEntityName(entityName, entityName, meaning);
}
function isRightSideOfQualifiedName() {
return (identifier.parent.kind === SyntaxKind.QualifiedName || identifier.parent.kind === SyntaxKind.PropertyAccess) &&
(<QualifiedName>identifier.parent).right === identifier;
}
}
// Emitter support
function getModuleObjectName(node: ModuleDeclaration): string {
return getSourceTextOfNode(node.name);
}
function isExternalModule(symbol: Symbol): boolean {
return symbol.flags & SymbolFlags.ValueModule && symbol.declarations.length === 1 && symbol.declarations[0].kind === SyntaxKind.SourceFile;
}
function getExpressionNamePrefix(node: Identifier): string {
var symbol = getNodeLinks(node).resolvedSymbol;
if (symbol) {
// In general, we need to prefix an identifier with its parent name if it references
// an exported entity from another module declaration. 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.
var exportSymbol = getExportSymbolOfValueSymbolIfExported(symbol);
var isExportedSymbolFoundInLocalScope = exportSymbol !== symbol;
var shouldEmitExportWithoutPrefix = (exportSymbol.flags & SymbolFlags.ExportHasLocal) !== 0;
if (isExportedSymbolFoundInLocalScope && !shouldEmitExportWithoutPrefix) {
symbol = exportSymbol;
}
// symbol will have a parent if it is an export symbol
if (symbol.parent) {
return isExternalModule(symbol.parent) ? "exports" : symbolToString(symbol.parent);
}
}
}
function getPropertyAccessSubstitution(node: PropertyAccess): string {
var symbol = getNodeLinks(node).resolvedSymbol;
if (symbol && (symbol.flags & SymbolFlags.EnumMember)) {
var declaration = symbol.valueDeclaration;
var constantValue: number;
if (declaration.kind === SyntaxKind.EnumMember && (constantValue = getNodeLinks(declaration).enumMemberValue) !== undefined) {
return constantValue.toString() + " /* " + identifierToString(declaration.name) + " */";
}
}
}
function getExportAssignmentName(node: SourceFile): string {
var symbol = getExportAssignmentSymbol(getSymbolOfNode(node));
return symbol && symbolIsValue(symbol) ? symbolToString(symbol): undefined;
}
function isTopLevelValueImportedViaEntityName(node: ImportDeclaration): boolean {
if (node.parent.kind !== SyntaxKind.SourceFile || !node.entityName) {
// parent is not source file or it is not reference to internal module
return false;
}
var symbol = getSymbolOfNode(node);
var target = resolveImport(symbol);
return target !== unknownSymbol && ((target.flags & SymbolFlags.Value) !== 0);
}
function shouldEmitDeclarations() {
// If the declaration emit and there are no errors being reported in program or by checker
// declarations can be emitted
return program.getCompilerOptions().declaration &&
!program.getDiagnostics().length &&
!getDiagnostics().length;
}
function isReferencedImportDeclaration(node: ImportDeclaration): boolean {
var symbol = getSymbolOfNode(node);
if (getSymbolLinks(symbol).referenced) {
return true;
}
// logic below will answer 'true' for exported import declaration in a nested module that itself is not exported.
// As a consequence this might cause emitting extra.
if (node.flags & NodeFlags.Export) {
var target = resolveImport(symbol);
if (target !== unknownSymbol && target.flags & SymbolFlags.Value) {
return true;
}
}
return false;
}
function getNodeCheckFlags(node: Node): NodeCheckFlags {
return getNodeLinks(node).flags;
}
function getEnumMemberValue(node: EnumMember): number {
return getNodeLinks(node).enumMemberValue;
}
function invokeEmitter() {
var resolver: EmitResolver = {
getProgram: () => program,
getModuleObjectName: getModuleObjectName,
getExpressionNamePrefix: getExpressionNamePrefix,
getPropertyAccessSubstitution: getPropertyAccessSubstitution,
getExportAssignmentName: getExportAssignmentName,
isReferencedImportDeclaration: isReferencedImportDeclaration,
getNodeCheckFlags: getNodeCheckFlags,
getEnumMemberValue: getEnumMemberValue,
isTopLevelValueImportedViaEntityName: isTopLevelValueImportedViaEntityName,
shouldEmitDeclarations: shouldEmitDeclarations
};
checkProgram();
return emitFiles(resolver);
}
function initializeTypeChecker() {
// Bind all source files and propagate errors
forEach(program.getSourceFiles(), file => {
bindSourceFile(file);
forEach(file.semanticErrors, addDiagnostic);
});
// Initialize global symbol table
forEach(program.getSourceFiles(), file => {
if (!(file.flags & NodeFlags.ExternalModule)) {
extendSymbolTable(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
globalObjectType = getGlobalType("Object");
globalFunctionType = getGlobalType("Function");
globalArrayType = getGlobalType("Array", 1);
globalStringType = getGlobalType("String");
globalNumberType = getGlobalType("Number");
globalBooleanType = getGlobalType("Boolean");
globalRegExpType = getGlobalType("RegExp");
}
initializeTypeChecker();
checker = {
getProgram: () => program,
getDiagnostics: getDiagnostics,
getGlobalDiagnostics: getGlobalDiagnostics,
getNodeCount: () => sum(program.getSourceFiles(), "nodeCount"),
getIdentifierCount: () => sum(program.getSourceFiles(), "identifierCount"),
getSymbolCount: () => sum(program.getSourceFiles(), "symbolCount"),
getTypeCount: () => typeCount,
checkProgram: checkProgram,
emitFiles: invokeEmitter,
getSymbolOfNode: getSymbolOfNode,
getParentOfSymbol: getParentOfSymbol,
getTypeOfSymbol: getTypeOfSymbol,
getDeclaredTypeOfSymbol: getDeclaredTypeOfSymbol,
getPropertiesOfType: getPropertiesOfType,
getSignaturesOfType: getSignaturesOfType,
getIndexTypeOfType: getIndexTypeOfType,
getReturnTypeOfSignature: getReturnTypeOfSignature,
resolveEntityName: resolveEntityName,
getSymbolsInScope: getSymbolsInScope,
getSymbolOfIdentifier: getSymbolOfIdentifier
};
return checker;
}
}