/// /// /// /// /// /// 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 = {}; 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 symbol; if (!symbol.id) symbol.id = nextSymbolId++; return symbolLinks[symbol.id] || (symbolLinks[symbol.id] = {}); } function getNodeLinks(node: Node): NodeLinks { if (!node.id) node.id = nextNodeId++; return nodeLinks[node.id] || (nodeLinks[node.id] = {}); } function getSourceFile(node: Node): SourceFile { return getAncestor(node, SyntaxKind.SourceFile); } function 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 = (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(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 = (location).name; if (id && name === id.text) { return returnResolvedSymbol(location.symbol); } break; case SyntaxKind.CatchBlock: var id = (location).variable; if (name === id.text) { return returnResolvedSymbol((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 = 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, (name).text, meaning, Diagnostics.Cannot_find_name_0, identifierToString(name)); if (!symbol) { return; } } else if (name.kind === SyntaxKind.QualifiedName) { var namespace = resolveEntityName(location, (name).left, SymbolFlags.Namespace); if (!namespace || namespace === unknownSymbol || (name).right.kind === SyntaxKind.Missing) return; var symbol = getSymbol(namespace.exports, (name).right.text, meaning); if (!symbol) { error(location, Diagnostics.Module_0_has_no_exported_member_1, getFullyQualifiedName(namespace), identifierToString((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 = (declaration.kind === SyntaxKind.SourceFile ? declaration : (declaration).body); forEach(block.statements, node => { if (node.kind === SyntaxKind.ExportAssignment) { result.push(node); } else { seenExportedMember = seenExportedMember || (node.flags & NodeFlags.Export) !== 0; } }); }); return { hasExportedMember: seenExportedMember, exportAssignments: result }; } function getMergedSymbol(symbol: Symbol): Symbol { var merged: Symbol; return symbol && symbol.mergeId && (merged = mergedSymbols[symbol.mergeId]) ? merged : symbol; } function getSymbolOfNode(node: Node): Symbol { return getMergedSymbol(node.symbol); } function getParentOfSymbol(symbol: Symbol): Symbol { return getMergedSymbol(symbol.parent); } function getExportSymbolOfValueSymbolIfExported(symbol: Symbol): Symbol { return symbol && (symbol.flags & SymbolFlags.ExportValue) !== 0 ? getMergedSymbol(symbol.exportSymbol) : symbol; } function symbolIsValue(symbol: Symbol): boolean { // If 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 && (member).body) { return member; } } } function createType(flags: TypeFlags): Type { var result = new Type(checker, flags); result.id = typeCount++; return result; } function createIntrinsicType(kind: TypeFlags, intrinsicName: string): IntrinsicType { var type = createType(kind); type.intrinsicName = intrinsicName; return type; } function createObjectType(kind: TypeFlags, symbol?: Symbol): ObjectType { var type = createType(kind); type.symbol = symbol; return type; } // A reserved member name starts with two underscores 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 { (type).members = members; (type).properties = getNamedMembers(members); (type).callSignatures = callSignatures; (type).constructSignatures = constructSignatures; if (stringIndexType) (type).stringIndexType = stringIndexType; if (numberIndexType) (type).numberIndexType = numberIndexType; return type; } function createAnonymousType(symbol: Symbol, members: SymbolTable, callSignatures: Signature[], constructSignatures: Signature[], stringIndexType: Type, numberIndexType: Type): 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 (type).intrinsicName; } if (type.flags & TypeFlags.Reference) { return typeReferenceToString(type); } if (type.flags & (TypeFlags.Class | TypeFlags.Interface | TypeFlags.Enum | TypeFlags.TypeParameter)) { return symbolToString(type.symbol); } if (type.flags & TypeFlags.Anonymous) { return anonymousTypeToString(type); } if (type.flags & TypeFlags.StringLiteral) { return (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 || (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(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 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 = getDeclaredTypeOfSymbol(prototype.parent); return classType.typeParameters ? createTypeReference(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 = 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 = 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(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 = getDeclarationOfKind(symbol, SyntaxKind.GetAccessor); var setter = getDeclarationOfKind(symbol, SyntaxKind.SetAccessor); var type: Type; // First try to see if the user specified a return type on the get-accessor. var getterReturnType = getAnnotatedAccessorType(getter); if (getterReturnType) { type = getterReturnType; } else { // If the user didn't specify a return type, try to use the set-accessor's parameter type. var setterParameterType = getAnnotatedAccessorType(setter); if (setterParameterType) { type = setterParameterType; } else { // If there are no specified types, try to infer it from the body of the get accessor if it exists. if (getter) { 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 ? (type).target : type; } function hasBaseType(type: InterfaceType, checkBase: InterfaceType) { return check(type); function check(type: InterfaceType) { var target = getTargetType(type); return target === checkBase || forEach(target.baseTypes, check); } } // Return combined list of type parameters from all declarations of a class or interface. Elsewhere we check they're all // the same, but even if they're not we still need the complete list to ensure instantiations supply type arguments // for all type parameters. function getTypeParametersOfClassOrInterface(symbol: Symbol): TypeParameter[] { var result: TypeParameter[]; forEach(symbol.declarations, node => { if (node.kind === SyntaxKind.InterfaceDeclaration || node.kind === SyntaxKind.ClassDeclaration) { var declaration = node; if (declaration.typeParameters && declaration.typeParameters.length) { forEach(declaration.typeParameters, node => { var tp = getDeclaredTypeOfTypeParameter(getSymbolOfNode(node)); if (!result) { result = [tp]; } else if (!contains(result, tp)) { result.push(tp); } }); } } }); return result; } function getDeclaredTypeOfClass(symbol: Symbol): InterfaceType { var links = getSymbolLinks(symbol); if (!links.declaredType) { var type = links.declaredType = createObjectType(TypeFlags.Class, symbol); var typeParameters = getTypeParametersOfClassOrInterface(symbol); if (typeParameters) { type.flags |= TypeFlags.Reference; type.typeParameters = typeParameters; (type).instantiations = {}; (type).instantiations[getTypeListId(type.typeParameters)] = type; (type).target = type; (type).typeArguments = type.typeParameters; } type.baseTypes = []; var declaration = getDeclarationOfKind(symbol, SyntaxKind.ClassDeclaration); if (declaration.baseType) { var baseType = getTypeFromTypeReferenceNode(declaration.baseType); if (baseType !== unknownType) { if (getTargetType(baseType).flags & TypeFlags.Class) { if (type !== baseType && !hasBaseType(baseType, type)) { type.baseTypes.push(baseType); } else { error(declaration, Diagnostics.Type_0_recursively_references_itself_as_a_base_type, typeToString(type, /*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 links.declaredType; } function getDeclaredTypeOfInterface(symbol: Symbol): InterfaceType { var links = getSymbolLinks(symbol); if (!links.declaredType) { var type = links.declaredType = createObjectType(TypeFlags.Interface, symbol); var typeParameters = getTypeParametersOfClassOrInterface(symbol); if (typeParameters) { type.flags |= TypeFlags.Reference; type.typeParameters = typeParameters; (type).instantiations = {}; (type).instantiations[getTypeListId(type.typeParameters)] = type; (type).target = type; (type).typeArguments = type.typeParameters; } type.baseTypes = []; forEach(symbol.declarations, declaration => { if (declaration.kind === SyntaxKind.InterfaceDeclaration && (declaration).baseTypes) { forEach((declaration).baseTypes, node => { var baseType = getTypeFromTypeReferenceNode(node); if (baseType !== unknownType) { if (getTargetType(baseType).flags & (TypeFlags.Class | TypeFlags.Interface)) { if (type !== baseType && !hasBaseType(baseType, type)) { type.baseTypes.push(baseType); } else { error(declaration, Diagnostics.Type_0_recursively_references_itself_as_a_base_type, typeToString(type, /*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 links.declaredType; } function getDeclaredTypeOfEnum(symbol: Symbol): Type { var links = getSymbolLinks(symbol); if (!links.declaredType) { var type = createType(TypeFlags.Enum); type.symbol = symbol; links.declaredType = type; } return links.declaredType; } function getDeclaredTypeOfTypeParameter(symbol: Symbol): TypeParameter { var links = getSymbolLinks(symbol); if (!links.declaredType) { var type = createType(TypeFlags.TypeParameter); type.symbol = symbol; if (!(getDeclarationOfKind(symbol, SyntaxKind.TypeParameter)).constraint) { type.constraint = noConstraintType; } links.declaredType = type; } return links.declaredType; } function getDeclaredTypeOfImport(symbol: Symbol): Type { var links = getSymbolLinks(symbol); if (!links.declaredType) { links.declaredType = getDeclaredTypeOfSymbol(resolveImport(symbol)); } return links.declaredType; } function getDeclaredTypeOfSymbol(symbol: Symbol): Type { 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, (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 (!(type).members) { if (type.flags & (TypeFlags.Class | TypeFlags.Interface)) { resolveClassOrInterfaceMembers(type); } else if (type.flags & TypeFlags.Anonymous) { resolveAnonymousTypeMembers(type); } else { resolveTypeReferenceMembers(type); } } return type; } function getPropertiesOfType(type: Type): Symbol[] { if (type.flags & TypeFlags.ObjectType) { return resolveObjectTypeMembers(type).properties; } return emptyArray; } function getPropertyOfType(type: Type, name: string): Symbol { if (type.flags & TypeFlags.ObjectType) { var resolved = resolveObjectTypeMembers(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(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(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(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((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 = getDeclarationOfKind(declaration.symbol, SyntaxKind.SetAccessor); returnType = getAnnotatedAccessorType(setter); } if (!returnType && !(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 && (node).body) { var previous = symbol.declarations[i - 1]; if (node.parent === previous.parent && node.kind === previous.kind && node.pos === previous.end) { break; } } result.push(getSignatureFromDeclaration(node)); } } return result; } function getReturnTypeOfSignature(signature: Signature): Type { if (!signature.resolvedReturnType) { signature.resolvedReturnType = resolvingType; if (signature.target) { var type = instantiateType(getReturnTypeOfSignature(signature.target), signature.mapper); } else { var type = getReturnTypeFromBody(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 && (type).target === globalArrayType) { return (type).typeArguments[0]; } } return anyType; } function getSignatureInstantiation(signature: Signature, typeArguments: Type[]): Signature { return instantiateSignature(signature, createTypeMapper(signature.typeParameters, typeArguments), true); } function getErasedSignature(signature: Signature): Signature { if (!signature.typeParameters) return signature; if (!signature.erasedSignatureCache) { if (signature.target) { signature.erasedSignatureCache = instantiateSignature(getErasedSignature(signature.target), signature.mapper); } else { signature.erasedSignatureCache = instantiateSignature(signature, createTypeEraser(signature.typeParameters), true); } } return signature.erasedSignatureCache; } function getOrCreateTypeFromSignature(signature: Signature): ObjectType { // There are two ways to declare a construct signature, one is by declaring a class constructor // using the constructor keyword, and the other is declaring a bare construct signature in an // object type literal or interface (using the new keyword). Each way of declaring a constructor // will result in a different declaration kind. if (!signature.isolatedSignatureType) { var isConstructor = signature.declaration.kind === SyntaxKind.Constructor || signature.declaration.kind === SyntaxKind.ConstructSignature; var type = createObjectType(TypeFlags.Anonymous | TypeFlags.FromSignature); type.members = emptySymbols; type.properties = emptyArray; type.callSignatures = !isConstructor ? [signature] : emptyArray; type.constructSignatures = isConstructor ? [signature] : emptyArray; signature.isolatedSignatureType = type; } return signature.isolatedSignatureType; } function getIndexSymbol(symbol: Symbol): Symbol { return symbol.members["__index"]; } function getIndexDeclarationOfSymbol(symbol: Symbol, kind: IndexKind): SignatureDeclaration { var syntaxKind = kind === IndexKind.Number ? SyntaxKind.NumberKeyword : SyntaxKind.StringKeyword; var indexSymbol = getIndexSymbol(symbol); if (indexSymbol) { var len = indexSymbol.declarations.length; for (var i = 0; i < len; i++) { var node = indexSymbol.declarations[i]; if (node.parameters.length === 1) { var parameter = node.parameters[0]; if (parameter && parameter.type && parameter.type.kind === syntaxKind) { return node; } } } } return undefined; } function getIndexTypeOfSymbol(symbol: Symbol, kind: IndexKind): Type { var declaration = getIndexDeclarationOfSymbol(symbol, kind); return declaration ? declaration.type ? getTypeFromTypeNode(declaration.type) : anyType : undefined; } function getConstraintOfTypeParameter(type: TypeParameter): Type { if (!type.constraint) { if (type.target) { var targetConstraint = getConstraintOfTypeParameter(type.target); type.constraint = targetConstraint ? instantiateType(targetConstraint, type.mapper) : noConstraintType; } else { type.constraint = getTypeFromTypeNode((getDeclarationOfKind(type.symbol, SyntaxKind.TypeParameter)).constraint); } } return type.constraint === noConstraintType ? undefined : type.constraint; } function getTypeListId(types: Type[]) { switch (types.length) { case 1: return "" + types[0].id; case 2: return types[0].id + "," + types[1].id; default: var result = ""; for (var i = 0; i < types.length; i++) { if (i > 0) result += ","; result += types[i].id; } return result; } } function createTypeReference(target: GenericType, typeArguments: Type[]): TypeReference { var id = getTypeListId(typeArguments); var type = target.instantiations[id]; if (!type) { type = target.instantiations[id] = 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 && (n).typeName.kind === SyntaxKind.Identifier) { var links = getNodeLinks(n); if (links.isIllegalTypeReferenceInConstraint === undefined) { var symbol = resolveName(typeParameter, ((n).typeName).text, SymbolFlags.Type, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined); if (symbol && (symbol.flags & SymbolFlags.TypeParameter)) { // TypeScript 1.0 spec (April 2014): 3.4.1 // Type parameters declared in a particular type parameter list // may not be referenced in constraints in that type parameter list // symbol.declaration.parent === typeParameter.parent // -> typeParameter and symbol.declaration originate from the same type parameter list // -> illegal for all declarations in symbol // forEach === exists links.isIllegalTypeReferenceInConstraint = forEach(symbol.declarations, d => d.parent == typeParameter.parent); } } if (links.isIllegalTypeReferenceInConstraint) { error(typeParameter, Diagnostics.Constraint_of_a_type_parameter_cannot_reference_any_type_parameter_from_the_same_type_parameter_list); } } forEachChild(n, check); } if (typeParameter.constraint) { typeParameterSymbol = getSymbolOfNode(typeParameter); check(typeParameter.constraint); } } function getTypeFromTypeReferenceNode(node: TypeReferenceNode): Type { var links = getNodeLinks(node); if (!links.resolvedType) { var symbol = resolveEntityName(node, node.typeName, SymbolFlags.Type); if (symbol) { var type: Type; if ((symbol.flags & SymbolFlags.TypeParameter) && isTypeParameterReferenceIllegalInConstraint(node, symbol)) { // TypeScript 1.0 spec (April 2014): 3.4.1 // Type parameters declared in a particular type parameter list // may not be referenced in constraints in that type parameter list // Implementation: such type references are resolved to 'unknown' type that usually denotes error type = unknownType; } else { type = getDeclaredTypeOfSymbol(symbol); if (type.flags & (TypeFlags.Class | TypeFlags.Interface) && type.flags & TypeFlags.Reference) { var typeParameters = (type).typeParameters; if (node.typeArguments && node.typeArguments.length === typeParameters.length) { type = createTypeReference(type, map(node.typeArguments, 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 (((type).typeParameters ? (type).typeParameters.length : 0) !== arity) { error(getTypeDeclaration(symbol), Diagnostics.Global_type_0_must_have_1_type_parameter_s, name, arity); return emptyObjectType; } return type; } function createArrayType(elementType: Type): Type { return globalArrayType !== emptyObjectType ? createTypeReference(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] = 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(node); case SyntaxKind.TypeReference: return getTypeFromTypeReferenceNode(node); case SyntaxKind.TypeQuery: return getTypeFromTypeQueryNode(node); case SyntaxKind.ArrayType: return getTypeFromArrayTypeNode(node); case SyntaxKind.TypeLiteral: return getTypeFromTypeLiteralNode(node); default: return unknownType; } } function instantiateList(items: T[], mapper: TypeMapper, instantiator: (item: T, mapper: TypeMapper) => T): T[] { if (items && items.length) { var result: T[] = []; for (var i = 0; i < items.length; i++) { result.push(instantiator(items[i], mapper)); } return result; } return items; } function createUnaryTypeMapper(source: Type, target: Type): TypeMapper { return t => t === source ? target : t; } function createBinaryTypeMapper(source1: Type, target1: Type, source2: Type, target2: Type): TypeMapper { return t => t === source1 ? target1 : t === source2 ? target2 : t; } function createTypeMapper(sources: Type[], targets: Type[]): TypeMapper { switch (sources.length) { case 1: return createUnaryTypeMapper(sources[0], targets[0]); case 2: return createBinaryTypeMapper(sources[0], targets[0], sources[1], targets[1]); } return t => { for (var i = 0; i < sources.length; i++) { if (t === sources[i]) return targets[i]; } return t; }; } function createUnaryTypeEraser(source: Type): TypeMapper { return t => t === source ? anyType : t; } function createBinaryTypeEraser(source1: Type, source2: Type): TypeMapper { return t => t === source1 || t === source2 ? anyType : t; } function createTypeEraser(sources: Type[]): TypeMapper { switch (sources.length) { case 1: return createUnaryTypeEraser(sources[0]); case 2: return createBinaryTypeEraser(sources[0], sources[1]); } return t => { for (var i = 0; i < sources.length; i++) { if (t === sources[i]) return anyType; } return t; }; } function createInferenceMapper(context: InferenceContext): TypeMapper { return t => { for (var i = 0; i < context.typeParameters.length; i++) { if (t === context.typeParameters[i]) { return getInferredType(context, i); } } return t; } } function identityMapper(type: Type): Type { return type; } function combineTypeMappers(mapper1: TypeMapper, mapper2: TypeMapper): TypeMapper { return t => mapper2(mapper1(t)); } function instantiateTypeParameter(typeParameter: TypeParameter, mapper: TypeMapper): TypeParameter { var result = createType(TypeFlags.TypeParameter); result.symbol = typeParameter.symbol; if (typeParameter.constraint) { result.constraint = instantiateType(typeParameter.constraint, mapper); } else { result.target = typeParameter; result.mapper = mapper; } return result; } function instantiateSignature(signature: Signature, mapper: TypeMapper, eraseTypeParameters?: boolean): Signature { if (signature.typeParameters && !eraseTypeParameters) { var freshTypeParameters = instantiateList(signature.typeParameters, mapper, instantiateTypeParameter); mapper = combineTypeMappers(createTypeMapper(signature.typeParameters, freshTypeParameters), mapper); } var result = createSignature(signature.declaration, freshTypeParameters, instantiateList(signature.parameters, mapper, instantiateSymbol), signature.resolvedReturnType ? instantiateType(signature.resolvedReturnType, mapper) : undefined, signature.minArgumentCount, signature.hasRestParameter, signature.hasStringLiterals); result.target = signature; result.mapper = mapper; return result; } function instantiateSymbol(symbol: Symbol, mapper: TypeMapper): Symbol { if (symbol.flags & SymbolFlags.Instantiated) { var links = getSymbolLinks(symbol); // If symbol being instantiated is itself a instantiation, fetch the original target and combine the // type mappers. This ensures that original type identities are properly preserved and that aliases // always reference a non-aliases. symbol = links.target; mapper = combineTypeMappers(links.mapper, mapper); } // Keep the flags from the symbol we're instantiating. Mark that is instantiated, and // also transient so that we can just store data on it directly. var result = createSymbol(SymbolFlags.Instantiated | SymbolFlags.Transient, symbol.name); result.declarations = symbol.declarations; result.parent = symbol.parent; result.target = symbol; result.mapper = mapper; if (symbol.valueDeclaration) { result.valueDeclaration = symbol.valueDeclaration; } return result; } function instantiateAnonymousType(type: ObjectType, mapper: TypeMapper): ObjectType { var result = createObjectType(TypeFlags.Anonymous, type.symbol); result.properties = instantiateList(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(type, mapper) : type; } if (type.flags & TypeFlags.Reference) { return createTypeReference((type).target, instantiateList((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 !(node).typeParameters && !forEach((node).parameters, p => p.type); case SyntaxKind.ObjectLiteral: return forEach((node).properties, p => p.kind === SyntaxKind.PropertyAssignment && isContextSensitiveExpression((p).initializer)); case SyntaxKind.ArrayLiteral: return forEach((node).elements, e => isContextSensitiveExpression(e)); case SyntaxKind.ConditionalExpression: return isContextSensitiveExpression((node).whenTrue) || isContextSensitiveExpression((node).whenFalse); case SyntaxKind.BinaryExpression: return (node).operator === SyntaxKind.BarBarToken && (isContextSensitiveExpression((node).left) || isContextSensitiveExpression((node).right)); } return false; } function getTypeWithoutConstructors(type: Type): Type { if (type.flags & TypeFlags.ObjectType) { var resolved = resolveObjectTypeMembers(type); if (resolved.constructSignatures.length) { var result = createObjectType(TypeFlags.Anonymous, type.symbol); result.members = resolved.members; result.properties = resolved.properties; result.callSignatures = resolved.callSignatures; result.constructSignatures = emptyArray; type = result; } } return type; } // TYPE CHECKING var subtypeRelation: Map = {}; var assignableRelation: Map = {}; var identityRelation: Map = {}; function isTypeIdenticalTo(source: Type, target: Type): boolean { return checkTypeRelatedTo(source, target, identityRelation, /*errorNode*/ undefined, /*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 { 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, 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(source, target, reportErrors)) { return true; } } else { var saveErrorInfo = errorInfo; if (source.flags & TypeFlags.Reference && target.flags & TypeFlags.Reference && (source).target === (target).target) { // We have type references to same target type, see if relationship holds for all type arguments if (typesRelatedTo((source).typeArguments, (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, 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 = 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 = (type).target; var count = 0; for (var i = 0; i < depth; i++) { var t = stack[i]; if (t.flags & TypeFlags.Reference && (t).target === target) { count++; if (count >= 10) return true; } } } return false; } function propertiesRelatedTo(source: ObjectType, target: ObjectType, reportErrors: boolean): boolean { if (relation === identityRelation) { return propertiesAreIdenticalTo(source, target, reportErrors); } else { return propertiesAreSubtypeOrAssignableTo(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 = 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 && (type).target === globalArrayType; } function getInnermostTypeOfNestedArrayTypes(type: Type): Type { while (isArrayType(type)) { type = (type).typeArguments[0]; } return type; } function getWidenedTypeOfArrayLiteral(type: Type): Type { var elementType = (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 = (type).target; var count = 0; for (var i = 0; i < depth; i++) { var t = stack[i]; if (t.flags & TypeFlags.Reference && (t).target === target) count++; } return count < 5; } return true; } function inferFromTypes(source: Type, target: Type) { if (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 && (source).target === (target).target) { // If source and target are references to the same generic type, infer from type arguments var sourceTypes = (source).typeArguments; var targetTypes = (target).typeArguments; for (var i = 0; i < sourceTypes.length; i++) { inferFromTypes(sourceTypes[i], targetTypes[i]); } } else if (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 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 = getAncestor(node, SyntaxKind.ClassDeclaration); var baseClass: Type; if (enclosingClass && enclosingClass.baseType) { var classType = 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(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((member.declarations[0]).initializer, contextualPropType, contextualMapper); var prop = 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 = getDeclarationOfKind(member, SyntaxKind.GetAccessor); if (getAccessor) { checkAccessorDeclaration(getAccessor); } var setAccessor = 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 (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 (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 = (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, 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(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 (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’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’s apparent type(section 3.8.1) is an object type with one or // more construct signatures, the expression is processed in the same manner as a // function call, but using the construct signatures as the initial set of candidate // signatures for overload resolution.The result type of the function call becomes // the result type of the operation. expressionType = getApparentType(expressionType); if (expressionType === unknownType) { // 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’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 = (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((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(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(node); case SyntaxKind.ArrayLiteral: return checkArrayLiteral(node, contextualType, contextualMapper); case SyntaxKind.ObjectLiteral: return checkObjectLiteral(node, contextualType, contextualMapper); case SyntaxKind.PropertyAccess: return checkPropertyAccess(node); case SyntaxKind.IndexedAccess: return checkIndexedAccess(node); case SyntaxKind.CallExpression: return checkCallExpression(node); case SyntaxKind.NewExpression: return checkNewExpression(node); case SyntaxKind.TypeAssertion: return checkTypeAssertion(node); case SyntaxKind.ParenExpression: return checkExpression((node).expression); case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: return checkFunctionExpression(node, contextualType, contextualMapper); case SyntaxKind.PrefixOperator: return checkPrefixExpression(node); case SyntaxKind.PostfixOperator: return checkPostfixExpression(node); case SyntaxKind.BinaryExpression: return checkBinaryExpression(node, contextualType, contextualMapper); case SyntaxKind.ConditionalExpression: return checkConditionalExpression(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 && (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 && !(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((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(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 = 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 && (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) && !!(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 ((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((node.parent).members, isInstancePropertyWithInitializer) || forEach(node.parameters, p => p.flags & (NodeFlags.Public | NodeFlags.Private)); if (superCallShouldBeFirst) { var statements = (node.body).statements; if (!statements.length || statements[0].kind !== SyntaxKind.ExpressionStatement || !isSuperCallExpression((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((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 = 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 = 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((type).target.typeParameters[i]); if (constraint) { var typeArgument = (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 ((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 = 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) || !(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 ((node.parent).body && hasRestParameters(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(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 && !(node.parent).body) { // just an overload - no codegen impact return; } // bubble up and find containing type var enclosingClass = 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 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(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((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((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 = getDeclaredTypeOfSymbol(symbol); var staticType = 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) ? (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 = 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 = 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 = 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 = (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 && (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(node); case SyntaxKind.Parameter: return checkParameter(node); case SyntaxKind.Property: return checkPropertyDeclaration(node); case SyntaxKind.CallSignature: case SyntaxKind.ConstructSignature: case SyntaxKind.IndexSignature: return checkSignatureDeclaration(node); case SyntaxKind.Method: return checkMethodDeclaration(node); case SyntaxKind.Constructor: return checkConstructorDeclaration(node); case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: return checkAccessorDeclaration(node); case SyntaxKind.TypeReference: return checkTypeReference(node); case SyntaxKind.TypeQuery: return checkTypeQuery(node); case SyntaxKind.TypeLiteral: return checkTypeLiteral(node); case SyntaxKind.ArrayType: return checkArrayType(node); case SyntaxKind.FunctionDeclaration: return checkFunctionDeclaration(node); case SyntaxKind.Block: case SyntaxKind.FunctionBlock: case SyntaxKind.ModuleBlock: return checkBlock(node); case SyntaxKind.VariableStatement: return checkVariableStatement(node); case SyntaxKind.ExpressionStatement: return checkExpressionStatement(node); case SyntaxKind.IfStatement: return checkIfStatement(node); case SyntaxKind.DoStatement: return checkDoStatement(node); case SyntaxKind.WhileStatement: return checkWhileStatement(node); case SyntaxKind.ForStatement: return checkForStatement(node); case SyntaxKind.ForInStatement: return checkForInStatement(node); case SyntaxKind.ContinueStatement: case SyntaxKind.BreakStatement: return checkBreakOrContinueStatement(node); case SyntaxKind.ReturnStatement: return checkReturnStatement(node); case SyntaxKind.WithStatement: return checkWithStatement(node); case SyntaxKind.SwitchStatement: return checkSwitchStatement(node); case SyntaxKind.LabelledStatement: return checkLabelledStatement(node); case SyntaxKind.ThrowStatement: return checkThrowStatement(node); case SyntaxKind.TryStatement: return checkTryStatement(node); case SyntaxKind.VariableDeclaration: return Debug.fail("Checker encountered variable declaration"); case SyntaxKind.ClassDeclaration: return checkClassDeclaration(node); case SyntaxKind.InterfaceDeclaration: return checkInterfaceDeclaration(node); case SyntaxKind.EnumDeclaration: return checkEnumDeclaration(node); case SyntaxKind.ModuleDeclaration: return checkModuleDeclaration(node); case SyntaxKind.ImportDeclaration: return checkImportDeclaration(node); case SyntaxKind.ExportAssignment: return checkExportAssignment(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 ((location).name) { copySymbol(location.symbol, meaning); } break; case SyntaxKind.CatchBlock: if ((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 (identifier.parent).name === identifier; case SyntaxKind.CatchBlock: return (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 (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 (parent).expression === node; case SyntaxKind.ForStatement: return (parent).initializer === node || (parent).condition === node || (parent).iterator === node; case SyntaxKind.ForInStatement: return (parent).variable === node || (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) && (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; } }