TypeScript/src/compiler/checker.ts
Nathan Shively-Sanders f9a05a1f9d Re-enable weak type check for intersection props
Previously, intersections disabled the weak type check for their
constituents, and all properties (recursively) of their constituents.

Also add test for this case.
2017-05-26 13:39:53 -07:00

24656 lines
1.3 MiB

/// <reference path="moduleNameResolver.ts"/>
/// <reference path="binder.ts"/>
/* @internal */
namespace ts {
const ambientModuleSymbolRegex = /^".+"$/;
let nextSymbolId = 1;
let nextNodeId = 1;
let nextMergeId = 1;
let nextFlowId = 1;
export function getNodeId(node: Node): number {
if (!node.id) {
node.id = nextNodeId;
nextNodeId++;
}
return node.id;
}
export function getSymbolId(symbol: Symbol): number {
if (!symbol.id) {
symbol.id = nextSymbolId;
nextSymbolId++;
}
return symbol.id;
}
export function createTypeChecker(host: TypeCheckerHost, produceDiagnostics: boolean): TypeChecker {
// Cancellation that controls whether or not we can cancel in the middle of type checking.
// In general cancelling is *not* safe for the type checker. We might be in the middle of
// computing something, and we will leave our internals in an inconsistent state. Callers
// who set the cancellation token should catch if a cancellation exception occurs, and
// should throw away and create a new TypeChecker.
//
// Currently we only support setting the cancellation token when getting diagnostics. This
// is because diagnostics can be quite expensive, and we want to allow hosts to bail out if
// they no longer need the information (for example, if the user started editing again).
let cancellationToken: CancellationToken;
let requestedExternalEmitHelpers: ExternalEmitHelpers;
let externalHelpersModule: Symbol;
const Symbol = objectAllocator.getSymbolConstructor();
const Type = objectAllocator.getTypeConstructor();
const Signature = objectAllocator.getSignatureConstructor();
let typeCount = 0;
let symbolCount = 0;
let enumCount = 0;
let symbolInstantiationDepth = 0;
const emptyArray: any[] = [];
const emptySymbols = createMap<Symbol>();
const compilerOptions = host.getCompilerOptions();
const languageVersion = getEmitScriptTarget(compilerOptions);
const modulekind = getEmitModuleKind(compilerOptions);
const noUnusedIdentifiers = !!compilerOptions.noUnusedLocals || !!compilerOptions.noUnusedParameters;
const allowSyntheticDefaultImports = typeof compilerOptions.allowSyntheticDefaultImports !== "undefined" ? compilerOptions.allowSyntheticDefaultImports : modulekind === ModuleKind.System;
const strictNullChecks = compilerOptions.strictNullChecks === undefined ? compilerOptions.strict : compilerOptions.strictNullChecks;
const noImplicitAny = compilerOptions.noImplicitAny === undefined ? compilerOptions.strict : compilerOptions.noImplicitAny;
const noImplicitThis = compilerOptions.noImplicitThis === undefined ? compilerOptions.strict : compilerOptions.noImplicitThis;
const emitResolver = createResolver();
const nodeBuilder = createNodeBuilder();
const undefinedSymbol = createSymbol(SymbolFlags.Property, "undefined");
undefinedSymbol.declarations = [];
const argumentsSymbol = createSymbol(SymbolFlags.Property, "arguments");
// for public members that accept a Node or one of its subtypes, we must guard against
// synthetic nodes created during transformations by calling `getParseTreeNode`.
// for most of these, we perform the guard only on `checker` to avoid any possible
// extra cost of calling `getParseTreeNode` when calling these functions from inside the
// checker.
const checker: TypeChecker = {
getNodeCount: () => sum(host.getSourceFiles(), "nodeCount"),
getIdentifierCount: () => sum(host.getSourceFiles(), "identifierCount"),
getSymbolCount: () => sum(host.getSourceFiles(), "symbolCount") + symbolCount,
getTypeCount: () => typeCount,
isUndefinedSymbol: symbol => symbol === undefinedSymbol,
isArgumentsSymbol: symbol => symbol === argumentsSymbol,
isUnknownSymbol: symbol => symbol === unknownSymbol,
getMergedSymbol,
getDiagnostics,
getGlobalDiagnostics,
getTypeOfSymbolAtLocation: (symbol, location) => {
location = getParseTreeNode(location);
return location ? getTypeOfSymbolAtLocation(symbol, location) : unknownType;
},
getSymbolsOfParameterPropertyDeclaration: (parameter, parameterName) => {
parameter = getParseTreeNode(parameter, isParameter);
Debug.assert(parameter !== undefined, "Cannot get symbols of a synthetic parameter that cannot be resolved to a parse-tree node.");
return getSymbolsOfParameterPropertyDeclaration(parameter, parameterName);
},
getDeclaredTypeOfSymbol,
getPropertiesOfType,
getPropertyOfType,
getIndexInfoOfType,
getSignaturesOfType,
getIndexTypeOfType,
getBaseTypes,
getBaseTypeOfLiteralType,
getWidenedType,
getTypeFromTypeNode: node => {
node = getParseTreeNode(node, isTypeNode);
return node ? getTypeFromTypeNode(node) : unknownType;
},
getParameterType: getTypeAtPosition,
getReturnTypeOfSignature,
getNonNullableType,
typeToTypeNode: nodeBuilder.typeToTypeNode,
indexInfoToIndexSignatureDeclaration: nodeBuilder.indexInfoToIndexSignatureDeclaration,
signatureToSignatureDeclaration: nodeBuilder.signatureToSignatureDeclaration,
getSymbolsInScope: (location, meaning) => {
location = getParseTreeNode(location);
return location ? getSymbolsInScope(location, meaning) : [];
},
getSymbolAtLocation: node => {
node = getParseTreeNode(node);
return node ? getSymbolAtLocation(node) : undefined;
},
getShorthandAssignmentValueSymbol: node => {
node = getParseTreeNode(node);
return node ? getShorthandAssignmentValueSymbol(node) : undefined;
},
getExportSpecifierLocalTargetSymbol: node => {
node = getParseTreeNode(node, isExportSpecifier);
return node ? getExportSpecifierLocalTargetSymbol(node) : undefined;
},
getTypeAtLocation: node => {
node = getParseTreeNode(node);
return node ? getTypeOfNode(node) : unknownType;
},
getPropertySymbolOfDestructuringAssignment: location => {
location = getParseTreeNode(location, isIdentifier);
return location ? getPropertySymbolOfDestructuringAssignment(location) : undefined;
},
signatureToString: (signature, enclosingDeclaration?, flags?, kind?) => {
return signatureToString(signature, getParseTreeNode(enclosingDeclaration), flags, kind);
},
typeToString: (type, enclosingDeclaration?, flags?) => {
return typeToString(type, getParseTreeNode(enclosingDeclaration), flags);
},
getSymbolDisplayBuilder,
symbolToString: (symbol, enclosingDeclaration?, meaning?) => {
return symbolToString(symbol, getParseTreeNode(enclosingDeclaration), meaning);
},
getAugmentedPropertiesOfType,
getRootSymbols,
getContextualType: node => {
node = getParseTreeNode(node, isExpression);
return node ? getContextualType(node) : undefined;
},
getFullyQualifiedName,
getResolvedSignature: (node, candidatesOutArray?) => {
node = getParseTreeNode(node, isCallLikeExpression);
return node ? getResolvedSignature(node, candidatesOutArray) : undefined;
},
getConstantValue: node => {
node = getParseTreeNode(node, canHaveConstantValue);
return node ? getConstantValue(node) : undefined;
},
isValidPropertyAccess: (node, propertyName) => {
node = getParseTreeNode(node, isPropertyAccessOrQualifiedName);
return node ? isValidPropertyAccess(node, propertyName) : false;
},
getSignatureFromDeclaration: declaration => {
declaration = getParseTreeNode(declaration, isFunctionLike);
return declaration ? getSignatureFromDeclaration(declaration) : undefined;
},
isImplementationOfOverload: node => {
node = getParseTreeNode(node, isFunctionLike);
return node ? isImplementationOfOverload(node) : undefined;
},
getImmediateAliasedSymbol: symbol => {
Debug.assert((symbol.flags & SymbolFlags.Alias) !== 0, "Should only get Alias here.");
const links = getSymbolLinks(symbol);
if (!links.immediateTarget) {
const node = getDeclarationOfAliasSymbol(symbol);
Debug.assert(!!node);
links.immediateTarget = getTargetOfAliasDeclaration(node, /*dontRecursivelyResolve*/ true);
}
return links.immediateTarget;
},
getAliasedSymbol: resolveAlias,
getEmitResolver,
getExportsOfModule: getExportsOfModuleAsArray,
getExportsAndPropertiesOfModule,
getAmbientModules,
getAllAttributesTypeFromJsxOpeningLikeElement: node => {
node = getParseTreeNode(node, isJsxOpeningLikeElement);
return node ? getAllAttributesTypeFromJsxOpeningLikeElement(node) : undefined;
},
getJsxIntrinsicTagNames,
isOptionalParameter: node => {
node = getParseTreeNode(node, isParameter);
return node ? isOptionalParameter(node) : false;
},
tryGetMemberInModuleExports,
tryFindAmbientModuleWithoutAugmentations: moduleName => {
// we deliberately exclude augmentations
// since we are only interested in declarations of the module itself
return tryFindAmbientModule(moduleName, /*withAugmentations*/ false);
},
getApparentType,
getAllPossiblePropertiesOfType,
getSuggestionForNonexistentProperty,
getSuggestionForNonexistentSymbol,
getBaseConstraintOfType,
};
const tupleTypes: GenericType[] = [];
const unionTypes = createMap<UnionType>();
const intersectionTypes = createMap<IntersectionType>();
const literalTypes = createMap<LiteralType>();
const indexedAccessTypes = createMap<IndexedAccessType>();
const evolvingArrayTypes: EvolvingArrayType[] = [];
const unknownSymbol = createSymbol(SymbolFlags.Property, "unknown");
const resolvingSymbol = createSymbol(0, "__resolving__");
const anyType = createIntrinsicType(TypeFlags.Any, "any");
const autoType = createIntrinsicType(TypeFlags.Any, "any");
const unknownType = createIntrinsicType(TypeFlags.Any, "unknown");
const undefinedType = createIntrinsicType(TypeFlags.Undefined, "undefined");
const undefinedWideningType = strictNullChecks ? undefinedType : createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsWideningType, "undefined");
const nullType = createIntrinsicType(TypeFlags.Null, "null");
const nullWideningType = strictNullChecks ? nullType : createIntrinsicType(TypeFlags.Null | TypeFlags.ContainsWideningType, "null");
const stringType = createIntrinsicType(TypeFlags.String, "string");
const numberType = createIntrinsicType(TypeFlags.Number, "number");
const trueType = createIntrinsicType(TypeFlags.BooleanLiteral, "true");
const falseType = createIntrinsicType(TypeFlags.BooleanLiteral, "false");
const booleanType = createBooleanType([trueType, falseType]);
const esSymbolType = createIntrinsicType(TypeFlags.ESSymbol, "symbol");
const voidType = createIntrinsicType(TypeFlags.Void, "void");
const neverType = createIntrinsicType(TypeFlags.Never, "never");
const silentNeverType = createIntrinsicType(TypeFlags.Never, "never");
const nonPrimitiveType = createIntrinsicType(TypeFlags.NonPrimitive, "object");
const emptyObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
const emptyTypeLiteralSymbol = createSymbol(SymbolFlags.TypeLiteral, "__type");
emptyTypeLiteralSymbol.members = createMap<Symbol>();
const emptyTypeLiteralType = createAnonymousType(emptyTypeLiteralSymbol, emptySymbols, emptyArray, emptyArray, undefined, undefined);
const emptyGenericType = <GenericType><ObjectType>createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
emptyGenericType.instantiations = createMap<TypeReference>();
const anyFunctionType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
// The anyFunctionType contains the anyFunctionType by definition. The flag is further propagated
// in getPropagatingFlagsOfTypes, and it is checked in inferFromTypes.
anyFunctionType.flags |= TypeFlags.ContainsAnyFunctionType;
const noConstraintType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
const circularConstraintType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
const anySignature = createSignature(undefined, undefined, undefined, emptyArray, anyType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false);
const unknownSignature = createSignature(undefined, undefined, undefined, emptyArray, unknownType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false);
const resolvingSignature = createSignature(undefined, undefined, undefined, emptyArray, anyType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false);
const silentNeverSignature = createSignature(undefined, undefined, undefined, emptyArray, silentNeverType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false);
const enumNumberIndexInfo = createIndexInfo(stringType, /*isReadonly*/ true);
const jsObjectLiteralIndexInfo = createIndexInfo(anyType, /*isReadonly*/ false);
const globals = createMap<Symbol>();
/**
* List of every ambient module with a "*" wildcard.
* Unlike other ambient modules, these can't be stored in `globals` because symbol tables only deal with exact matches.
* This is only used if there is no exact match.
*/
let patternAmbientModules: PatternAmbientModule[];
let globalObjectType: ObjectType;
let globalFunctionType: ObjectType;
let globalArrayType: GenericType;
let globalReadonlyArrayType: GenericType;
let globalStringType: ObjectType;
let globalNumberType: ObjectType;
let globalBooleanType: ObjectType;
let globalRegExpType: ObjectType;
let globalThisType: GenericType;
let anyArrayType: Type;
let autoArrayType: Type;
let anyReadonlyArrayType: Type;
// The library files are only loaded when the feature is used.
// This allows users to just specify library files they want to used through --lib
// and they will not get an error from not having unrelated library files
let deferredGlobalESSymbolConstructorSymbol: Symbol;
let deferredGlobalESSymbolType: ObjectType;
let deferredGlobalTypedPropertyDescriptorType: GenericType;
let deferredGlobalPromiseType: GenericType;
let deferredGlobalPromiseConstructorSymbol: Symbol;
let deferredGlobalPromiseConstructorLikeType: ObjectType;
let deferredGlobalIterableType: GenericType;
let deferredGlobalIteratorType: GenericType;
let deferredGlobalIterableIteratorType: GenericType;
let deferredGlobalAsyncIterableType: GenericType;
let deferredGlobalAsyncIteratorType: GenericType;
let deferredGlobalAsyncIterableIteratorType: GenericType;
let deferredGlobalTemplateStringsArrayType: ObjectType;
let deferredJsxElementClassType: Type;
let deferredJsxElementType: Type;
let deferredJsxStatelessElementType: Type;
let deferredNodes: Node[];
let deferredUnusedIdentifierNodes: Node[];
let flowLoopStart = 0;
let flowLoopCount = 0;
let visitedFlowCount = 0;
const emptyStringType = getLiteralType("");
const zeroType = getLiteralType(0);
const resolutionTargets: TypeSystemEntity[] = [];
const resolutionResults: boolean[] = [];
const resolutionPropertyNames: TypeSystemPropertyName[] = [];
let suggestionCount = 0;
const maximumSuggestionCount = 10;
const mergedSymbols: Symbol[] = [];
const symbolLinks: SymbolLinks[] = [];
const nodeLinks: NodeLinks[] = [];
const flowLoopCaches: Map<Type>[] = [];
const flowLoopNodes: FlowNode[] = [];
const flowLoopKeys: string[] = [];
const flowLoopTypes: Type[][] = [];
const visitedFlowNodes: FlowNode[] = [];
const visitedFlowTypes: FlowType[] = [];
const potentialThisCollisions: Node[] = [];
const potentialNewTargetCollisions: Node[] = [];
const awaitedTypeStack: number[] = [];
const diagnostics = createDiagnosticCollection();
const enum TypeFacts {
None = 0,
TypeofEQString = 1 << 0, // typeof x === "string"
TypeofEQNumber = 1 << 1, // typeof x === "number"
TypeofEQBoolean = 1 << 2, // typeof x === "boolean"
TypeofEQSymbol = 1 << 3, // typeof x === "symbol"
TypeofEQObject = 1 << 4, // typeof x === "object"
TypeofEQFunction = 1 << 5, // typeof x === "function"
TypeofEQHostObject = 1 << 6, // typeof x === "xxx"
TypeofNEString = 1 << 7, // typeof x !== "string"
TypeofNENumber = 1 << 8, // typeof x !== "number"
TypeofNEBoolean = 1 << 9, // typeof x !== "boolean"
TypeofNESymbol = 1 << 10, // typeof x !== "symbol"
TypeofNEObject = 1 << 11, // typeof x !== "object"
TypeofNEFunction = 1 << 12, // typeof x !== "function"
TypeofNEHostObject = 1 << 13, // typeof x !== "xxx"
EQUndefined = 1 << 14, // x === undefined
EQNull = 1 << 15, // x === null
EQUndefinedOrNull = 1 << 16, // x === undefined / x === null
NEUndefined = 1 << 17, // x !== undefined
NENull = 1 << 18, // x !== null
NEUndefinedOrNull = 1 << 19, // x != undefined / x != null
Truthy = 1 << 20, // x
Falsy = 1 << 21, // !x
Discriminatable = 1 << 22, // May have discriminant property
All = (1 << 23) - 1,
// The following members encode facts about particular kinds of types for use in the getTypeFacts function.
// The presence of a particular fact means that the given test is true for some (and possibly all) values
// of that kind of type.
BaseStringStrictFacts = TypeofEQString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull,
BaseStringFacts = BaseStringStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy,
StringStrictFacts = BaseStringStrictFacts | Truthy | Falsy,
StringFacts = BaseStringFacts | Truthy,
EmptyStringStrictFacts = BaseStringStrictFacts | Falsy,
EmptyStringFacts = BaseStringFacts,
NonEmptyStringStrictFacts = BaseStringStrictFacts | Truthy,
NonEmptyStringFacts = BaseStringFacts | Truthy,
BaseNumberStrictFacts = TypeofEQNumber | TypeofNEString | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull,
BaseNumberFacts = BaseNumberStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy,
NumberStrictFacts = BaseNumberStrictFacts | Truthy | Falsy,
NumberFacts = BaseNumberFacts | Truthy,
ZeroStrictFacts = BaseNumberStrictFacts | Falsy,
ZeroFacts = BaseNumberFacts,
NonZeroStrictFacts = BaseNumberStrictFacts | Truthy,
NonZeroFacts = BaseNumberFacts | Truthy,
BaseBooleanStrictFacts = TypeofEQBoolean | TypeofNEString | TypeofNENumber | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull,
BaseBooleanFacts = BaseBooleanStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy,
BooleanStrictFacts = BaseBooleanStrictFacts | Truthy | Falsy,
BooleanFacts = BaseBooleanFacts | Truthy,
FalseStrictFacts = BaseBooleanStrictFacts | Falsy,
FalseFacts = BaseBooleanFacts,
TrueStrictFacts = BaseBooleanStrictFacts | Truthy,
TrueFacts = BaseBooleanFacts | Truthy,
SymbolStrictFacts = TypeofEQSymbol | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull | Truthy,
SymbolFacts = SymbolStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy,
ObjectStrictFacts = TypeofEQObject | TypeofEQHostObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | NEUndefined | NENull | NEUndefinedOrNull | Truthy | Discriminatable,
ObjectFacts = ObjectStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy,
FunctionStrictFacts = TypeofEQFunction | TypeofEQHostObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | NEUndefined | NENull | NEUndefinedOrNull | Truthy | Discriminatable,
FunctionFacts = FunctionStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy,
UndefinedFacts = TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | EQUndefined | EQUndefinedOrNull | NENull | Falsy,
NullFacts = TypeofEQObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | TypeofNEHostObject | EQNull | EQUndefinedOrNull | NEUndefined | Falsy,
}
const typeofEQFacts = createMapFromTemplate({
"string": TypeFacts.TypeofEQString,
"number": TypeFacts.TypeofEQNumber,
"boolean": TypeFacts.TypeofEQBoolean,
"symbol": TypeFacts.TypeofEQSymbol,
"undefined": TypeFacts.EQUndefined,
"object": TypeFacts.TypeofEQObject,
"function": TypeFacts.TypeofEQFunction
});
const typeofNEFacts = createMapFromTemplate({
"string": TypeFacts.TypeofNEString,
"number": TypeFacts.TypeofNENumber,
"boolean": TypeFacts.TypeofNEBoolean,
"symbol": TypeFacts.TypeofNESymbol,
"undefined": TypeFacts.NEUndefined,
"object": TypeFacts.TypeofNEObject,
"function": TypeFacts.TypeofNEFunction
});
const typeofTypesByName = createMapFromTemplate<Type>({
"string": stringType,
"number": numberType,
"boolean": booleanType,
"symbol": esSymbolType,
"undefined": undefinedType
});
const typeofType = createTypeofType();
let _jsxNamespace: string;
let _jsxFactoryEntity: EntityName;
let _jsxElementPropertiesName: string;
let _hasComputedJsxElementPropertiesName = false;
let _jsxElementChildrenPropertyName: string;
let _hasComputedJsxElementChildrenPropertyName = false;
/** Things we lazy load from the JSX namespace */
const jsxTypes = createMap<Type>();
const JsxNames = {
JSX: "JSX",
IntrinsicElements: "IntrinsicElements",
ElementClass: "ElementClass",
ElementAttributesPropertyNameContainer: "ElementAttributesProperty",
ElementChildrenAttributeNameContainer: "ElementChildrenAttribute",
Element: "Element",
IntrinsicAttributes: "IntrinsicAttributes",
IntrinsicClassAttributes: "IntrinsicClassAttributes"
};
const subtypeRelation = createMap<RelationComparisonResult>();
const assignableRelation = createMap<RelationComparisonResult>();
const comparableRelation = createMap<RelationComparisonResult>();
const identityRelation = createMap<RelationComparisonResult>();
const enumRelation = createMap<boolean>();
// This is for caching the result of getSymbolDisplayBuilder. Do not access directly.
let _displayBuilder: SymbolDisplayBuilder;
type TypeSystemEntity = Symbol | Type | Signature;
const enum TypeSystemPropertyName {
Type,
ResolvedBaseConstructorType,
DeclaredType,
ResolvedReturnType
}
const enum CheckMode {
Normal = 0, // Normal type checking
SkipContextSensitive = 1, // Skip context sensitive function expressions
Inferential = 2, // Inferential typing
}
const builtinGlobals = createMap<Symbol>();
builtinGlobals.set(undefinedSymbol.name, undefinedSymbol);
initializeTypeChecker();
return checker;
function getJsxNamespace(): string {
if (!_jsxNamespace) {
_jsxNamespace = "React";
if (compilerOptions.jsxFactory) {
_jsxFactoryEntity = parseIsolatedEntityName(compilerOptions.jsxFactory, languageVersion);
if (_jsxFactoryEntity) {
_jsxNamespace = getFirstIdentifier(_jsxFactoryEntity).text;
}
}
else if (compilerOptions.reactNamespace) {
_jsxNamespace = compilerOptions.reactNamespace;
}
}
return _jsxNamespace;
}
function getEmitResolver(sourceFile: SourceFile, cancellationToken: CancellationToken) {
// Ensure we have all the type information in place for this file so that all the
// emitter questions of this resolver will return the right information.
getDiagnostics(sourceFile, cancellationToken);
return emitResolver;
}
function error(location: Node, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number): void {
const diagnostic = location
? createDiagnosticForNode(location, message, arg0, arg1, arg2)
: createCompilerDiagnostic(message, arg0, arg1, arg2);
diagnostics.add(diagnostic);
}
function createSymbol(flags: SymbolFlags, name: string) {
symbolCount++;
const symbol = <TransientSymbol>(new Symbol(flags | SymbolFlags.Transient, name));
symbol.checkFlags = 0;
return symbol;
}
function isTransientSymbol(symbol: Symbol): symbol is TransientSymbol {
return (symbol.flags & SymbolFlags.Transient) !== 0;
}
function getExcludedSymbolFlags(flags: SymbolFlags): SymbolFlags {
let result: SymbolFlags = 0;
if (flags & SymbolFlags.BlockScopedVariable) result |= SymbolFlags.BlockScopedVariableExcludes;
if (flags & SymbolFlags.FunctionScopedVariable) result |= SymbolFlags.FunctionScopedVariableExcludes;
if (flags & SymbolFlags.Property) result |= SymbolFlags.PropertyExcludes;
if (flags & SymbolFlags.EnumMember) result |= SymbolFlags.EnumMemberExcludes;
if (flags & SymbolFlags.Function) result |= SymbolFlags.FunctionExcludes;
if (flags & SymbolFlags.Class) result |= SymbolFlags.ClassExcludes;
if (flags & SymbolFlags.Interface) result |= SymbolFlags.InterfaceExcludes;
if (flags & SymbolFlags.RegularEnum) result |= SymbolFlags.RegularEnumExcludes;
if (flags & SymbolFlags.ConstEnum) result |= SymbolFlags.ConstEnumExcludes;
if (flags & SymbolFlags.ValueModule) result |= SymbolFlags.ValueModuleExcludes;
if (flags & SymbolFlags.Method) result |= SymbolFlags.MethodExcludes;
if (flags & SymbolFlags.GetAccessor) result |= SymbolFlags.GetAccessorExcludes;
if (flags & SymbolFlags.SetAccessor) result |= SymbolFlags.SetAccessorExcludes;
if (flags & SymbolFlags.TypeParameter) result |= SymbolFlags.TypeParameterExcludes;
if (flags & SymbolFlags.TypeAlias) result |= SymbolFlags.TypeAliasExcludes;
if (flags & SymbolFlags.Alias) result |= SymbolFlags.AliasExcludes;
return result;
}
function recordMergedSymbol(target: Symbol, source: Symbol) {
if (!source.mergeId) {
source.mergeId = nextMergeId;
nextMergeId++;
}
mergedSymbols[source.mergeId] = target;
}
function cloneSymbol(symbol: Symbol): Symbol {
const result = createSymbol(symbol.flags, symbol.name);
result.declarations = symbol.declarations.slice(0);
result.parent = symbol.parent;
if (symbol.valueDeclaration) result.valueDeclaration = symbol.valueDeclaration;
if (symbol.constEnumOnlyModule) result.constEnumOnlyModule = true;
if (symbol.members) result.members = cloneMap(symbol.members);
if (symbol.exports) result.exports = cloneMap(symbol.exports);
recordMergedSymbol(result, symbol);
return result;
}
function mergeSymbol(target: Symbol, source: Symbol) {
if (!(target.flags & getExcludedSymbolFlags(source.flags))) {
if (source.flags & SymbolFlags.ValueModule && target.flags & SymbolFlags.ValueModule && target.constEnumOnlyModule && !source.constEnumOnlyModule) {
// reset flag when merging instantiated module into value module that has only const enums
target.constEnumOnlyModule = false;
}
target.flags |= source.flags;
if (source.valueDeclaration &&
(!target.valueDeclaration ||
(target.valueDeclaration.kind === SyntaxKind.ModuleDeclaration && source.valueDeclaration.kind !== SyntaxKind.ModuleDeclaration))) {
// other kinds of value declarations take precedence over modules
target.valueDeclaration = source.valueDeclaration;
}
addRange(target.declarations, source.declarations);
if (source.members) {
if (!target.members) target.members = createMap<Symbol>();
mergeSymbolTable(target.members, source.members);
}
if (source.exports) {
if (!target.exports) target.exports = createMap<Symbol>();
mergeSymbolTable(target.exports, source.exports);
}
recordMergedSymbol(target, source);
}
else if (target.flags & SymbolFlags.NamespaceModule) {
error(getNameOfDeclaration(source.declarations[0]), Diagnostics.Cannot_augment_module_0_with_value_exports_because_it_resolves_to_a_non_module_entity, symbolToString(target));
}
else {
const message = target.flags & SymbolFlags.BlockScopedVariable || source.flags & SymbolFlags.BlockScopedVariable
? Diagnostics.Cannot_redeclare_block_scoped_variable_0 : Diagnostics.Duplicate_identifier_0;
forEach(source.declarations, node => {
error(getNameOfDeclaration(node) || node, message, symbolToString(source));
});
forEach(target.declarations, node => {
error(getNameOfDeclaration(node) || node, message, symbolToString(source));
});
}
}
function mergeSymbolTable(target: SymbolTable, source: SymbolTable) {
source.forEach((sourceSymbol, id) => {
let targetSymbol = target.get(id);
if (!targetSymbol) {
target.set(id, sourceSymbol);
}
else {
if (!(targetSymbol.flags & SymbolFlags.Transient)) {
targetSymbol = cloneSymbol(targetSymbol);
target.set(id, targetSymbol);
}
mergeSymbol(targetSymbol, sourceSymbol);
}
});
}
function mergeModuleAugmentation(moduleName: LiteralExpression): void {
const moduleAugmentation = <ModuleDeclaration>moduleName.parent;
if (moduleAugmentation.symbol.declarations[0] !== moduleAugmentation) {
// this is a combined symbol for multiple augmentations within the same file.
// its symbol already has accumulated information for all declarations
// so we need to add it just once - do the work only for first declaration
Debug.assert(moduleAugmentation.symbol.declarations.length > 1);
return;
}
if (isGlobalScopeAugmentation(moduleAugmentation)) {
mergeSymbolTable(globals, moduleAugmentation.symbol.exports);
}
else {
// find a module that about to be augmented
// do not validate names of augmentations that are defined in ambient context
const moduleNotFoundError = !isInAmbientContext(moduleName.parent.parent)
? Diagnostics.Invalid_module_name_in_augmentation_module_0_cannot_be_found
: undefined;
let mainModule = resolveExternalModuleNameWorker(moduleName, moduleName, moduleNotFoundError, /*isForAugmentation*/ true);
if (!mainModule) {
return;
}
// obtain item referenced by 'export='
mainModule = resolveExternalModuleSymbol(mainModule);
if (mainModule.flags & SymbolFlags.Namespace) {
// if module symbol has already been merged - it is safe to use it.
// otherwise clone it
mainModule = mainModule.flags & SymbolFlags.Transient ? mainModule : cloneSymbol(mainModule);
mergeSymbol(mainModule, moduleAugmentation.symbol);
}
else {
error(moduleName, Diagnostics.Cannot_augment_module_0_because_it_resolves_to_a_non_module_entity, moduleName.text);
}
}
}
function addToSymbolTable(target: SymbolTable, source: SymbolTable, message: DiagnosticMessage) {
source.forEach((sourceSymbol, id) => {
const targetSymbol = target.get(id);
if (targetSymbol) {
// Error on redeclarations
forEach(targetSymbol.declarations, addDeclarationDiagnostic(id, message));
}
else {
target.set(id, sourceSymbol);
}
});
function addDeclarationDiagnostic(id: string, message: DiagnosticMessage) {
return (declaration: Declaration) => diagnostics.add(createDiagnosticForNode(declaration, message, id));
}
}
function getSymbolLinks(symbol: Symbol): SymbolLinks {
if (symbol.flags & SymbolFlags.Transient) return <TransientSymbol>symbol;
const id = getSymbolId(symbol);
return symbolLinks[id] || (symbolLinks[id] = {});
}
function getNodeLinks(node: Node): NodeLinks {
const nodeId = getNodeId(node);
return nodeLinks[nodeId] || (nodeLinks[nodeId] = { flags: 0 });
}
function getObjectFlags(type: Type): ObjectFlags {
return type.flags & TypeFlags.Object ? (<ObjectType>type).objectFlags : 0;
}
function isGlobalSourceFile(node: Node) {
return node.kind === SyntaxKind.SourceFile && !isExternalOrCommonJsModule(<SourceFile>node);
}
function getSymbol(symbols: SymbolTable, name: string, meaning: SymbolFlags): Symbol {
if (meaning) {
const symbol = symbols.get(name);
if (symbol) {
Debug.assert((getCheckFlags(symbol) & CheckFlags.Instantiated) === 0, "Should never get an instantiated symbol here.");
if (symbol.flags & meaning) {
return symbol;
}
if (symbol.flags & SymbolFlags.Alias) {
const target = resolveAlias(symbol);
// Unknown symbol means an error occurred in alias resolution, treat it as positive answer to avoid cascading errors
if (target === unknownSymbol || target.flags & meaning) {
return symbol;
}
}
}
}
// return undefined if we can't find a symbol.
}
/**
* Get symbols that represent parameter-property-declaration as parameter and as property declaration
* @param parameter a parameterDeclaration node
* @param parameterName a name of the parameter to get the symbols for.
* @return a tuple of two symbols
*/
function getSymbolsOfParameterPropertyDeclaration(parameter: ParameterDeclaration, parameterName: string): [Symbol, Symbol] {
const constructorDeclaration = parameter.parent;
const classDeclaration = parameter.parent.parent;
const parameterSymbol = getSymbol(constructorDeclaration.locals, parameterName, SymbolFlags.Value);
const propertySymbol = getSymbol(classDeclaration.symbol.members, parameterName, SymbolFlags.Value);
if (parameterSymbol && propertySymbol) {
return [parameterSymbol, propertySymbol];
}
Debug.fail("There should exist two symbols, one as property declaration and one as parameter declaration");
}
function isBlockScopedNameDeclaredBeforeUse(declaration: Declaration, usage: Node): boolean {
const declarationFile = getSourceFileOfNode(declaration);
const useFile = getSourceFileOfNode(usage);
if (declarationFile !== useFile) {
if ((modulekind && (declarationFile.externalModuleIndicator || useFile.externalModuleIndicator)) ||
(!compilerOptions.outFile && !compilerOptions.out) ||
isInAmbientContext(declaration)) {
// nodes are in different files and order cannot be determined
return true;
}
// declaration is after usage
// can be legal if usage is deferred (i.e. inside function or in initializer of instance property)
if (isUsedInFunctionOrInstanceProperty(usage, declaration)) {
return true;
}
const sourceFiles = host.getSourceFiles();
return indexOf(sourceFiles, declarationFile) <= indexOf(sourceFiles, useFile);
}
if (declaration.pos <= usage.pos) {
// declaration is before usage
if (declaration.kind === SyntaxKind.BindingElement) {
// still might be illegal if declaration and usage are both binding elements (eg var [a = b, b = b] = [1, 2])
const errorBindingElement = getAncestor(usage, SyntaxKind.BindingElement) as BindingElement;
if (errorBindingElement) {
return findAncestor(errorBindingElement, isBindingElement) !== findAncestor(declaration, isBindingElement) ||
declaration.pos < errorBindingElement.pos;
}
// or it might be illegal if usage happens before parent variable is declared (eg var [a] = a)
return isBlockScopedNameDeclaredBeforeUse(getAncestor(declaration, SyntaxKind.VariableDeclaration) as Declaration, usage);
}
else if (declaration.kind === SyntaxKind.VariableDeclaration) {
// still might be illegal if usage is in the initializer of the variable declaration (eg var a = a)
return !isImmediatelyUsedInInitializerOfBlockScopedVariable(declaration as VariableDeclaration, usage);
}
return true;
}
// declaration is after usage, but it can still be legal if usage is deferred:
// 1. inside an export specifier
// 2. inside a function
// 3. inside an instance property initializer, a reference to a non-instance property
// 4. inside a static property initializer, a reference to a static method in the same class
// or if usage is in a type context:
// 1. inside a type query (typeof in type position)
if (usage.parent.kind === SyntaxKind.ExportSpecifier) {
// export specifiers do not use the variable, they only make it available for use
return true;
}
const container = getEnclosingBlockScopeContainer(declaration);
return isInTypeQuery(usage) || isUsedInFunctionOrInstanceProperty(usage, declaration, container);
function isImmediatelyUsedInInitializerOfBlockScopedVariable(declaration: VariableDeclaration, usage: Node): boolean {
const container = getEnclosingBlockScopeContainer(declaration);
switch (declaration.parent.parent.kind) {
case SyntaxKind.VariableStatement:
case SyntaxKind.ForStatement:
case SyntaxKind.ForOfStatement:
// variable statement/for/for-of statement case,
// use site should not be inside variable declaration (initializer of declaration or binding element)
if (isSameScopeDescendentOf(usage, declaration, container)) {
return true;
}
break;
}
switch (declaration.parent.parent.kind) {
case SyntaxKind.ForInStatement:
case SyntaxKind.ForOfStatement:
// ForIn/ForOf case - use site should not be used in expression part
if (isSameScopeDescendentOf(usage, (<ForInStatement | ForOfStatement>declaration.parent.parent).expression, container)) {
return true;
}
}
return false;
}
function isUsedInFunctionOrInstanceProperty(usage: Node, declaration: Node, container?: Node): boolean {
return !!findAncestor(usage, current => {
if (current === container) {
return "quit";
}
if (isFunctionLike(current)) {
return true;
}
const initializerOfProperty = current.parent &&
current.parent.kind === SyntaxKind.PropertyDeclaration &&
(<PropertyDeclaration>current.parent).initializer === current;
if (initializerOfProperty) {
if (getModifierFlags(current.parent) & ModifierFlags.Static) {
if (declaration.kind === SyntaxKind.MethodDeclaration) {
return true;
}
}
else {
const isDeclarationInstanceProperty = declaration.kind === SyntaxKind.PropertyDeclaration && !(getModifierFlags(declaration) & ModifierFlags.Static);
if (!isDeclarationInstanceProperty || getContainingClass(usage) !== getContainingClass(declaration)) {
return true;
}
}
}
});
}
}
// Resolve a given name for a given meaning at a given location. An error is reported if the name was not found and
// the nameNotFoundMessage argument is not undefined. Returns the resolved symbol, or undefined if no symbol with
// the given name can be found.
function resolveName(
location: Node | undefined,
name: string,
meaning: SymbolFlags,
nameNotFoundMessage: DiagnosticMessage,
nameArg: string | Identifier,
suggestedNameNotFoundMessage?: DiagnosticMessage): Symbol {
return resolveNameHelper(location, name, meaning, nameNotFoundMessage, nameArg, getSymbol, suggestedNameNotFoundMessage);
}
function resolveNameHelper(
location: Node | undefined,
name: string,
meaning: SymbolFlags,
nameNotFoundMessage: DiagnosticMessage,
nameArg: string | Identifier,
lookup: (symbols: SymbolTable, name: string, meaning: SymbolFlags) => Symbol,
suggestedNameNotFoundMessage?: DiagnosticMessage): Symbol {
const originalLocation = location; // needed for did-you-mean error reporting, which gathers candidates starting from the original location
let result: Symbol;
let lastLocation: Node;
let propertyWithInvalidInitializer: Node;
const errorLocation = location;
let grandparent: Node;
let isInExternalModule = false;
loop: while (location) {
// Locals of a source file are not in scope (because they get merged into the global symbol table)
if (location.locals && !isGlobalSourceFile(location)) {
if (result = lookup(location.locals, name, meaning)) {
let useResult = true;
if (isFunctionLike(location) && lastLocation && lastLocation !== (<FunctionLikeDeclaration>location).body) {
// symbol lookup restrictions for function-like declarations
// - Type parameters of a function are in scope in the entire function declaration, including the parameter
// list and return type. However, local types are only in scope in the function body.
// - parameters are only in the scope of function body
// This restriction does not apply to JSDoc comment types because they are parented
// at a higher level than type parameters would normally be
if (meaning & result.flags & SymbolFlags.Type && lastLocation.kind !== SyntaxKind.JSDocComment) {
useResult = result.flags & SymbolFlags.TypeParameter
// type parameters are visible in parameter list, return type and type parameter list
? lastLocation === (<FunctionLikeDeclaration>location).type ||
lastLocation.kind === SyntaxKind.Parameter ||
lastLocation.kind === SyntaxKind.TypeParameter
// local types not visible outside the function body
: false;
}
if (meaning & SymbolFlags.Value && result.flags & SymbolFlags.FunctionScopedVariable) {
// parameters are visible only inside function body, parameter list and return type
// technically for parameter list case here we might mix parameters and variables declared in function,
// however it is detected separately when checking initializers of parameters
// to make sure that they reference no variables declared after them.
useResult =
lastLocation.kind === SyntaxKind.Parameter ||
(
lastLocation === (<FunctionLikeDeclaration>location).type &&
result.valueDeclaration.kind === SyntaxKind.Parameter
);
}
}
if (useResult) {
break loop;
}
else {
result = undefined;
}
}
}
switch (location.kind) {
case SyntaxKind.SourceFile:
if (!isExternalOrCommonJsModule(<SourceFile>location)) break;
isInExternalModule = true;
// falls through
case SyntaxKind.ModuleDeclaration:
const moduleExports = getSymbolOfNode(location).exports;
if (location.kind === SyntaxKind.SourceFile || isAmbientModule(location)) {
// It's an external module. First see if the module has an export default and if the local
// name of that export default matches.
if (result = moduleExports.get("default")) {
const localSymbol = getLocalSymbolForExportDefault(result);
if (localSymbol && (result.flags & meaning) && localSymbol.name === name) {
break loop;
}
result = undefined;
}
// Because of module/namespace merging, a module's exports are in scope,
// yet we never want to treat an export specifier as putting a member in scope.
// Therefore, if the name we find is purely an export specifier, it is not actually considered in scope.
// Two things to note about this:
// 1. We have to check this without calling getSymbol. The problem with calling getSymbol
// on an export specifier is that it might find the export specifier itself, and try to
// resolve it as an alias. This will cause the checker to consider the export specifier
// a circular alias reference when it might not be.
// 2. We check === SymbolFlags.Alias in order to check that the symbol is *purely*
// an alias. If we used &, we'd be throwing out symbols that have non alias aspects,
// which is not the desired behavior.
const moduleExport = moduleExports.get(name);
if (moduleExport &&
moduleExport.flags === SymbolFlags.Alias &&
getDeclarationOfKind(moduleExport, SyntaxKind.ExportSpecifier)) {
break;
}
}
if (result = lookup(moduleExports, name, meaning & SymbolFlags.ModuleMember)) {
break loop;
}
break;
case SyntaxKind.EnumDeclaration:
if (result = lookup(getSymbolOfNode(location).exports, name, meaning & SymbolFlags.EnumMember)) {
break loop;
}
break;
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.PropertySignature:
// TypeScript 1.0 spec (April 2014): 8.4.1
// Initializer expressions for instance member variables are evaluated in the scope
// of the class constructor body but are not permitted to reference parameters or
// local variables of the constructor. This effectively means that entities from outer scopes
// by the same name as a constructor parameter or local variable are inaccessible
// in initializer expressions for instance member variables.
if (isClassLike(location.parent) && !(getModifierFlags(location) & ModifierFlags.Static)) {
const ctor = findConstructorDeclaration(<ClassLikeDeclaration>location.parent);
if (ctor && ctor.locals) {
if (lookup(ctor.locals, name, meaning & SymbolFlags.Value)) {
// Remember the property node, it will be used later to report appropriate error
propertyWithInvalidInitializer = location;
}
}
}
break;
case SyntaxKind.ClassDeclaration:
case SyntaxKind.ClassExpression:
case SyntaxKind.InterfaceDeclaration:
if (result = lookup(getSymbolOfNode(location).members, name, meaning & SymbolFlags.Type)) {
if (!isTypeParameterSymbolDeclaredInContainer(result, location)) {
// ignore type parameters not declared in this container
result = undefined;
break;
}
if (lastLocation && getModifierFlags(lastLocation) & ModifierFlags.Static) {
// TypeScript 1.0 spec (April 2014): 3.4.1
// The scope of a type parameter extends over the entire declaration with which the type
// parameter list is associated, with the exception of static member declarations in classes.
error(errorLocation, Diagnostics.Static_members_cannot_reference_class_type_parameters);
return undefined;
}
break loop;
}
if (location.kind === SyntaxKind.ClassExpression && meaning & SymbolFlags.Class) {
const className = (<ClassExpression>location).name;
if (className && name === className.text) {
result = location.symbol;
break loop;
}
}
break;
// It is not legal to reference a class's own type parameters from a computed property name that
// belongs to the class. For example:
//
// function foo<T>() { return '' }
// class C<T> { // <-- Class's own type parameter T
// [foo<T>()]() { } // <-- Reference to T from class's own computed property
// }
//
case SyntaxKind.ComputedPropertyName:
grandparent = location.parent.parent;
if (isClassLike(grandparent) || grandparent.kind === SyntaxKind.InterfaceDeclaration) {
// A reference to this grandparent's type parameters would be an error
if (result = lookup(getSymbolOfNode(grandparent).members, name, meaning & SymbolFlags.Type)) {
error(errorLocation, Diagnostics.A_computed_property_name_cannot_reference_a_type_parameter_from_its_containing_type);
return undefined;
}
}
break;
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
case SyntaxKind.Constructor:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.ArrowFunction:
if (meaning & SymbolFlags.Variable && name === "arguments") {
result = argumentsSymbol;
break loop;
}
break;
case SyntaxKind.FunctionExpression:
if (meaning & SymbolFlags.Variable && name === "arguments") {
result = argumentsSymbol;
break loop;
}
if (meaning & SymbolFlags.Function) {
const functionName = (<FunctionExpression>location).name;
if (functionName && name === functionName.text) {
result = location.symbol;
break loop;
}
}
break;
case SyntaxKind.Decorator:
// Decorators are resolved at the class declaration. Resolving at the parameter
// or member would result in looking up locals in the method.
//
// function y() {}
// class C {
// method(@y x, y) {} // <-- decorator y should be resolved at the class declaration, not the parameter.
// }
//
if (location.parent && location.parent.kind === SyntaxKind.Parameter) {
location = location.parent;
}
//
// function y() {}
// class C {
// @y method(x, y) {} // <-- decorator y should be resolved at the class declaration, not the method.
// }
//
if (location.parent && isClassElement(location.parent)) {
location = location.parent;
}
break;
}
lastLocation = location;
location = location.parent;
}
if (result && nameNotFoundMessage && noUnusedIdentifiers) {
result.isReferenced = true;
}
if (!result) {
result = lookup(globals, name, meaning);
}
if (!result) {
if (nameNotFoundMessage) {
if (!errorLocation ||
!checkAndReportErrorForMissingPrefix(errorLocation, name, nameArg) &&
!checkAndReportErrorForExtendingInterface(errorLocation) &&
!checkAndReportErrorForUsingTypeAsNamespace(errorLocation, name, meaning) &&
!checkAndReportErrorForUsingTypeAsValue(errorLocation, name, meaning) &&
!checkAndReportErrorForUsingNamespaceModuleAsValue(errorLocation, name, meaning)) {
let suggestion: string | undefined;
if (suggestedNameNotFoundMessage && suggestionCount < maximumSuggestionCount) {
suggestion = getSuggestionForNonexistentSymbol(originalLocation, name, meaning);
if (suggestion) {
suggestionCount++;
error(errorLocation, suggestedNameNotFoundMessage, typeof nameArg === "string" ? nameArg : declarationNameToString(nameArg), suggestion);
}
}
if (!suggestion) {
error(errorLocation, nameNotFoundMessage, typeof nameArg === "string" ? nameArg : declarationNameToString(nameArg));
}
}
}
return undefined;
}
// Perform extra checks only if error reporting was requested
if (nameNotFoundMessage) {
if (propertyWithInvalidInitializer) {
// We have a match, but the reference occurred within a property initializer and the identifier also binds
// to a local variable in the constructor where the code will be emitted.
const propertyName = (<PropertyDeclaration>propertyWithInvalidInitializer).name;
error(errorLocation, Diagnostics.Initializer_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor,
declarationNameToString(propertyName), typeof nameArg === "string" ? nameArg : declarationNameToString(nameArg));
return undefined;
}
// Only check for block-scoped variable if we are looking for the
// name with variable meaning
// For example,
// declare module foo {
// interface bar {}
// }
// const foo/*1*/: foo/*2*/.bar;
// The foo at /*1*/ and /*2*/ will share same symbol with two meanings:
// block-scoped variable and namespace module. However, only when we
// try to resolve name in /*1*/ which is used in variable position,
// we want to check for block-scoped
if (meaning & SymbolFlags.BlockScopedVariable ||
((meaning & SymbolFlags.Class || meaning & SymbolFlags.Enum) && (meaning & SymbolFlags.Value) === SymbolFlags.Value)) {
const exportOrLocalSymbol = getExportSymbolOfValueSymbolIfExported(result);
if (exportOrLocalSymbol.flags & SymbolFlags.BlockScopedVariable || exportOrLocalSymbol.flags & SymbolFlags.Class || exportOrLocalSymbol.flags & SymbolFlags.Enum) {
checkResolvedBlockScopedVariable(exportOrLocalSymbol, errorLocation);
}
}
// If we're in an external module, we can't reference value symbols created from UMD export declarations
if (result && isInExternalModule && (meaning & SymbolFlags.Value) === SymbolFlags.Value) {
const decls = result.declarations;
if (decls && decls.length === 1 && decls[0].kind === SyntaxKind.NamespaceExportDeclaration) {
error(errorLocation, Diagnostics._0_refers_to_a_UMD_global_but_the_current_file_is_a_module_Consider_adding_an_import_instead, name);
}
}
}
return result;
}
function isTypeParameterSymbolDeclaredInContainer(symbol: Symbol, container: Node) {
for (const decl of symbol.declarations) {
if (decl.kind === SyntaxKind.TypeParameter && decl.parent === container) {
return true;
}
}
return false;
}
function checkAndReportErrorForMissingPrefix(errorLocation: Node, name: string, nameArg: string | Identifier): boolean {
if ((errorLocation.kind === SyntaxKind.Identifier && (isTypeReferenceIdentifier(<Identifier>errorLocation)) || isInTypeQuery(errorLocation))) {
return false;
}
const container = getThisContainer(errorLocation, /*includeArrowFunctions*/ true);
let location = container;
while (location) {
if (isClassLike(location.parent)) {
const classSymbol = getSymbolOfNode(location.parent);
if (!classSymbol) {
break;
}
// Check to see if a static member exists.
const constructorType = getTypeOfSymbol(classSymbol);
if (getPropertyOfType(constructorType, name)) {
error(errorLocation, Diagnostics.Cannot_find_name_0_Did_you_mean_the_static_member_1_0, typeof nameArg === "string" ? nameArg : declarationNameToString(nameArg), symbolToString(classSymbol));
return true;
}
// No static member is present.
// Check if we're in an instance method and look for a relevant instance member.
if (location === container && !(getModifierFlags(location) & ModifierFlags.Static)) {
const instanceType = (<InterfaceType>getDeclaredTypeOfSymbol(classSymbol)).thisType;
if (getPropertyOfType(instanceType, name)) {
error(errorLocation, Diagnostics.Cannot_find_name_0_Did_you_mean_the_instance_member_this_0, typeof nameArg === "string" ? nameArg : declarationNameToString(nameArg));
return true;
}
}
}
location = location.parent;
}
return false;
}
function checkAndReportErrorForExtendingInterface(errorLocation: Node): boolean {
const expression = getEntityNameForExtendingInterface(errorLocation);
const isError = !!(expression && resolveEntityName(expression, SymbolFlags.Interface, /*ignoreErrors*/ true));
if (isError) {
error(errorLocation, Diagnostics.Cannot_extend_an_interface_0_Did_you_mean_implements, getTextOfNode(expression));
}
return isError;
}
/**
* Climbs up parents to an ExpressionWithTypeArguments, and returns its expression,
* but returns undefined if that expression is not an EntityNameExpression.
*/
function getEntityNameForExtendingInterface(node: Node): EntityNameExpression | undefined {
switch (node.kind) {
case SyntaxKind.Identifier:
case SyntaxKind.PropertyAccessExpression:
return node.parent ? getEntityNameForExtendingInterface(node.parent) : undefined;
case SyntaxKind.ExpressionWithTypeArguments:
Debug.assert(isEntityNameExpression((<ExpressionWithTypeArguments>node).expression));
return <EntityNameExpression>(<ExpressionWithTypeArguments>node).expression;
default:
return undefined;
}
}
function checkAndReportErrorForUsingTypeAsNamespace(errorLocation: Node, name: string, meaning: SymbolFlags): boolean {
if (meaning === SymbolFlags.Namespace) {
const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.Type & ~SymbolFlags.Value, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined));
if (symbol) {
error(errorLocation, Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_namespace_here, name);
return true;
}
}
return false;
}
function checkAndReportErrorForUsingTypeAsValue(errorLocation: Node, name: string, meaning: SymbolFlags): boolean {
if (meaning & (SymbolFlags.Value & ~SymbolFlags.NamespaceModule)) {
if (name === "any" || name === "string" || name === "number" || name === "boolean" || name === "never") {
error(errorLocation, Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_value_here, name);
return true;
}
const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.Type & ~SymbolFlags.Value, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined));
if (symbol && !(symbol.flags & SymbolFlags.NamespaceModule)) {
error(errorLocation, Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_value_here, name);
return true;
}
}
return false;
}
function checkAndReportErrorForUsingNamespaceModuleAsValue(errorLocation: Node, name: string, meaning: SymbolFlags): boolean {
if (meaning & (SymbolFlags.Value & ~SymbolFlags.NamespaceModule & ~SymbolFlags.Type)) {
const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.NamespaceModule & ~SymbolFlags.Value, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined));
if (symbol) {
error(errorLocation, Diagnostics.Cannot_use_namespace_0_as_a_value, name);
return true;
}
}
else if (meaning & (SymbolFlags.Type & ~SymbolFlags.NamespaceModule & ~SymbolFlags.Value)) {
const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.NamespaceModule & ~SymbolFlags.Type, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined));
if (symbol) {
error(errorLocation, Diagnostics.Cannot_use_namespace_0_as_a_type, name);
return true;
}
}
return false;
}
function checkResolvedBlockScopedVariable(result: Symbol, errorLocation: Node): void {
Debug.assert(!!(result.flags & SymbolFlags.BlockScopedVariable || result.flags & SymbolFlags.Class || result.flags & SymbolFlags.Enum));
// Block-scoped variables cannot be used before their definition
const declaration = forEach(result.declarations, d => isBlockOrCatchScoped(d) || isClassLike(d) || (d.kind === SyntaxKind.EnumDeclaration) ? d : undefined);
Debug.assert(declaration !== undefined, "Declaration to checkResolvedBlockScopedVariable is undefined");
if (!isInAmbientContext(declaration) && !isBlockScopedNameDeclaredBeforeUse(declaration, errorLocation)) {
if (result.flags & SymbolFlags.BlockScopedVariable) {
error(errorLocation, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, declarationNameToString(getNameOfDeclaration(declaration)));
}
else if (result.flags & SymbolFlags.Class) {
error(errorLocation, Diagnostics.Class_0_used_before_its_declaration, declarationNameToString(getNameOfDeclaration(declaration)));
}
else if (result.flags & SymbolFlags.RegularEnum) {
error(errorLocation, Diagnostics.Enum_0_used_before_its_declaration, declarationNameToString(getNameOfDeclaration(declaration)));
}
}
}
/* Starting from 'initial' node walk up the parent chain until 'stopAt' node is reached.
* If at any point current node is equal to 'parent' node - return true.
* Return false if 'stopAt' node is reached or isFunctionLike(current) === true.
*/
function isSameScopeDescendentOf(initial: Node, parent: Node, stopAt: Node): boolean {
return parent && !!findAncestor(initial, n => n === stopAt || isFunctionLike(n) ? "quit" : n === parent);
}
function getAnyImportSyntax(node: Node): AnyImportSyntax {
if (isAliasSymbolDeclaration(node)) {
if (node.kind === SyntaxKind.ImportEqualsDeclaration) {
return <ImportEqualsDeclaration>node;
}
return findAncestor(node, isImportDeclaration);
}
}
function getDeclarationOfAliasSymbol(symbol: Symbol): Declaration | undefined {
return find<Declaration>(symbol.declarations, isAliasSymbolDeclaration);
}
function getTargetOfImportEqualsDeclaration(node: ImportEqualsDeclaration, dontResolveAlias: boolean): Symbol {
if (node.moduleReference.kind === SyntaxKind.ExternalModuleReference) {
return resolveExternalModuleSymbol(resolveExternalModuleName(node, getExternalModuleImportEqualsDeclarationExpression(node)));
}
return getSymbolOfPartOfRightHandSideOfImportEquals(<EntityName>node.moduleReference, dontResolveAlias);
}
function getTargetOfImportClause(node: ImportClause, dontResolveAlias: boolean): Symbol {
const moduleSymbol = resolveExternalModuleName(node, (<ImportDeclaration>node.parent).moduleSpecifier);
if (moduleSymbol) {
let exportDefaultSymbol: Symbol;
if (isShorthandAmbientModuleSymbol(moduleSymbol)) {
exportDefaultSymbol = moduleSymbol;
}
else {
const exportValue = moduleSymbol.exports.get("export=");
exportDefaultSymbol = exportValue
? getPropertyOfType(getTypeOfSymbol(exportValue), "default")
: resolveSymbol(moduleSymbol.exports.get("default"), dontResolveAlias);
}
if (!exportDefaultSymbol && !allowSyntheticDefaultImports) {
error(node.name, Diagnostics.Module_0_has_no_default_export, symbolToString(moduleSymbol));
}
else if (!exportDefaultSymbol && allowSyntheticDefaultImports) {
return resolveExternalModuleSymbol(moduleSymbol, dontResolveAlias) || resolveSymbol(moduleSymbol, dontResolveAlias);
}
return exportDefaultSymbol;
}
}
function getTargetOfNamespaceImport(node: NamespaceImport, dontResolveAlias: boolean): Symbol {
const moduleSpecifier = (<ImportDeclaration>node.parent.parent).moduleSpecifier;
return resolveESModuleSymbol(resolveExternalModuleName(node, moduleSpecifier), moduleSpecifier, dontResolveAlias);
}
// This function creates a synthetic symbol that combines the value side of one symbol with the
// type/namespace side of another symbol. Consider this example:
//
// declare module graphics {
// interface Point {
// x: number;
// y: number;
// }
// }
// declare var graphics: {
// Point: new (x: number, y: number) => graphics.Point;
// }
// declare module "graphics" {
// export = graphics;
// }
//
// An 'import { Point } from "graphics"' needs to create a symbol that combines the value side 'Point'
// property with the type/namespace side interface 'Point'.
function combineValueAndTypeSymbols(valueSymbol: Symbol, typeSymbol: Symbol): Symbol {
if (valueSymbol.flags & (SymbolFlags.Type | SymbolFlags.Namespace)) {
return valueSymbol;
}
const result = createSymbol(valueSymbol.flags | typeSymbol.flags, valueSymbol.name);
result.declarations = concatenate(valueSymbol.declarations, typeSymbol.declarations);
result.parent = valueSymbol.parent || typeSymbol.parent;
if (valueSymbol.valueDeclaration) result.valueDeclaration = valueSymbol.valueDeclaration;
if (typeSymbol.members) result.members = typeSymbol.members;
if (valueSymbol.exports) result.exports = valueSymbol.exports;
return result;
}
function getExportOfModule(symbol: Symbol, name: string, dontResolveAlias: boolean): Symbol {
if (symbol.flags & SymbolFlags.Module) {
return resolveSymbol(getExportsOfSymbol(symbol).get(name), dontResolveAlias);
}
}
function getPropertyOfVariable(symbol: Symbol, name: string): Symbol {
if (symbol.flags & SymbolFlags.Variable) {
const typeAnnotation = (<VariableDeclaration>symbol.valueDeclaration).type;
if (typeAnnotation) {
return resolveSymbol(getPropertyOfType(getTypeFromTypeNode(typeAnnotation), name));
}
}
}
function getExternalModuleMember(node: ImportDeclaration | ExportDeclaration, specifier: ImportOrExportSpecifier, dontResolveAlias?: boolean): Symbol {
const moduleSymbol = resolveExternalModuleName(node, node.moduleSpecifier);
const targetSymbol = resolveESModuleSymbol(moduleSymbol, node.moduleSpecifier, dontResolveAlias);
if (targetSymbol) {
const name = specifier.propertyName || specifier.name;
if (name.text) {
if (isShorthandAmbientModuleSymbol(moduleSymbol)) {
return moduleSymbol;
}
let symbolFromVariable: Symbol;
// First check if module was specified with "export=". If so, get the member from the resolved type
if (moduleSymbol && moduleSymbol.exports && moduleSymbol.exports.get("export=")) {
symbolFromVariable = getPropertyOfType(getTypeOfSymbol(targetSymbol), name.text);
}
else {
symbolFromVariable = getPropertyOfVariable(targetSymbol, name.text);
}
// if symbolFromVariable is export - get its final target
symbolFromVariable = resolveSymbol(symbolFromVariable, dontResolveAlias);
let symbolFromModule = getExportOfModule(targetSymbol, name.text, dontResolveAlias);
// If the export member we're looking for is default, and there is no real default but allowSyntheticDefaultImports is on, return the entire module as the default
if (!symbolFromModule && allowSyntheticDefaultImports && name.text === "default") {
symbolFromModule = resolveExternalModuleSymbol(moduleSymbol, dontResolveAlias) || resolveSymbol(moduleSymbol, dontResolveAlias);
}
const symbol = symbolFromModule && symbolFromVariable ?
combineValueAndTypeSymbols(symbolFromVariable, symbolFromModule) :
symbolFromModule || symbolFromVariable;
if (!symbol) {
error(name, Diagnostics.Module_0_has_no_exported_member_1, getFullyQualifiedName(moduleSymbol), declarationNameToString(name));
}
return symbol;
}
}
}
function getTargetOfImportSpecifier(node: ImportSpecifier, dontResolveAlias: boolean): Symbol {
return getExternalModuleMember(<ImportDeclaration>node.parent.parent.parent, node, dontResolveAlias);
}
function getTargetOfNamespaceExportDeclaration(node: NamespaceExportDeclaration, dontResolveAlias: boolean): Symbol {
return resolveExternalModuleSymbol(node.parent.symbol, dontResolveAlias);
}
function getTargetOfExportSpecifier(node: ExportSpecifier, meaning: SymbolFlags, dontResolveAlias?: boolean) {
return node.parent.parent.moduleSpecifier ?
getExternalModuleMember(node.parent.parent, node, dontResolveAlias) :
resolveEntityName(node.propertyName || node.name, meaning, /*ignoreErrors*/ false, dontResolveAlias);
}
function getTargetOfExportAssignment(node: ExportAssignment, dontResolveAlias: boolean): Symbol {
return resolveEntityName(<EntityNameExpression>node.expression, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace, /*ignoreErrors*/ false, dontResolveAlias);
}
function getTargetOfAliasDeclaration(node: Declaration, dontRecursivelyResolve?: boolean): Symbol {
switch (node.kind) {
case SyntaxKind.ImportEqualsDeclaration:
return getTargetOfImportEqualsDeclaration(<ImportEqualsDeclaration>node, dontRecursivelyResolve);
case SyntaxKind.ImportClause:
return getTargetOfImportClause(<ImportClause>node, dontRecursivelyResolve);
case SyntaxKind.NamespaceImport:
return getTargetOfNamespaceImport(<NamespaceImport>node, dontRecursivelyResolve);
case SyntaxKind.ImportSpecifier:
return getTargetOfImportSpecifier(<ImportSpecifier>node, dontRecursivelyResolve);
case SyntaxKind.ExportSpecifier:
return getTargetOfExportSpecifier(<ExportSpecifier>node, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace, dontRecursivelyResolve);
case SyntaxKind.ExportAssignment:
return getTargetOfExportAssignment(<ExportAssignment>node, dontRecursivelyResolve);
case SyntaxKind.NamespaceExportDeclaration:
return getTargetOfNamespaceExportDeclaration(<NamespaceExportDeclaration>node, dontRecursivelyResolve);
}
}
function resolveSymbol(symbol: Symbol, dontResolveAlias?: boolean): Symbol {
const shouldResolve = !dontResolveAlias && symbol && symbol.flags & SymbolFlags.Alias && !(symbol.flags & (SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace));
return shouldResolve ? resolveAlias(symbol) : symbol;
}
function resolveAlias(symbol: Symbol): Symbol {
Debug.assert((symbol.flags & SymbolFlags.Alias) !== 0, "Should only get Alias here.");
const links = getSymbolLinks(symbol);
if (!links.target) {
links.target = resolvingSymbol;
const node = getDeclarationOfAliasSymbol(symbol);
Debug.assert(!!node);
const target = getTargetOfAliasDeclaration(node);
if (links.target === resolvingSymbol) {
links.target = target || unknownSymbol;
}
else {
error(node, Diagnostics.Circular_definition_of_import_alias_0, symbolToString(symbol));
}
}
else if (links.target === resolvingSymbol) {
links.target = unknownSymbol;
}
return links.target;
}
function markExportAsReferenced(node: ImportEqualsDeclaration | ExportAssignment | ExportSpecifier) {
const symbol = getSymbolOfNode(node);
const target = resolveAlias(symbol);
if (target) {
const markAlias = target === unknownSymbol ||
((target.flags & SymbolFlags.Value) && !isConstEnumOrConstEnumOnlyModule(target));
if (markAlias) {
markAliasSymbolAsReferenced(symbol);
}
}
}
// When an alias symbol is referenced, we need to mark the entity it references as referenced and in turn repeat that until
// we reach a non-alias or an exported entity (which is always considered referenced). We do this by checking the target of
// the alias as an expression (which recursively takes us back here if the target references another alias).
function markAliasSymbolAsReferenced(symbol: Symbol) {
const links = getSymbolLinks(symbol);
if (!links.referenced) {
links.referenced = true;
const node = getDeclarationOfAliasSymbol(symbol);
Debug.assert(!!node);
if (node.kind === SyntaxKind.ExportAssignment) {
// export default <symbol>
checkExpressionCached((<ExportAssignment>node).expression);
}
else if (node.kind === SyntaxKind.ExportSpecifier) {
// export { <symbol> } or export { <symbol> as foo }
checkExpressionCached((<ExportSpecifier>node).propertyName || (<ExportSpecifier>node).name);
}
else if (isInternalModuleImportEqualsDeclaration(node)) {
// import foo = <symbol>
checkExpressionCached(<Expression>(<ImportEqualsDeclaration>node).moduleReference);
}
}
}
// This function is only for imports with entity names
function getSymbolOfPartOfRightHandSideOfImportEquals(entityName: EntityName, dontResolveAlias?: boolean): Symbol {
// There are three things we might try to look for. In the following examples,
// the search term is enclosed in |...|:
//
// import a = |b|; // Namespace
// import a = |b.c|; // Value, type, namespace
// import a = |b.c|.d; // Namespace
if (entityName.kind === SyntaxKind.Identifier && isRightSideOfQualifiedNameOrPropertyAccess(entityName)) {
entityName = <QualifiedName>entityName.parent;
}
// Check for case 1 and 3 in the above example
if (entityName.kind === SyntaxKind.Identifier || entityName.parent.kind === SyntaxKind.QualifiedName) {
return resolveEntityName(entityName, SymbolFlags.Namespace, /*ignoreErrors*/ false, dontResolveAlias);
}
else {
// Case 2 in above example
// entityName.kind could be a QualifiedName or a Missing identifier
Debug.assert(entityName.parent.kind === SyntaxKind.ImportEqualsDeclaration);
return resolveEntityName(entityName, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace, /*ignoreErrors*/ false, dontResolveAlias);
}
}
function getFullyQualifiedName(symbol: Symbol): string {
return symbol.parent ? getFullyQualifiedName(symbol.parent) + "." + symbolToString(symbol) : symbolToString(symbol);
}
/**
* Resolves a qualified name and any involved aliases.
*/
function resolveEntityName(name: EntityNameOrEntityNameExpression, meaning: SymbolFlags, ignoreErrors?: boolean, dontResolveAlias?: boolean, location?: Node): Symbol | undefined {
if (nodeIsMissing(name)) {
return undefined;
}
let symbol: Symbol;
if (name.kind === SyntaxKind.Identifier) {
const message = meaning === SymbolFlags.Namespace ? Diagnostics.Cannot_find_namespace_0 : Diagnostics.Cannot_find_name_0;
symbol = resolveName(location || name, (<Identifier>name).text, meaning, ignoreErrors ? undefined : message, <Identifier>name);
if (!symbol) {
return undefined;
}
}
else if (name.kind === SyntaxKind.QualifiedName || name.kind === SyntaxKind.PropertyAccessExpression) {
let left: EntityNameOrEntityNameExpression;
if (name.kind === SyntaxKind.QualifiedName) {
left = (<QualifiedName>name).left;
}
else if (name.kind === SyntaxKind.PropertyAccessExpression &&
(name.expression.kind === SyntaxKind.ParenthesizedExpression || isEntityNameExpression(name.expression))) {
left = name.expression;
}
else {
// If the expression in property-access expression is not entity-name or parenthsizedExpression (e.g. it is a call expression), it won't be able to successfully resolve the name.
// This is the case when we are trying to do any language service operation in heritage clauses. By return undefined, the getSymbolOfEntityNameOrPropertyAccessExpression
// will attempt to checkPropertyAccessExpression to resolve symbol.
// i.e class C extends foo()./*do language service operation here*/B {}
return undefined;
}
const right = name.kind === SyntaxKind.QualifiedName ? name.right : name.name;
const namespace = resolveEntityName(left, SymbolFlags.Namespace, ignoreErrors, /*dontResolveAlias*/ false, location);
if (!namespace || nodeIsMissing(right)) {
return undefined;
}
else if (namespace === unknownSymbol) {
return namespace;
}
symbol = getSymbol(getExportsOfSymbol(namespace), right.text, meaning);
if (!symbol) {
if (!ignoreErrors) {
error(right, Diagnostics.Namespace_0_has_no_exported_member_1, getFullyQualifiedName(namespace), declarationNameToString(right));
}
return undefined;
}
}
else if (name.kind === SyntaxKind.ParenthesizedExpression) {
// If the expression in parenthesizedExpression is not an entity-name (e.g. it is a call expression), it won't be able to successfully resolve the name.
// This is the case when we are trying to do any language service operation in heritage clauses.
// By return undefined, the getSymbolOfEntityNameOrPropertyAccessExpression will attempt to checkPropertyAccessExpression to resolve symbol.
// i.e class C extends foo()./*do language service operation here*/B {}
return isEntityNameExpression(name.expression) ?
resolveEntityName(name.expression as EntityNameOrEntityNameExpression, meaning, ignoreErrors, dontResolveAlias, location) :
undefined;
}
else {
Debug.fail("Unknown entity name kind.");
}
Debug.assert((getCheckFlags(symbol) & CheckFlags.Instantiated) === 0, "Should never get an instantiated symbol here.");
return (symbol.flags & meaning) || dontResolveAlias ? symbol : resolveAlias(symbol);
}
function resolveExternalModuleName(location: Node, moduleReferenceExpression: Expression): Symbol {
return resolveExternalModuleNameWorker(location, moduleReferenceExpression, Diagnostics.Cannot_find_module_0);
}
function resolveExternalModuleNameWorker(location: Node, moduleReferenceExpression: Expression, moduleNotFoundError: DiagnosticMessage, isForAugmentation = false): Symbol {
if (moduleReferenceExpression.kind !== SyntaxKind.StringLiteral && moduleReferenceExpression.kind !== SyntaxKind.NoSubstitutionTemplateLiteral) {
return;
}
const moduleReferenceLiteral = <LiteralExpression>moduleReferenceExpression;
return resolveExternalModule(location, moduleReferenceLiteral.text, moduleNotFoundError, moduleReferenceLiteral, isForAugmentation);
}
function resolveExternalModule(location: Node, moduleReference: string, moduleNotFoundError: DiagnosticMessage, errorNode: Node, isForAugmentation = false): Symbol {
// Module names are escaped in our symbol table. However, string literal values aren't.
// Escape the name in the "require(...)" clause to ensure we find the right symbol.
const moduleName = escapeIdentifier(moduleReference);
if (moduleName === undefined) {
return;
}
if (startsWith(moduleReference, "@types/")) {
const diag = Diagnostics.Cannot_import_type_declaration_files_Consider_importing_0_instead_of_1;
const withoutAtTypePrefix = removePrefix(moduleReference, "@types/");
error(errorNode, diag, withoutAtTypePrefix, moduleReference);
}
const ambientModule = tryFindAmbientModule(moduleName, /*withAugmentations*/ true);
if (ambientModule) {
return ambientModule;
}
const isRelative = isExternalModuleNameRelative(moduleName);
const resolvedModule = getResolvedModule(getSourceFileOfNode(location), moduleReference);
const resolutionDiagnostic = resolvedModule && getResolutionDiagnostic(compilerOptions, resolvedModule);
const sourceFile = resolvedModule && !resolutionDiagnostic && host.getSourceFile(resolvedModule.resolvedFileName);
if (sourceFile) {
if (sourceFile.symbol) {
// merged symbol is module declaration symbol combined with all augmentations
return getMergedSymbol(sourceFile.symbol);
}
if (moduleNotFoundError) {
// report errors only if it was requested
error(errorNode, Diagnostics.File_0_is_not_a_module, sourceFile.fileName);
}
return undefined;
}
if (patternAmbientModules) {
const pattern = findBestPatternMatch(patternAmbientModules, _ => _.pattern, moduleName);
if (pattern) {
return getMergedSymbol(pattern.symbol);
}
}
// May be an untyped module. If so, ignore resolutionDiagnostic.
if (!isRelative && resolvedModule && !extensionIsTypeScript(resolvedModule.extension)) {
if (isForAugmentation) {
const diag = Diagnostics.Invalid_module_name_in_augmentation_Module_0_resolves_to_an_untyped_module_at_1_which_cannot_be_augmented;
error(errorNode, diag, moduleReference, resolvedModule.resolvedFileName);
}
else if (noImplicitAny && moduleNotFoundError) {
let errorInfo = chainDiagnosticMessages(/*details*/ undefined,
Diagnostics.Try_npm_install_types_Slash_0_if_it_exists_or_add_a_new_declaration_d_ts_file_containing_declare_module_0,
moduleReference);
errorInfo = chainDiagnosticMessages(errorInfo,
Diagnostics.Could_not_find_a_declaration_file_for_module_0_1_implicitly_has_an_any_type,
moduleReference,
resolvedModule.resolvedFileName);
diagnostics.add(createDiagnosticForNodeFromMessageChain(errorNode, errorInfo));
}
// Failed imports and untyped modules are both treated in an untyped manner; only difference is whether we give a diagnostic first.
return undefined;
}
if (moduleNotFoundError) {
// report errors only if it was requested
if (resolutionDiagnostic) {
error(errorNode, resolutionDiagnostic, moduleName, resolvedModule.resolvedFileName);
}
else {
const tsExtension = tryExtractTypeScriptExtension(moduleName);
if (tsExtension) {
const diag = Diagnostics.An_import_path_cannot_end_with_a_0_extension_Consider_importing_1_instead;
error(errorNode, diag, tsExtension, removeExtension(moduleName, tsExtension));
}
else {
error(errorNode, moduleNotFoundError, moduleName);
}
}
}
return undefined;
}
// An external module with an 'export =' declaration resolves to the target of the 'export =' declaration,
// and an external module with no 'export =' declaration resolves to the module itself.
function resolveExternalModuleSymbol(moduleSymbol: Symbol, dontResolveAlias?: boolean): Symbol {
return moduleSymbol && getMergedSymbol(resolveSymbol(moduleSymbol.exports.get("export="), dontResolveAlias)) || moduleSymbol;
}
// An external module with an 'export =' declaration may be referenced as an ES6 module provided the 'export ='
// references a symbol that is at least declared as a module or a variable. The target of the 'export =' may
// combine other declarations with the module or variable (e.g. a class/module, function/module, interface/variable).
function resolveESModuleSymbol(moduleSymbol: Symbol, moduleReferenceExpression: Expression, dontResolveAlias: boolean): Symbol {
const symbol = resolveExternalModuleSymbol(moduleSymbol, dontResolveAlias);
if (!dontResolveAlias && symbol && !(symbol.flags & (SymbolFlags.Module | SymbolFlags.Variable))) {
error(moduleReferenceExpression, Diagnostics.Module_0_resolves_to_a_non_module_entity_and_cannot_be_imported_using_this_construct, symbolToString(moduleSymbol));
}
return symbol;
}
function hasExportAssignmentSymbol(moduleSymbol: Symbol): boolean {
return moduleSymbol.exports.get("export=") !== undefined;
}
function getExportsOfModuleAsArray(moduleSymbol: Symbol): Symbol[] {
return symbolsToArray(getExportsOfModule(moduleSymbol));
}
function getExportsAndPropertiesOfModule(moduleSymbol: Symbol): Symbol[] {
const exports = getExportsOfModuleAsArray(moduleSymbol);
const exportEquals = resolveExternalModuleSymbol(moduleSymbol);
if (exportEquals !== moduleSymbol) {
addRange(exports, getPropertiesOfType(getTypeOfSymbol(exportEquals)));
}
return exports;
}
function tryGetMemberInModuleExports(memberName: string, moduleSymbol: Symbol): Symbol | undefined {
const symbolTable = getExportsOfModule(moduleSymbol);
if (symbolTable) {
return symbolTable.get(memberName);
}
}
function getExportsOfSymbol(symbol: Symbol): SymbolTable {
return symbol.flags & SymbolFlags.Module ? getExportsOfModule(symbol) : symbol.exports || emptySymbols;
}
function getExportsOfModule(moduleSymbol: Symbol): SymbolTable {
const links = getSymbolLinks(moduleSymbol);
return links.resolvedExports || (links.resolvedExports = getExportsForModule(moduleSymbol));
}
interface ExportCollisionTracker {
specifierText: string;
exportsWithDuplicate: ExportDeclaration[];
}
/**
* Extends one symbol table with another while collecting information on name collisions for error message generation into the `lookupTable` argument
* Not passing `lookupTable` and `exportNode` disables this collection, and just extends the tables
*/
function extendExportSymbols(target: SymbolTable, source: SymbolTable, lookupTable?: Map<ExportCollisionTracker>, exportNode?: ExportDeclaration) {
source && source.forEach((sourceSymbol, id) => {
if (id === "default") return;
const targetSymbol = target.get(id);
if (!targetSymbol) {
target.set(id, sourceSymbol);
if (lookupTable && exportNode) {
lookupTable.set(id, {
specifierText: getTextOfNode(exportNode.moduleSpecifier)
} as ExportCollisionTracker);
}
}
else if (lookupTable && exportNode && targetSymbol && resolveSymbol(targetSymbol) !== resolveSymbol(sourceSymbol)) {
const collisionTracker = lookupTable.get(id);
if (!collisionTracker.exportsWithDuplicate) {
collisionTracker.exportsWithDuplicate = [exportNode];
}
else {
collisionTracker.exportsWithDuplicate.push(exportNode);
}
}
});
}
function getExportsForModule(moduleSymbol: Symbol): SymbolTable {
const visitedSymbols: Symbol[] = [];
// A module defined by an 'export=' consists on one export that needs to be resolved
moduleSymbol = resolveExternalModuleSymbol(moduleSymbol);
return visit(moduleSymbol) || moduleSymbol.exports;
// The ES6 spec permits export * declarations in a module to circularly reference the module itself. For example,
// module 'a' can 'export * from "b"' and 'b' can 'export * from "a"' without error.
function visit(symbol: Symbol): SymbolTable {
if (!(symbol && symbol.flags & SymbolFlags.HasExports && !contains(visitedSymbols, symbol))) {
return;
}
visitedSymbols.push(symbol);
const symbols = cloneMap(symbol.exports);
// All export * declarations are collected in an __export symbol by the binder
const exportStars = symbol.exports.get("__export");
if (exportStars) {
const nestedSymbols = createMap<Symbol>();
const lookupTable = createMap<ExportCollisionTracker>();
for (const node of exportStars.declarations) {
const resolvedModule = resolveExternalModuleName(node, (node as ExportDeclaration).moduleSpecifier);
const exportedSymbols = visit(resolvedModule);
extendExportSymbols(
nestedSymbols,
exportedSymbols,
lookupTable,
node as ExportDeclaration
);
}
lookupTable.forEach(({ exportsWithDuplicate }, id) => {
// It's not an error if the file with multiple `export *`s with duplicate names exports a member with that name itself
if (id === "export=" || !(exportsWithDuplicate && exportsWithDuplicate.length) || symbols.has(id)) {
return;
}
for (const node of exportsWithDuplicate) {
diagnostics.add(createDiagnosticForNode(
node,
Diagnostics.Module_0_has_already_exported_a_member_named_1_Consider_explicitly_re_exporting_to_resolve_the_ambiguity,
lookupTable.get(id).specifierText,
id
));
}
});
extendExportSymbols(symbols, nestedSymbols);
}
return symbols;
}
}
function getMergedSymbol(symbol: Symbol): Symbol {
let merged: Symbol;
return symbol && symbol.mergeId && (merged = mergedSymbols[symbol.mergeId]) ? merged : symbol;
}
function getSymbolOfNode(node: Node): Symbol {
return getMergedSymbol(node.symbol);
}
function getParentOfSymbol(symbol: Symbol): Symbol {
return getMergedSymbol(symbol.parent);
}
function getExportSymbolOfValueSymbolIfExported(symbol: Symbol): Symbol {
return symbol && (symbol.flags & SymbolFlags.ExportValue) !== 0
? getMergedSymbol(symbol.exportSymbol)
: symbol;
}
function symbolIsValue(symbol: Symbol): boolean {
return !!(symbol.flags & SymbolFlags.Value || symbol.flags & SymbolFlags.Alias && resolveAlias(symbol).flags & SymbolFlags.Value);
}
function findConstructorDeclaration(node: ClassLikeDeclaration): ConstructorDeclaration {
const members = node.members;
for (const member of members) {
if (member.kind === SyntaxKind.Constructor && nodeIsPresent((<ConstructorDeclaration>member).body)) {
return <ConstructorDeclaration>member;
}
}
}
function createType(flags: TypeFlags): Type {
const result = new Type(checker, flags);
typeCount++;
result.id = typeCount;
return result;
}
function createIntrinsicType(kind: TypeFlags, intrinsicName: string): IntrinsicType {
const type = <IntrinsicType>createType(kind);
type.intrinsicName = intrinsicName;
return type;
}
function createBooleanType(trueFalseTypes: Type[]): IntrinsicType & UnionType {
const type = <IntrinsicType & UnionType>getUnionType(trueFalseTypes);
type.flags |= TypeFlags.Boolean;
type.intrinsicName = "boolean";
return type;
}
function createObjectType(objectFlags: ObjectFlags, symbol?: Symbol): ObjectType {
const type = <ObjectType>createType(TypeFlags.Object);
type.objectFlags = objectFlags;
type.symbol = symbol;
return type;
}
function createTypeofType() {
return getUnionType(convertToArray(typeofEQFacts.keys(), getLiteralType));
}
// A reserved member name starts with two underscores, but the third character cannot be an underscore
// or the @ symbol. A third underscore indicates an escaped form of an identifer that started
// with at least two underscores. The @ character indicates that the name is denoted by a well known ES
// Symbol instance.
function isReservedMemberName(name: string) {
return name.charCodeAt(0) === CharacterCodes._ &&
name.charCodeAt(1) === CharacterCodes._ &&
name.charCodeAt(2) !== CharacterCodes._ &&
name.charCodeAt(2) !== CharacterCodes.at;
}
function getNamedMembers(members: SymbolTable): Symbol[] {
let result: Symbol[];
members.forEach((symbol, id) => {
if (!isReservedMemberName(id)) {
if (!result) result = [];
if (symbolIsValue(symbol)) {
result.push(symbol);
}
}
});
return result || emptyArray;
}
function setStructuredTypeMembers(type: StructuredType, members: SymbolTable, callSignatures: Signature[], constructSignatures: Signature[], stringIndexInfo: IndexInfo, numberIndexInfo: IndexInfo): ResolvedType {
(<ResolvedType>type).members = members;
(<ResolvedType>type).properties = getNamedMembers(members);
(<ResolvedType>type).callSignatures = callSignatures;
(<ResolvedType>type).constructSignatures = constructSignatures;
if (stringIndexInfo) (<ResolvedType>type).stringIndexInfo = stringIndexInfo;
if (numberIndexInfo) (<ResolvedType>type).numberIndexInfo = numberIndexInfo;
return <ResolvedType>type;
}
function createAnonymousType(symbol: Symbol, members: SymbolTable, callSignatures: Signature[], constructSignatures: Signature[], stringIndexInfo: IndexInfo, numberIndexInfo: IndexInfo): ResolvedType {
return setStructuredTypeMembers(createObjectType(ObjectFlags.Anonymous, symbol),
members, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo);
}
function forEachSymbolTableInScope<T>(enclosingDeclaration: Node, callback: (symbolTable: SymbolTable) => T): T {
let result: T;
for (let location = enclosingDeclaration; location; location = location.parent) {
// Locals of a source file are not in scope (because they get merged into the global symbol table)
if (location.locals && !isGlobalSourceFile(location)) {
if (result = callback(location.locals)) {
return result;
}
}
switch (location.kind) {
case SyntaxKind.SourceFile:
if (!isExternalOrCommonJsModule(<SourceFile>location)) {
break;
}
// falls through
case SyntaxKind.ModuleDeclaration:
if (result = callback(getSymbolOfNode(location).exports)) {
return result;
}
break;
}
}
return callback(globals);
}
function getQualifiedLeftMeaning(rightMeaning: SymbolFlags) {
// If we are looking in value space, the parent meaning is value, other wise it is namespace
return rightMeaning === SymbolFlags.Value ? SymbolFlags.Value : SymbolFlags.Namespace;
}
function getAccessibleSymbolChain(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags, useOnlyExternalAliasing: boolean): Symbol[] {
function getAccessibleSymbolChainFromSymbolTable(symbols: SymbolTable) {
return getAccessibleSymbolChainFromSymbolTableWorker(symbols, []);
}
function getAccessibleSymbolChainFromSymbolTableWorker(symbols: SymbolTable, visitedSymbolTables: SymbolTable[]): Symbol[] {
if (contains(visitedSymbolTables, symbols)) {
return undefined;
}
visitedSymbolTables.push(symbols);
const result = trySymbolTable(symbols);
visitedSymbolTables.pop();
return result;
function canQualifySymbol(symbolFromSymbolTable: Symbol, meaning: SymbolFlags) {
// If the symbol is equivalent and doesn't need further qualification, this symbol is accessible
if (!needsQualification(symbolFromSymbolTable, enclosingDeclaration, meaning)) {
return true;
}
// If symbol needs qualification, make sure that parent is accessible, if it is then this symbol is accessible too
const accessibleParent = getAccessibleSymbolChain(symbolFromSymbolTable.parent, enclosingDeclaration, getQualifiedLeftMeaning(meaning), useOnlyExternalAliasing);
return !!accessibleParent;
}
function isAccessible(symbolFromSymbolTable: Symbol, resolvedAliasSymbol?: Symbol) {
if (symbol === (resolvedAliasSymbol || symbolFromSymbolTable)) {
// if the symbolFromSymbolTable is not external module (it could be if it was determined as ambient external module and would be in globals table)
// and if symbolFromSymbolTable or alias resolution matches the symbol,
// check the symbol can be qualified, it is only then this symbol is accessible
return !forEach(symbolFromSymbolTable.declarations, hasExternalModuleSymbol) &&
canQualifySymbol(symbolFromSymbolTable, meaning);
}
}
function trySymbolTable(symbols: SymbolTable) {
// If symbol is directly available by its name in the symbol table
if (isAccessible(symbols.get(symbol.name))) {
return [symbol];
}
// Check if symbol is any of the alias
return forEachEntry(symbols, symbolFromSymbolTable => {
if (symbolFromSymbolTable.flags & SymbolFlags.Alias
&& symbolFromSymbolTable.name !== "export="
&& !getDeclarationOfKind(symbolFromSymbolTable, SyntaxKind.ExportSpecifier)) {
if (!useOnlyExternalAliasing || // We can use any type of alias to get the name
// Is this external alias, then use it to name
ts.forEach(symbolFromSymbolTable.declarations, isExternalModuleImportEqualsDeclaration)) {
const resolvedImportedSymbol = resolveAlias(symbolFromSymbolTable);
if (isAccessible(symbolFromSymbolTable, resolvedImportedSymbol)) {
return [symbolFromSymbolTable];
}
// Look in the exported members, if we can find accessibleSymbolChain, symbol is accessible using this chain
// but only if the symbolFromSymbolTable can be qualified
const accessibleSymbolsFromExports = resolvedImportedSymbol.exports ? getAccessibleSymbolChainFromSymbolTableWorker(resolvedImportedSymbol.exports, visitedSymbolTables) : undefined;
if (accessibleSymbolsFromExports && canQualifySymbol(symbolFromSymbolTable, getQualifiedLeftMeaning(meaning))) {
return [symbolFromSymbolTable].concat(accessibleSymbolsFromExports);
}
}
}
});
}
}
if (symbol) {
if (!(isPropertyOrMethodDeclarationSymbol(symbol))) {
return forEachSymbolTableInScope(enclosingDeclaration, getAccessibleSymbolChainFromSymbolTable);
}
}
}
function needsQualification(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags) {
let qualify = false;
forEachSymbolTableInScope(enclosingDeclaration, symbolTable => {
// If symbol of this name is not available in the symbol table we are ok
let symbolFromSymbolTable = symbolTable.get(symbol.name);
if (!symbolFromSymbolTable) {
// Continue to the next symbol table
return false;
}
// If the symbol with this name is present it should refer to the symbol
if (symbolFromSymbolTable === symbol) {
// No need to qualify
return true;
}
// Qualify if the symbol from symbol table has same meaning as expected
symbolFromSymbolTable = (symbolFromSymbolTable.flags & SymbolFlags.Alias && !getDeclarationOfKind(symbolFromSymbolTable, SyntaxKind.ExportSpecifier)) ? resolveAlias(symbolFromSymbolTable) : symbolFromSymbolTable;
if (symbolFromSymbolTable.flags & meaning) {
qualify = true;
return true;
}
// Continue to the next symbol table
return false;
});
return qualify;
}
function isPropertyOrMethodDeclarationSymbol(symbol: Symbol) {
if (symbol.declarations && symbol.declarations.length) {
for (const declaration of symbol.declarations) {
switch (declaration.kind) {
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
continue;
default:
return false;
}
}
return true;
}
return false;
}
/**
* Check if the given symbol in given enclosing declaration is accessible and mark all associated alias to be visible if requested
*
* @param symbol a Symbol to check if accessible
* @param enclosingDeclaration a Node containing reference to the symbol
* @param meaning a SymbolFlags to check if such meaning of the symbol is accessible
* @param shouldComputeAliasToMakeVisible a boolean value to indicate whether to return aliases to be mark visible in case the symbol is accessible
*/
function isSymbolAccessible(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags, shouldComputeAliasesToMakeVisible: boolean): SymbolAccessibilityResult {
if (symbol && enclosingDeclaration && !(symbol.flags & SymbolFlags.TypeParameter)) {
const initialSymbol = symbol;
let meaningToLook = meaning;
while (symbol) {
// Symbol is accessible if it by itself is accessible
const accessibleSymbolChain = getAccessibleSymbolChain(symbol, enclosingDeclaration, meaningToLook, /*useOnlyExternalAliasing*/ false);
if (accessibleSymbolChain) {
const hasAccessibleDeclarations = hasVisibleDeclarations(accessibleSymbolChain[0], shouldComputeAliasesToMakeVisible);
if (!hasAccessibleDeclarations) {
return <SymbolAccessibilityResult>{
accessibility: SymbolAccessibility.NotAccessible,
errorSymbolName: symbolToString(initialSymbol, enclosingDeclaration, meaning),
errorModuleName: symbol !== initialSymbol ? symbolToString(symbol, enclosingDeclaration, SymbolFlags.Namespace) : undefined,
};
}
return hasAccessibleDeclarations;
}
// If we haven't got the accessible symbol, it doesn't mean the symbol is actually inaccessible.
// It could be a qualified symbol and hence verify the path
// e.g.:
// module m {
// export class c {
// }
// }
// const x: typeof m.c
// In the above example when we start with checking if typeof m.c symbol is accessible,
// we are going to see if c can be accessed in scope directly.
// But it can't, hence the accessible is going to be undefined, but that doesn't mean m.c is inaccessible
// It is accessible if the parent m is accessible because then m.c can be accessed through qualification
meaningToLook = getQualifiedLeftMeaning(meaning);
symbol = getParentOfSymbol(symbol);
}
// This could be a symbol that is not exported in the external module
// or it could be a symbol from different external module that is not aliased and hence cannot be named
const symbolExternalModule = forEach(initialSymbol.declarations, getExternalModuleContainer);
if (symbolExternalModule) {
const enclosingExternalModule = getExternalModuleContainer(enclosingDeclaration);
if (symbolExternalModule !== enclosingExternalModule) {
// name from different external module that is not visible
return {
accessibility: SymbolAccessibility.CannotBeNamed,
errorSymbolName: symbolToString(initialSymbol, enclosingDeclaration, meaning),
errorModuleName: symbolToString(symbolExternalModule)
};
}
}
// Just a local name that is not accessible
return {
accessibility: SymbolAccessibility.NotAccessible,
errorSymbolName: symbolToString(initialSymbol, enclosingDeclaration, meaning),
};
}
return { accessibility: SymbolAccessibility.Accessible };
function getExternalModuleContainer(declaration: Node) {
const node = findAncestor(declaration, hasExternalModuleSymbol);
return node && getSymbolOfNode(node);
}
}
function hasExternalModuleSymbol(declaration: Node) {
return isAmbientModule(declaration) || (declaration.kind === SyntaxKind.SourceFile && isExternalOrCommonJsModule(<SourceFile>declaration));
}
function hasVisibleDeclarations(symbol: Symbol, shouldComputeAliasToMakeVisible: boolean): SymbolVisibilityResult {
let aliasesToMakeVisible: AnyImportSyntax[];
if (forEach(symbol.declarations, declaration => !getIsDeclarationVisible(declaration))) {
return undefined;
}
return { accessibility: SymbolAccessibility.Accessible, aliasesToMakeVisible };
function getIsDeclarationVisible(declaration: Declaration) {
if (!isDeclarationVisible(declaration)) {
// Mark the unexported alias as visible if its parent is visible
// because these kind of aliases can be used to name types in declaration file
const anyImportSyntax = getAnyImportSyntax(declaration);
if (anyImportSyntax &&
!(getModifierFlags(anyImportSyntax) & ModifierFlags.Export) && // import clause without export
isDeclarationVisible(<Declaration>anyImportSyntax.parent)) {
// In function "buildTypeDisplay" where we decide whether to write type-alias or serialize types,
// we want to just check if type- alias is accessible or not but we don't care about emitting those alias at that time
// since we will do the emitting later in trackSymbol.
if (shouldComputeAliasToMakeVisible) {
getNodeLinks(declaration).isVisible = true;
if (aliasesToMakeVisible) {
if (!contains(aliasesToMakeVisible, anyImportSyntax)) {
aliasesToMakeVisible.push(anyImportSyntax);
}
}
else {
aliasesToMakeVisible = [anyImportSyntax];
}
}
return true;
}
// Declaration is not visible
return false;
}
return true;
}
}
function isEntityNameVisible(entityName: EntityNameOrEntityNameExpression, enclosingDeclaration: Node): SymbolVisibilityResult {
// get symbol of the first identifier of the entityName
let meaning: SymbolFlags;
if (entityName.parent.kind === SyntaxKind.TypeQuery || isExpressionWithTypeArgumentsInClassExtendsClause(entityName.parent)) {
// Typeof value
meaning = SymbolFlags.Value | SymbolFlags.ExportValue;
}
else if (entityName.kind === SyntaxKind.QualifiedName || entityName.kind === SyntaxKind.PropertyAccessExpression ||
entityName.parent.kind === SyntaxKind.ImportEqualsDeclaration) {
// Left identifier from type reference or TypeAlias
// Entity name of the import declaration
meaning = SymbolFlags.Namespace;
}
else {
// Type Reference or TypeAlias entity = Identifier
meaning = SymbolFlags.Type;
}
const firstIdentifier = getFirstIdentifier(entityName);
const symbol = resolveName(enclosingDeclaration, (<Identifier>firstIdentifier).text, meaning, /*nodeNotFoundErrorMessage*/ undefined, /*nameArg*/ undefined);
// Verify if the symbol is accessible
return (symbol && hasVisibleDeclarations(symbol, /*shouldComputeAliasToMakeVisible*/ true)) || <SymbolVisibilityResult>{
accessibility: SymbolAccessibility.NotAccessible,
errorSymbolName: getTextOfNode(firstIdentifier),
errorNode: firstIdentifier
};
}
function writeKeyword(writer: SymbolWriter, kind: SyntaxKind) {
writer.writeKeyword(tokenToString(kind));
}
function writePunctuation(writer: SymbolWriter, kind: SyntaxKind) {
writer.writePunctuation(tokenToString(kind));
}
function writeSpace(writer: SymbolWriter) {
writer.writeSpace(" ");
}
function symbolToString(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): string {
const writer = getSingleLineStringWriter();
getSymbolDisplayBuilder().buildSymbolDisplay(symbol, writer, enclosingDeclaration, meaning);
const result = writer.string();
releaseStringWriter(writer);
return result;
}
function signatureToString(signature: Signature, enclosingDeclaration?: Node, flags?: TypeFormatFlags, kind?: SignatureKind): string {
const writer = getSingleLineStringWriter();
getSymbolDisplayBuilder().buildSignatureDisplay(signature, writer, enclosingDeclaration, flags, kind);
const result = writer.string();
releaseStringWriter(writer);
return result;
}
function typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string {
const typeNode = nodeBuilder.typeToTypeNode(type, enclosingDeclaration, toNodeBuilderFlags(flags) | NodeBuilderFlags.IgnoreErrors | NodeBuilderFlags.WriteTypeParametersInQualifiedName);
Debug.assert(typeNode !== undefined, "should always get typenode?");
const options = { removeComments: true };
const writer = createTextWriter("");
const printer = createPrinter(options);
const sourceFile = enclosingDeclaration && getSourceFileOfNode(enclosingDeclaration);
printer.writeNode(EmitHint.Unspecified, typeNode, /*sourceFile*/ sourceFile, writer);
const result = writer.getText();
const maxLength = compilerOptions.noErrorTruncation || flags & TypeFormatFlags.NoTruncation ? undefined : 100;
if (maxLength && result.length >= maxLength) {
return result.substr(0, maxLength - "...".length) + "...";
}
return result;
function toNodeBuilderFlags(flags?: TypeFormatFlags): NodeBuilderFlags {
let result = NodeBuilderFlags.None;
if (!flags) {
return result;
}
if (flags & TypeFormatFlags.NoTruncation) {
result |= NodeBuilderFlags.NoTruncation;
}
if (flags & TypeFormatFlags.UseFullyQualifiedType) {
result |= NodeBuilderFlags.UseFullyQualifiedType;
}
if (flags & TypeFormatFlags.SuppressAnyReturnType) {
result |= NodeBuilderFlags.SuppressAnyReturnType;
}
if (flags & TypeFormatFlags.WriteArrayAsGenericType) {
result |= NodeBuilderFlags.WriteArrayAsGenericType;
}
if (flags & TypeFormatFlags.WriteTypeArgumentsOfSignature) {
result |= NodeBuilderFlags.WriteTypeArgumentsOfSignature;
}
return result;
}
}
function createNodeBuilder() {
return {
typeToTypeNode: (type: Type, enclosingDeclaration?: Node, flags?: NodeBuilderFlags) => {
const context = createNodeBuilderContext(enclosingDeclaration, flags);
const resultingNode = typeToTypeNodeHelper(type, context);
const result = context.encounteredError ? undefined : resultingNode;
return result;
},
indexInfoToIndexSignatureDeclaration: (indexInfo: IndexInfo, kind: IndexKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags) => {
const context = createNodeBuilderContext(enclosingDeclaration, flags);
const resultingNode = indexInfoToIndexSignatureDeclarationHelper(indexInfo, kind, context);
const result = context.encounteredError ? undefined : resultingNode;
return result;
},
signatureToSignatureDeclaration: (signature: Signature, kind: SyntaxKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags) => {
const context = createNodeBuilderContext(enclosingDeclaration, flags);
const resultingNode = signatureToSignatureDeclarationHelper(signature, kind, context);
const result = context.encounteredError ? undefined : resultingNode;
return result;
}
};
interface NodeBuilderContext {
enclosingDeclaration: Node | undefined;
flags: NodeBuilderFlags | undefined;
// State
encounteredError: boolean;
symbolStack: Symbol[] | undefined;
}
function createNodeBuilderContext(enclosingDeclaration: Node | undefined, flags: NodeBuilderFlags | undefined): NodeBuilderContext {
return {
enclosingDeclaration,
flags,
encounteredError: false,
symbolStack: undefined
};
}
function typeToTypeNodeHelper(type: Type, context: NodeBuilderContext): TypeNode {
const inTypeAlias = context.flags & NodeBuilderFlags.InTypeAlias;
context.flags &= ~NodeBuilderFlags.InTypeAlias;
if (!type) {
context.encounteredError = true;
return undefined;
}
if (type.flags & TypeFlags.Any) {
return createKeywordTypeNode(SyntaxKind.AnyKeyword);
}
if (type.flags & TypeFlags.String) {
return createKeywordTypeNode(SyntaxKind.StringKeyword);
}
if (type.flags & TypeFlags.Number) {
return createKeywordTypeNode(SyntaxKind.NumberKeyword);
}
if (type.flags & TypeFlags.Boolean) {
return createKeywordTypeNode(SyntaxKind.BooleanKeyword);
}
if (type.flags & TypeFlags.EnumLiteral && !(type.flags & TypeFlags.Union)) {
const parentSymbol = getParentOfSymbol(type.symbol);
const parentName = symbolToName(parentSymbol, context, SymbolFlags.Type, /*expectsIdentifier*/ false);
const enumLiteralName = getDeclaredTypeOfSymbol(parentSymbol) === type ? parentName : createQualifiedName(parentName, getNameOfSymbol(type.symbol, context));
return createTypeReferenceNode(enumLiteralName, /*typeArguments*/ undefined);
}
if (type.flags & TypeFlags.EnumLike) {
const name = symbolToName(type.symbol, context, SymbolFlags.Type, /*expectsIdentifier*/ false);
return createTypeReferenceNode(name, /*typeArguments*/ undefined);
}
if (type.flags & (TypeFlags.StringLiteral)) {
return createLiteralTypeNode(setEmitFlags(createLiteral((<StringLiteralType>type).value), EmitFlags.NoAsciiEscaping));
}
if (type.flags & (TypeFlags.NumberLiteral)) {
return createLiteralTypeNode((createLiteral((<NumberLiteralType>type).value)));
}
if (type.flags & TypeFlags.BooleanLiteral) {
return (<IntrinsicType>type).intrinsicName === "true" ? createTrue() : createFalse();
}
if (type.flags & TypeFlags.Void) {
return createKeywordTypeNode(SyntaxKind.VoidKeyword);
}
if (type.flags & TypeFlags.Undefined) {
return createKeywordTypeNode(SyntaxKind.UndefinedKeyword);
}
if (type.flags & TypeFlags.Null) {
return createKeywordTypeNode(SyntaxKind.NullKeyword);
}
if (type.flags & TypeFlags.Never) {
return createKeywordTypeNode(SyntaxKind.NeverKeyword);
}
if (type.flags & TypeFlags.ESSymbol) {
return createKeywordTypeNode(SyntaxKind.SymbolKeyword);
}
if (type.flags & TypeFlags.NonPrimitive) {
return createKeywordTypeNode(SyntaxKind.ObjectKeyword);
}
if (type.flags & TypeFlags.TypeParameter && (type as TypeParameter).isThisType) {
if (context.flags & NodeBuilderFlags.InObjectTypeLiteral) {
if (!context.encounteredError && !(context.flags & NodeBuilderFlags.AllowThisInObjectLiteral)) {
context.encounteredError = true;
}
}
return createThis();
}
const objectFlags = getObjectFlags(type);
if (objectFlags & ObjectFlags.Reference) {
Debug.assert(!!(type.flags & TypeFlags.Object));
return typeReferenceToTypeNode(<TypeReference>type);
}
if (type.flags & TypeFlags.TypeParameter || objectFlags & ObjectFlags.ClassOrInterface) {
const name = symbolToName(type.symbol, context, SymbolFlags.Type, /*expectsIdentifier*/ false);
// Ignore constraint/default when creating a usage (as opposed to declaration) of a type parameter.
return createTypeReferenceNode(name, /*typeArguments*/ undefined);
}
if (!inTypeAlias && type.aliasSymbol &&
isSymbolAccessible(type.aliasSymbol, context.enclosingDeclaration, SymbolFlags.Type, /*shouldComputeAliasesToMakeVisible*/ false).accessibility === SymbolAccessibility.Accessible) {
const name = symbolToTypeReferenceName(type.aliasSymbol);
const typeArgumentNodes = mapToTypeNodes(type.aliasTypeArguments, context);
return createTypeReferenceNode(name, typeArgumentNodes);
}
if (type.flags & (TypeFlags.Union | TypeFlags.Intersection)) {
const types = type.flags & TypeFlags.Union ? formatUnionTypes((<UnionType>type).types) : (<IntersectionType>type).types;
const typeNodes = mapToTypeNodes(types, context);
if (typeNodes && typeNodes.length > 0) {
const unionOrIntersectionTypeNode = createUnionOrIntersectionTypeNode(type.flags & TypeFlags.Union ? SyntaxKind.UnionType : SyntaxKind.IntersectionType, typeNodes);
return unionOrIntersectionTypeNode;
}
else {
if (!context.encounteredError && !(context.flags & NodeBuilderFlags.AllowEmptyUnionOrIntersection)) {
context.encounteredError = true;
}
return undefined;
}
}
if (objectFlags & (ObjectFlags.Anonymous | ObjectFlags.Mapped)) {
Debug.assert(!!(type.flags & TypeFlags.Object));
// The type is an object literal type.
return createAnonymousTypeNode(<ObjectType>type);
}
if (type.flags & TypeFlags.Index) {
const indexedType = (<IndexType>type).type;
const indexTypeNode = typeToTypeNodeHelper(indexedType, context);
return createTypeOperatorNode(indexTypeNode);
}
if (type.flags & TypeFlags.IndexedAccess) {
const objectTypeNode = typeToTypeNodeHelper((<IndexedAccessType>type).objectType, context);
const indexTypeNode = typeToTypeNodeHelper((<IndexedAccessType>type).indexType, context);
return createIndexedAccessTypeNode(objectTypeNode, indexTypeNode);
}
Debug.fail("Should be unreachable.");
function createMappedTypeNodeFromType(type: MappedType) {
Debug.assert(!!(type.flags & TypeFlags.Object));
const readonlyToken = type.declaration && type.declaration.readonlyToken ? createToken(SyntaxKind.ReadonlyKeyword) : undefined;
const questionToken = type.declaration && type.declaration.questionToken ? createToken(SyntaxKind.QuestionToken) : undefined;
const typeParameterNode = typeParameterToDeclaration(getTypeParameterFromMappedType(type), context);
const templateTypeNode = typeToTypeNodeHelper(getTemplateTypeFromMappedType(type), context);
const mappedTypeNode = createMappedTypeNode(readonlyToken, typeParameterNode, questionToken, templateTypeNode);
return setEmitFlags(mappedTypeNode, EmitFlags.SingleLine);
}
function createAnonymousTypeNode(type: ObjectType): TypeNode {
const symbol = type.symbol;
if (symbol) {
// Always use 'typeof T' for type of class, enum, and module objects
if (symbol.flags & SymbolFlags.Class && !getBaseTypeVariableOfClass(symbol) ||
symbol.flags & (SymbolFlags.Enum | SymbolFlags.ValueModule) ||
shouldWriteTypeOfFunctionSymbol()) {
return createTypeQueryNodeFromSymbol(symbol, SymbolFlags.Value);
}
else if (contains(context.symbolStack, symbol)) {
// If type is an anonymous type literal in a type alias declaration, use type alias name
const typeAlias = getTypeAliasForTypeLiteral(type);
if (typeAlias) {
// The specified symbol flags need to be reinterpreted as type flags
const entityName = symbolToName(typeAlias, context, SymbolFlags.Type, /*expectsIdentifier*/ false);
return createTypeReferenceNode(entityName, /*typeArguments*/ undefined);
}
else {
return createKeywordTypeNode(SyntaxKind.AnyKeyword);
}
}
else {
// Since instantiations of the same anonymous type have the same symbol, tracking symbols instead
// of types allows us to catch circular references to instantiations of the same anonymous type
if (!context.symbolStack) {
context.symbolStack = [];
}
context.symbolStack.push(symbol);
const result = createTypeNodeFromObjectType(type);
context.symbolStack.pop();
return result;
}
}
else {
// Anonymous types without a symbol are never circular.
return createTypeNodeFromObjectType(type);
}
function shouldWriteTypeOfFunctionSymbol() {
const isStaticMethodSymbol = !!(symbol.flags & SymbolFlags.Method && // typeof static method
forEach(symbol.declarations, declaration => getModifierFlags(declaration) & ModifierFlags.Static));
const isNonLocalFunctionSymbol = !!(symbol.flags & SymbolFlags.Function) &&
(symbol.parent || // is exported function symbol
forEach(symbol.declarations, declaration =>
declaration.parent.kind === SyntaxKind.SourceFile || declaration.parent.kind === SyntaxKind.ModuleBlock));
if (isStaticMethodSymbol || isNonLocalFunctionSymbol) {
// typeof is allowed only for static/non local functions
return contains(context.symbolStack, symbol); // it is type of the symbol uses itself recursively
}
}
}
function createTypeNodeFromObjectType(type: ObjectType): TypeNode {
if (type.objectFlags & ObjectFlags.Mapped) {
if (getConstraintTypeFromMappedType(<MappedType>type).flags & (TypeFlags.TypeParameter | TypeFlags.Index)) {
return createMappedTypeNodeFromType(<MappedType>type);
}
}
const resolved = resolveStructuredTypeMembers(type);
if (!resolved.properties.length && !resolved.stringIndexInfo && !resolved.numberIndexInfo) {
if (!resolved.callSignatures.length && !resolved.constructSignatures.length) {
return setEmitFlags(createTypeLiteralNode(/*members*/ undefined), EmitFlags.SingleLine);
}
if (resolved.callSignatures.length === 1 && !resolved.constructSignatures.length) {
const signature = resolved.callSignatures[0];
const signatureNode = <FunctionTypeNode>signatureToSignatureDeclarationHelper(signature, SyntaxKind.FunctionType, context);
return signatureNode;
}
if (resolved.constructSignatures.length === 1 && !resolved.callSignatures.length) {
const signature = resolved.constructSignatures[0];
const signatureNode = <ConstructorTypeNode>signatureToSignatureDeclarationHelper(signature, SyntaxKind.ConstructorType, context);
return signatureNode;
}
}
const savedFlags = context.flags;
context.flags |= NodeBuilderFlags.InObjectTypeLiteral;
const members = createTypeNodesFromResolvedType(resolved);
context.flags = savedFlags;
const typeLiteralNode = createTypeLiteralNode(members);
return setEmitFlags(typeLiteralNode, EmitFlags.SingleLine);
}
function createTypeQueryNodeFromSymbol(symbol: Symbol, symbolFlags: SymbolFlags) {
const entityName = symbolToName(symbol, context, symbolFlags, /*expectsIdentifier*/ false);
return createTypeQueryNode(entityName);
}
function symbolToTypeReferenceName(symbol: Symbol) {
// Unnamed function expressions and arrow functions have reserved names that we don't want to display
const entityName = symbol.flags & SymbolFlags.Class || !isReservedMemberName(symbol.name) ? symbolToName(symbol, context, SymbolFlags.Type, /*expectsIdentifier*/ false) : createIdentifier("");
return entityName;
}
function typeReferenceToTypeNode(type: TypeReference) {
const typeArguments: Type[] = type.typeArguments || emptyArray;
if (type.target === globalArrayType) {
if (context.flags & NodeBuilderFlags.WriteArrayAsGenericType) {
const typeArgumentNode = typeToTypeNodeHelper(typeArguments[0], context);
return createTypeReferenceNode("Array", [typeArgumentNode]);
}
const elementType = typeToTypeNodeHelper(typeArguments[0], context);
return createArrayTypeNode(elementType);
}
else if (type.target.objectFlags & ObjectFlags.Tuple) {
if (typeArguments.length > 0) {
const tupleConstituentNodes = mapToTypeNodes(typeArguments.slice(0, getTypeReferenceArity(type)), context);
if (tupleConstituentNodes && tupleConstituentNodes.length > 0) {
return createTupleTypeNode(tupleConstituentNodes);
}
}
if (!context.encounteredError && !(context.flags & NodeBuilderFlags.AllowEmptyTuple)) {
context.encounteredError = true;
}
return undefined;
}
else {
const outerTypeParameters = type.target.outerTypeParameters;
let i = 0;
let qualifiedName: QualifiedName | undefined;
if (outerTypeParameters) {
const length = outerTypeParameters.length;
while (i < length) {
// Find group of type arguments for type parameters with the same declaring container.
const start = i;
const parent = getParentSymbolOfTypeParameter(outerTypeParameters[i]);
do {
i++;
} while (i < length && getParentSymbolOfTypeParameter(outerTypeParameters[i]) === parent);
// When type parameters are their own type arguments for the whole group (i.e. we have
// the default outer type arguments), we don't show the group.
if (!rangeEquals(outerTypeParameters, typeArguments, start, i)) {
const typeArgumentSlice = mapToTypeNodes(typeArguments.slice(start, i), context);
const typeArgumentNodes = typeArgumentSlice && createNodeArray(typeArgumentSlice);
const namePart = symbolToTypeReferenceName(parent);
(namePart.kind === SyntaxKind.Identifier ? <Identifier>namePart : namePart.right).typeArguments = typeArgumentNodes;
if (qualifiedName) {
Debug.assert(!qualifiedName.right);
qualifiedName = addToQualifiedNameMissingRightIdentifier(qualifiedName, namePart);
qualifiedName = createQualifiedName(qualifiedName, /*right*/ undefined);
}
else {
qualifiedName = createQualifiedName(namePart, /*right*/ undefined);
}
}
}
}
let entityName: EntityName = undefined;
const nameIdentifier = symbolToTypeReferenceName(type.symbol);
if (qualifiedName) {
Debug.assert(!qualifiedName.right);
qualifiedName = addToQualifiedNameMissingRightIdentifier(qualifiedName, nameIdentifier);
entityName = qualifiedName;
}
else {
entityName = nameIdentifier;
}
let typeArgumentNodes: TypeNode[] | undefined;
if (typeArguments.length > 0) {
const typeParameterCount = (type.target.typeParameters || emptyArray).length;
typeArgumentNodes = mapToTypeNodes(typeArguments.slice(i, typeParameterCount), context);
}
if (typeArgumentNodes) {
const lastIdentifier = entityName.kind === SyntaxKind.Identifier ? <Identifier>entityName : entityName.right;
lastIdentifier.typeArguments = undefined;
}
return createTypeReferenceNode(entityName, typeArgumentNodes);
}
}
function addToQualifiedNameMissingRightIdentifier(left: QualifiedName, right: Identifier | QualifiedName) {
Debug.assert(left.right === undefined);
if (right.kind === SyntaxKind.Identifier) {
left.right = right;
return left;
}
let rightPart = right;
while (rightPart.left.kind !== SyntaxKind.Identifier) {
rightPart = rightPart.left;
}
left.right = <Identifier>rightPart.left;
rightPart.left = left;
return right;
}
function createTypeNodesFromResolvedType(resolvedType: ResolvedType): TypeElement[] {
const typeElements: TypeElement[] = [];
for (const signature of resolvedType.callSignatures) {
typeElements.push(<CallSignatureDeclaration>signatureToSignatureDeclarationHelper(signature, SyntaxKind.CallSignature, context));
}
for (const signature of resolvedType.constructSignatures) {
typeElements.push(<ConstructSignatureDeclaration>signatureToSignatureDeclarationHelper(signature, SyntaxKind.ConstructSignature, context));
}
if (resolvedType.stringIndexInfo) {
typeElements.push(indexInfoToIndexSignatureDeclarationHelper(resolvedType.stringIndexInfo, IndexKind.String, context));
}
if (resolvedType.numberIndexInfo) {
typeElements.push(indexInfoToIndexSignatureDeclarationHelper(resolvedType.numberIndexInfo, IndexKind.Number, context));
}
const properties = resolvedType.properties;
if (!properties) {
return typeElements;
}
for (const propertySymbol of properties) {
const propertyType = getTypeOfSymbol(propertySymbol);
const saveEnclosingDeclaration = context.enclosingDeclaration;
context.enclosingDeclaration = undefined;
const propertyName = symbolToName(propertySymbol, context, SymbolFlags.Value, /*expectsIdentifier*/ true);
context.enclosingDeclaration = saveEnclosingDeclaration;
const optionalToken = propertySymbol.flags & SymbolFlags.Optional ? createToken(SyntaxKind.QuestionToken) : undefined;
if (propertySymbol.flags & (SymbolFlags.Function | SymbolFlags.Method) && !getPropertiesOfObjectType(propertyType).length) {
const signatures = getSignaturesOfType(propertyType, SignatureKind.Call);
for (const signature of signatures) {
const methodDeclaration = <MethodSignature>signatureToSignatureDeclarationHelper(signature, SyntaxKind.MethodSignature, context);
methodDeclaration.name = propertyName;
methodDeclaration.questionToken = optionalToken;
typeElements.push(methodDeclaration);
}
}
else {
const propertyTypeNode = propertyType ? typeToTypeNodeHelper(propertyType, context) : createKeywordTypeNode(SyntaxKind.AnyKeyword);
const modifiers = isReadonlySymbol(propertySymbol) ? [createToken(SyntaxKind.ReadonlyKeyword)] : undefined;
const propertySignature = createPropertySignature(
modifiers,
propertyName,
optionalToken,
propertyTypeNode,
/*initializer*/ undefined);
typeElements.push(propertySignature);
}
}
return typeElements.length ? typeElements : undefined;
}
}
function mapToTypeNodes(types: Type[], context: NodeBuilderContext): TypeNode[] {
if (some(types)) {
const result = [];
for (let i = 0; i < types.length; ++i) {
const type = types[i];
const typeNode = typeToTypeNodeHelper(type, context);
if (typeNode) {
result.push(typeNode);
}
}
return result;
}
}
function indexInfoToIndexSignatureDeclarationHelper(indexInfo: IndexInfo, kind: IndexKind, context: NodeBuilderContext): IndexSignatureDeclaration {
const name = getNameFromIndexInfo(indexInfo) || "x";
const indexerTypeNode = createKeywordTypeNode(kind === IndexKind.String ? SyntaxKind.StringKeyword : SyntaxKind.NumberKeyword);
const indexingParameter = createParameter(
/*decorators*/ undefined,
/*modifiers*/ undefined,
/*dotDotDotToken*/ undefined,
name,
/*questionToken*/ undefined,
indexerTypeNode,
/*initializer*/ undefined);
const typeNode = typeToTypeNodeHelper(indexInfo.type, context);
return createIndexSignature(
/*decorators*/ undefined,
indexInfo.isReadonly ? [createToken(SyntaxKind.ReadonlyKeyword)] : undefined,
[indexingParameter],
typeNode);
}
function signatureToSignatureDeclarationHelper(signature: Signature, kind: SyntaxKind, context: NodeBuilderContext): SignatureDeclaration {
const typeParameters = signature.typeParameters && signature.typeParameters.map(parameter => typeParameterToDeclaration(parameter, context));
const parameters = signature.parameters.map(parameter => symbolToParameterDeclaration(parameter, context));
if (signature.thisParameter) {
const thisParameter = symbolToParameterDeclaration(signature.thisParameter, context);
parameters.unshift(thisParameter);
}
let returnTypeNode: TypeNode;
if (signature.typePredicate) {
const typePredicate = signature.typePredicate;
const parameterName = typePredicate.kind === TypePredicateKind.Identifier ?
setEmitFlags(createIdentifier((<IdentifierTypePredicate>typePredicate).parameterName), EmitFlags.NoAsciiEscaping) :
createThisTypeNode();
const typeNode = typeToTypeNodeHelper(typePredicate.type, context);
returnTypeNode = createTypePredicateNode(parameterName, typeNode);
}
else {
const returnType = getReturnTypeOfSignature(signature);
returnTypeNode = returnType && typeToTypeNodeHelper(returnType, context);
}
if (context.flags & NodeBuilderFlags.SuppressAnyReturnType) {
if (returnTypeNode && returnTypeNode.kind === SyntaxKind.AnyKeyword) {
returnTypeNode = undefined;
}
}
else if (!returnTypeNode) {
returnTypeNode = createKeywordTypeNode(SyntaxKind.AnyKeyword);
}
return createSignatureDeclaration(kind, typeParameters, parameters, returnTypeNode);
}
function typeParameterToDeclaration(type: TypeParameter, context: NodeBuilderContext): TypeParameterDeclaration {
const name = symbolToName(type.symbol, context, SymbolFlags.Type, /*expectsIdentifier*/ true);
const constraint = getConstraintFromTypeParameter(type);
const constraintNode = constraint && typeToTypeNodeHelper(constraint, context);
const defaultParameter = getDefaultFromTypeParameter(type);
const defaultParameterNode = defaultParameter && typeToTypeNodeHelper(defaultParameter, context);
return createTypeParameterDeclaration(name, constraintNode, defaultParameterNode);
}
function symbolToParameterDeclaration(parameterSymbol: Symbol, context: NodeBuilderContext): ParameterDeclaration {
const parameterDeclaration = getDeclarationOfKind<ParameterDeclaration>(parameterSymbol, SyntaxKind.Parameter);
const modifiers = parameterDeclaration.modifiers && parameterDeclaration.modifiers.map(getSynthesizedClone);
const dotDotDotToken = isRestParameter(parameterDeclaration) ? createToken(SyntaxKind.DotDotDotToken) : undefined;
const name = parameterDeclaration.name.kind === SyntaxKind.Identifier ?
setEmitFlags(getSynthesizedClone(parameterDeclaration.name), EmitFlags.NoAsciiEscaping) :
cloneBindingName(parameterDeclaration.name);
const questionToken = isOptionalParameter(parameterDeclaration) ? createToken(SyntaxKind.QuestionToken) : undefined;
let parameterType = getTypeOfSymbol(parameterSymbol);
if (isRequiredInitializedParameter(parameterDeclaration)) {
parameterType = getNullableType(parameterType, TypeFlags.Undefined);
}
const parameterTypeNode = typeToTypeNodeHelper(parameterType, context);
const parameterNode = createParameter(
/*decorators*/ undefined,
modifiers,
dotDotDotToken,
name,
questionToken,
parameterTypeNode,
/*initializer*/ undefined);
return parameterNode;
function cloneBindingName(node: BindingName): BindingName {
return <BindingName>elideInitializerAndSetEmitFlags(node);
function elideInitializerAndSetEmitFlags(node: Node): Node {
const visited = visitEachChild(node, elideInitializerAndSetEmitFlags, nullTransformationContext, /*nodesVisitor*/ undefined, elideInitializerAndSetEmitFlags);
const clone = nodeIsSynthesized(visited) ? visited : getSynthesizedClone(visited);
if (clone.kind === SyntaxKind.BindingElement) {
(<BindingElement>clone).initializer = undefined;
}
return setEmitFlags(clone, EmitFlags.SingleLine | EmitFlags.NoAsciiEscaping);
}
}
}
function symbolToName(symbol: Symbol, context: NodeBuilderContext, meaning: SymbolFlags, expectsIdentifier: true): Identifier;
function symbolToName(symbol: Symbol, context: NodeBuilderContext, meaning: SymbolFlags, expectsIdentifier: false): EntityName;
function symbolToName(symbol: Symbol, context: NodeBuilderContext, meaning: SymbolFlags, expectsIdentifier: boolean): EntityName {
// Try to get qualified name if the symbol is not a type parameter and there is an enclosing declaration.
let chain: Symbol[];
const isTypeParameter = symbol.flags & SymbolFlags.TypeParameter;
if (!isTypeParameter && (context.enclosingDeclaration || context.flags & NodeBuilderFlags.UseFullyQualifiedType)) {
chain = getSymbolChain(symbol, meaning, /*endOfChain*/ true);
Debug.assert(chain && chain.length > 0);
}
else {
chain = [symbol];
}
if (expectsIdentifier && chain.length !== 1
&& !context.encounteredError
&& !(context.flags & NodeBuilderFlags.AllowQualifedNameInPlaceOfIdentifier)) {
context.encounteredError = true;
}
return createEntityNameFromSymbolChain(chain, chain.length - 1);
function createEntityNameFromSymbolChain(chain: Symbol[], index: number): EntityName {
Debug.assert(chain && 0 <= index && index < chain.length);
const symbol = chain[index];
let typeParameterNodes: TypeNode[] | undefined;
if (context.flags & NodeBuilderFlags.WriteTypeParametersInQualifiedName && index > 0) {
const parentSymbol = chain[index - 1];
let typeParameters: TypeParameter[];
if (getCheckFlags(symbol) & CheckFlags.Instantiated) {
typeParameters = getTypeParametersOfClassOrInterface(parentSymbol);
}
else {
const targetSymbol = getTargetSymbol(parentSymbol);
if (targetSymbol.flags & (SymbolFlags.Class | SymbolFlags.Interface | SymbolFlags.TypeAlias)) {
typeParameters = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol);
}
}
typeParameterNodes = mapToTypeNodes(typeParameters, context);
}
const symbolName = getNameOfSymbol(symbol, context);
const identifier = setEmitFlags(createIdentifier(symbolName, typeParameterNodes), EmitFlags.NoAsciiEscaping);
return index > 0 ? createQualifiedName(createEntityNameFromSymbolChain(chain, index - 1), identifier) : identifier;
}
/** @param endOfChain Set to false for recursive calls; non-recursive calls should always output something. */
function getSymbolChain(symbol: Symbol, meaning: SymbolFlags, endOfChain: boolean): Symbol[] | undefined {
let accessibleSymbolChain = getAccessibleSymbolChain(symbol, context.enclosingDeclaration, meaning, /*useOnlyExternalAliasing*/ false);
let parentSymbol: Symbol;
if (!accessibleSymbolChain ||
needsQualification(accessibleSymbolChain[0], context.enclosingDeclaration, accessibleSymbolChain.length === 1 ? meaning : getQualifiedLeftMeaning(meaning))) {
// Go up and add our parent.
const parent = getParentOfSymbol(accessibleSymbolChain ? accessibleSymbolChain[0] : symbol);
if (parent) {
const parentChain = getSymbolChain(parent, getQualifiedLeftMeaning(meaning), /*endOfChain*/ false);
if (parentChain) {
parentSymbol = parent;
accessibleSymbolChain = parentChain.concat(accessibleSymbolChain || [symbol]);
}
}
}
if (accessibleSymbolChain) {
return accessibleSymbolChain;
}
if (
// If this is the last part of outputting the symbol, always output. The cases apply only to parent symbols.
endOfChain ||
// If a parent symbol is an external module, don't write it. (We prefer just `x` vs `"foo/bar".x`.)
!(!parentSymbol && ts.forEach(symbol.declarations, hasExternalModuleSymbol)) &&
// If a parent symbol is an anonymous type, don't write it.
!(symbol.flags & (SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral))) {
return [symbol];
}
}
}
function getNameOfSymbol(symbol: Symbol, context: NodeBuilderContext): string {
const declaration = firstOrUndefined(symbol.declarations);
if (declaration) {
const name = getNameOfDeclaration(declaration);
if (name) {
return declarationNameToString(name);
}
if (declaration.parent && declaration.parent.kind === SyntaxKind.VariableDeclaration) {
return declarationNameToString((<VariableDeclaration>declaration.parent).name);
}
if (!context.encounteredError && !(context.flags & NodeBuilderFlags.AllowAnonymousIdentifier)) {
context.encounteredError = true;
}
switch (declaration.kind) {
case SyntaxKind.ClassExpression:
return "(Anonymous class)";
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
return "(Anonymous function)";
}
}
return symbol.name;
}
}
function typePredicateToString(typePredicate: TypePredicate, enclosingDeclaration?: Declaration, flags?: TypeFormatFlags): string {
const writer = getSingleLineStringWriter();
getSymbolDisplayBuilder().buildTypePredicateDisplay(typePredicate, writer, enclosingDeclaration, flags);
const result = writer.string();
releaseStringWriter(writer);
return result;
}
function formatUnionTypes(types: Type[]): Type[] {
const result: Type[] = [];
let flags: TypeFlags = 0;
for (let i = 0; i < types.length; i++) {
const t = types[i];
flags |= t.flags;
if (!(t.flags & TypeFlags.Nullable)) {
if (t.flags & (TypeFlags.BooleanLiteral | TypeFlags.EnumLiteral)) {
const baseType = t.flags & TypeFlags.BooleanLiteral ? booleanType : getBaseTypeOfEnumLiteralType(<LiteralType>t);
if (baseType.flags & TypeFlags.Union) {
const count = (<UnionType>baseType).types.length;
if (i + count <= types.length && types[i + count - 1] === (<UnionType>baseType).types[count - 1]) {
result.push(baseType);
i += count - 1;
continue;
}
}
}
result.push(t);
}
}
if (flags & TypeFlags.Null) result.push(nullType);
if (flags & TypeFlags.Undefined) result.push(undefinedType);
return result || types;
}
function visibilityToString(flags: ModifierFlags): string | undefined {
if (flags === ModifierFlags.Private) {
return "private";
}
if (flags === ModifierFlags.Protected) {
return "protected";
}
return "public";
}
function getTypeAliasForTypeLiteral(type: Type): Symbol {
if (type.symbol && type.symbol.flags & SymbolFlags.TypeLiteral) {
const node = findAncestor(type.symbol.declarations[0].parent, n => n.kind !== SyntaxKind.ParenthesizedType);
if (node.kind === SyntaxKind.TypeAliasDeclaration) {
return getSymbolOfNode(node);
}
}
return undefined;
}
function isTopLevelInExternalModuleAugmentation(node: Node): boolean {
return node && node.parent &&
node.parent.kind === SyntaxKind.ModuleBlock &&
isExternalModuleAugmentation(node.parent.parent);
}
function literalTypeToString(type: LiteralType) {
return type.flags & TypeFlags.StringLiteral ? `"${escapeString((<StringLiteralType>type).value)}"` : "" + (<NumberLiteralType>type).value;
}
function getNameOfSymbol(symbol: Symbol): string {
if (symbol.declarations && symbol.declarations.length) {
const declaration = symbol.declarations[0];
const name = getNameOfDeclaration(declaration);
if (name) {
return declarationNameToString(name);
}
if (declaration.parent && declaration.parent.kind === SyntaxKind.VariableDeclaration) {
return declarationNameToString((<VariableDeclaration>declaration.parent).name);
}
switch (declaration.kind) {
case SyntaxKind.ClassExpression:
return "(Anonymous class)";
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
return "(Anonymous function)";
}
}
return symbol.name;
}
function getSymbolDisplayBuilder(): SymbolDisplayBuilder {
/**
* Writes only the name of the symbol out to the writer. Uses the original source text
* for the name of the symbol if it is available to match how the user wrote the name.
*/
function appendSymbolNameOnly(symbol: Symbol, writer: SymbolWriter): void {
writer.writeSymbol(getNameOfSymbol(symbol), symbol);
}
/**
* Writes a property access or element access with the name of the symbol out to the writer.
* Uses the original source text for the name of the symbol if it is available to match how the user wrote the name,
* ensuring that any names written with literals use element accesses.
*/
function appendPropertyOrElementAccessForSymbol(symbol: Symbol, writer: SymbolWriter): void {
const symbolName = getNameOfSymbol(symbol);
const firstChar = symbolName.charCodeAt(0);
const needsElementAccess = !isIdentifierStart(firstChar, languageVersion);
if (needsElementAccess) {
writePunctuation(writer, SyntaxKind.OpenBracketToken);
if (isSingleOrDoubleQuote(firstChar)) {
writer.writeStringLiteral(symbolName);
}
else {
writer.writeSymbol(symbolName, symbol);
}
writePunctuation(writer, SyntaxKind.CloseBracketToken);
}
else {
writePunctuation(writer, SyntaxKind.DotToken);
writer.writeSymbol(symbolName, symbol);
}
}
/**
* Enclosing declaration is optional when we don't want to get qualified name in the enclosing declaration scope
* Meaning needs to be specified if the enclosing declaration is given
*/
function buildSymbolDisplay(symbol: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, meaning?: SymbolFlags, flags?: SymbolFormatFlags, typeFlags?: TypeFormatFlags): void {
let parentSymbol: Symbol;
function appendParentTypeArgumentsAndSymbolName(symbol: Symbol): void {
if (parentSymbol) {
// Write type arguments of instantiated class/interface here
if (flags & SymbolFormatFlags.WriteTypeParametersOrArguments) {
if (getCheckFlags(symbol) & CheckFlags.Instantiated) {
const params = getTypeParametersOfClassOrInterface(parentSymbol.flags & SymbolFlags.Alias ? resolveAlias(parentSymbol) : parentSymbol);
buildDisplayForTypeArgumentsAndDelimiters(params, (<TransientSymbol>symbol).mapper, writer, enclosingDeclaration);
}
else {
buildTypeParameterDisplayFromSymbol(parentSymbol, writer, enclosingDeclaration);
}
}
appendPropertyOrElementAccessForSymbol(symbol, writer);
}
else {
appendSymbolNameOnly(symbol, writer);
}
parentSymbol = symbol;
}
// Let the writer know we just wrote out a symbol. The declaration emitter writer uses
// this to determine if an import it has previously seen (and not written out) needs
// to be written to the file once the walk of the tree is complete.
//
// NOTE(cyrusn): This approach feels somewhat unfortunate. A simple pass over the tree
// up front (for example, during checking) could determine if we need to emit the imports
// and we could then access that data during declaration emit.
writer.trackSymbol(symbol, enclosingDeclaration, meaning);
/** @param endOfChain Set to false for recursive calls; non-recursive calls should always output something. */
function walkSymbol(symbol: Symbol, meaning: SymbolFlags, endOfChain: boolean): void {
const accessibleSymbolChain = getAccessibleSymbolChain(symbol, enclosingDeclaration, meaning, !!(flags & SymbolFormatFlags.UseOnlyExternalAliasing));
if (!accessibleSymbolChain ||
needsQualification(accessibleSymbolChain[0], enclosingDeclaration, accessibleSymbolChain.length === 1 ? meaning : getQualifiedLeftMeaning(meaning))) {
// Go up and add our parent.
const parent = getParentOfSymbol(accessibleSymbolChain ? accessibleSymbolChain[0] : symbol);
if (parent) {
walkSymbol(parent, getQualifiedLeftMeaning(meaning), /*endOfChain*/ false);
}
}
if (accessibleSymbolChain) {
for (const accessibleSymbol of accessibleSymbolChain) {
appendParentTypeArgumentsAndSymbolName(accessibleSymbol);
}
}
else if (
// If this is the last part of outputting the symbol, always output. The cases apply only to parent symbols.
endOfChain ||
// If a parent symbol is an external module, don't write it. (We prefer just `x` vs `"foo/bar".x`.)
!(!parentSymbol && ts.forEach(symbol.declarations, hasExternalModuleSymbol)) &&
// If a parent symbol is an anonymous type, don't write it.
!(symbol.flags & (SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral))) {
appendParentTypeArgumentsAndSymbolName(symbol);
}
}
// Get qualified name if the symbol is not a type parameter
// and there is an enclosing declaration or we specifically
// asked for it
const isTypeParameter = symbol.flags & SymbolFlags.TypeParameter;
const typeFormatFlag = TypeFormatFlags.UseFullyQualifiedType & typeFlags;
if (!isTypeParameter && (enclosingDeclaration || typeFormatFlag)) {
walkSymbol(symbol, meaning, /*endOfChain*/ true);
}
else {
appendParentTypeArgumentsAndSymbolName(symbol);
}
}
function buildTypeDisplay(type: Type, writer: SymbolWriter, enclosingDeclaration?: Node, globalFlags?: TypeFormatFlags, symbolStack?: Symbol[]) {
const globalFlagsToPass = globalFlags & (TypeFormatFlags.WriteOwnNameForAnyLike | TypeFormatFlags.WriteClassExpressionAsTypeLiteral);
let inObjectTypeLiteral = false;
return writeType(type, globalFlags);
function writeType(type: Type, flags: TypeFormatFlags) {
const nextFlags = flags & ~TypeFormatFlags.InTypeAlias;
// Write undefined/null type as any
if (type.flags & TypeFlags.Intrinsic) {
// Special handling for unknown / resolving types, they should show up as any and not unknown or __resolving
writer.writeKeyword(!(globalFlags & TypeFormatFlags.WriteOwnNameForAnyLike) && isTypeAny(type)
? "any"
: (<IntrinsicType>type).intrinsicName);
}
else if (type.flags & TypeFlags.TypeParameter && (type as TypeParameter).isThisType) {
if (inObjectTypeLiteral) {
writer.reportInaccessibleThisError();
}
writer.writeKeyword("this");
}
else if (getObjectFlags(type) & ObjectFlags.Reference) {
writeTypeReference(<TypeReference>type, nextFlags);
}
else if (type.flags & TypeFlags.EnumLiteral && !(type.flags & TypeFlags.Union)) {
const parent = getParentOfSymbol(type.symbol);
buildSymbolDisplay(parent, writer, enclosingDeclaration, SymbolFlags.Type, SymbolFormatFlags.None, nextFlags);
// In a literal enum type with a single member E { A }, E and E.A denote the
// same type. We always display this type simply as E.
if (getDeclaredTypeOfSymbol(parent) !== type) {
writePunctuation(writer, SyntaxKind.DotToken);
appendSymbolNameOnly(type.symbol, writer);
}
}
else if (getObjectFlags(type) & ObjectFlags.ClassOrInterface || type.flags & (TypeFlags.EnumLike | TypeFlags.TypeParameter)) {
// The specified symbol flags need to be reinterpreted as type flags
buildSymbolDisplay(type.symbol, writer, enclosingDeclaration, SymbolFlags.Type, SymbolFormatFlags.None, nextFlags);
}
else if (!(flags & TypeFormatFlags.InTypeAlias) && type.aliasSymbol &&
isSymbolAccessible(type.aliasSymbol, enclosingDeclaration, SymbolFlags.Type, /*shouldComputeAliasesToMakeVisible*/ false).accessibility === SymbolAccessibility.Accessible) {
const typeArguments = type.aliasTypeArguments;
writeSymbolTypeReference(type.aliasSymbol, typeArguments, 0, length(typeArguments), nextFlags);
}
else if (type.flags & TypeFlags.UnionOrIntersection) {
writeUnionOrIntersectionType(<UnionOrIntersectionType>type, nextFlags);
}
else if (getObjectFlags(type) & (ObjectFlags.Anonymous | ObjectFlags.Mapped)) {
writeAnonymousType(<ObjectType>type, nextFlags);
}
else if (type.flags & TypeFlags.StringOrNumberLiteral) {
writer.writeStringLiteral(literalTypeToString(<LiteralType>type));
}
else if (type.flags & TypeFlags.Index) {
writer.writeKeyword("keyof");
writeSpace(writer);
writeType((<IndexType>type).type, TypeFormatFlags.InElementType);
}
else if (type.flags & TypeFlags.IndexedAccess) {
writeType((<IndexedAccessType>type).objectType, TypeFormatFlags.InElementType);
writePunctuation(writer, SyntaxKind.OpenBracketToken);
writeType((<IndexedAccessType>type).indexType, TypeFormatFlags.None);
writePunctuation(writer, SyntaxKind.CloseBracketToken);
}
else {
// Should never get here
// { ... }
writePunctuation(writer, SyntaxKind.OpenBraceToken);
writeSpace(writer);
writePunctuation(writer, SyntaxKind.DotDotDotToken);
writeSpace(writer);
writePunctuation(writer, SyntaxKind.CloseBraceToken);
}
}
function writeTypeList(types: Type[], delimiter: SyntaxKind) {
for (let i = 0; i < types.length; i++) {
if (i > 0) {
if (delimiter !== SyntaxKind.CommaToken) {
writeSpace(writer);
}
writePunctuation(writer, delimiter);
writeSpace(writer);
}
writeType(types[i], delimiter === SyntaxKind.CommaToken ? TypeFormatFlags.None : TypeFormatFlags.InElementType);
}
}
function writeSymbolTypeReference(symbol: Symbol, typeArguments: Type[], pos: number, end: number, flags: TypeFormatFlags) {
// Unnamed function expressions and arrow functions have reserved names that we don't want to display
if (symbol.flags & SymbolFlags.Class || !isReservedMemberName(symbol.name)) {
buildSymbolDisplay(symbol, writer, enclosingDeclaration, SymbolFlags.Type, SymbolFormatFlags.None, flags);
}
if (pos < end) {
writePunctuation(writer, SyntaxKind.LessThanToken);
writeType(typeArguments[pos], TypeFormatFlags.InFirstTypeArgument);
pos++;
while (pos < end) {
writePunctuation(writer, SyntaxKind.CommaToken);
writeSpace(writer);
writeType(typeArguments[pos], TypeFormatFlags.None);
pos++;
}
writePunctuation(writer, SyntaxKind.GreaterThanToken);
}
}
function writeTypeReference(type: TypeReference, flags: TypeFormatFlags) {
const typeArguments = type.typeArguments || emptyArray;
if (type.target === globalArrayType && !(flags & TypeFormatFlags.WriteArrayAsGenericType)) {
writeType(typeArguments[0], TypeFormatFlags.InElementType);
writePunctuation(writer, SyntaxKind.OpenBracketToken);
writePunctuation(writer, SyntaxKind.CloseBracketToken);
}
else if (type.target.objectFlags & ObjectFlags.Tuple) {
writePunctuation(writer, SyntaxKind.OpenBracketToken);
writeTypeList(type.typeArguments.slice(0, getTypeReferenceArity(type)), SyntaxKind.CommaToken);
writePunctuation(writer, SyntaxKind.CloseBracketToken);
}
else if (flags & TypeFormatFlags.WriteClassExpressionAsTypeLiteral &&
type.symbol.valueDeclaration &&
type.symbol.valueDeclaration.kind === SyntaxKind.ClassExpression) {
writeAnonymousType(getDeclaredTypeOfClassOrInterface(type.symbol), flags);
}
else {
// Write the type reference in the format f<A>.g<B>.C<X, Y> where A and B are type arguments
// for outer type parameters, and f and g are the respective declaring containers of those
// type parameters.
const outerTypeParameters = type.target.outerTypeParameters;
let i = 0;
if (outerTypeParameters) {
const length = outerTypeParameters.length;
while (i < length) {
// Find group of type arguments for type parameters with the same declaring container.
const start = i;
const parent = getParentSymbolOfTypeParameter(outerTypeParameters[i]);
do {
i++;
} while (i < length && getParentSymbolOfTypeParameter(outerTypeParameters[i]) === parent);
// When type parameters are their own type arguments for the whole group (i.e. we have
// the default outer type arguments), we don't show the group.
if (!rangeEquals(outerTypeParameters, typeArguments, start, i)) {
writeSymbolTypeReference(parent, typeArguments, start, i, flags);
writePunctuation(writer, SyntaxKind.DotToken);
}
}
}
const typeParameterCount = (type.target.typeParameters || emptyArray).length;
writeSymbolTypeReference(type.symbol, typeArguments, i, typeParameterCount, flags);
}
}
function writeUnionOrIntersectionType(type: UnionOrIntersectionType, flags: TypeFormatFlags) {
if (flags & TypeFormatFlags.InElementType) {
writePunctuation(writer, SyntaxKind.OpenParenToken);
}
if (type.flags & TypeFlags.Union) {
writeTypeList(formatUnionTypes(type.types), SyntaxKind.BarToken);
}
else {
writeTypeList(type.types, SyntaxKind.AmpersandToken);
}
if (flags & TypeFormatFlags.InElementType) {
writePunctuation(writer, SyntaxKind.CloseParenToken);
}
}
function writeAnonymousType(type: ObjectType, flags: TypeFormatFlags) {
const symbol = type.symbol;
if (symbol) {
// Always use 'typeof T' for type of class, enum, and module objects
if (symbol.flags & SymbolFlags.Class &&
!getBaseTypeVariableOfClass(symbol) &&
!(symbol.valueDeclaration.kind === SyntaxKind.ClassExpression && flags & TypeFormatFlags.WriteClassExpressionAsTypeLiteral) ||
symbol.flags & (SymbolFlags.Enum | SymbolFlags.ValueModule)) {
writeTypeOfSymbol(type, flags);
}
else if (shouldWriteTypeOfFunctionSymbol()) {
writeTypeOfSymbol(type, flags);
}
else if (contains(symbolStack, symbol)) {
// If type is an anonymous type literal in a type alias declaration, use type alias name
const typeAlias = getTypeAliasForTypeLiteral(type);
if (typeAlias) {
// The specified symbol flags need to be reinterpreted as type flags
buildSymbolDisplay(typeAlias, writer, enclosingDeclaration, SymbolFlags.Type, SymbolFormatFlags.None, flags);
}
else {
// Recursive usage, use any
writeKeyword(writer, SyntaxKind.AnyKeyword);
}
}
else {
// Since instantiations of the same anonymous type have the same symbol, tracking symbols instead
// of types allows us to catch circular references to instantiations of the same anonymous type
// However, in case of class expressions, we want to write both the static side and the instance side.
// We skip adding the static side so that the instance side has a chance to be written
// before checking for circular references.
if (!symbolStack) {
symbolStack = [];
}
const isConstructorObject = type.flags & TypeFlags.Object &&
getObjectFlags(type) & ObjectFlags.Anonymous &&
type.symbol && type.symbol.flags & SymbolFlags.Class;
if (isConstructorObject) {
writeLiteralType(type, flags);
}
else {
symbolStack.push(symbol);
writeLiteralType(type, flags);
symbolStack.pop();
}
}
}
else {
// Anonymous types with no symbol are never circular
writeLiteralType(type, flags);
}
function shouldWriteTypeOfFunctionSymbol() {
const isStaticMethodSymbol = !!(symbol.flags & SymbolFlags.Method && // typeof static method
forEach(symbol.declarations, declaration => getModifierFlags(declaration) & ModifierFlags.Static));
const isNonLocalFunctionSymbol = !!(symbol.flags & SymbolFlags.Function) &&
(symbol.parent || // is exported function symbol
forEach(symbol.declarations, declaration =>
declaration.parent.kind === SyntaxKind.SourceFile || declaration.parent.kind === SyntaxKind.ModuleBlock));
if (isStaticMethodSymbol || isNonLocalFunctionSymbol) {
// typeof is allowed only for static/non local functions
return !!(flags & TypeFormatFlags.UseTypeOfFunction) || // use typeof if format flags specify it
(contains(symbolStack, symbol)); // it is type of the symbol uses itself recursively
}
}
}
function writeTypeOfSymbol(type: ObjectType, typeFormatFlags?: TypeFormatFlags) {
writeKeyword(writer, SyntaxKind.TypeOfKeyword);
writeSpace(writer);
buildSymbolDisplay(type.symbol, writer, enclosingDeclaration, SymbolFlags.Value, SymbolFormatFlags.None, typeFormatFlags);
}
function writePropertyWithModifiers(prop: Symbol) {
if (isReadonlySymbol(prop)) {
writeKeyword(writer, SyntaxKind.ReadonlyKeyword);
writeSpace(writer);
}
buildSymbolDisplay(prop, writer);
if (prop.flags & SymbolFlags.Optional) {
writePunctuation(writer, SyntaxKind.QuestionToken);
}
}
function shouldAddParenthesisAroundFunctionType(callSignature: Signature, flags: TypeFormatFlags) {
if (flags & TypeFormatFlags.InElementType) {
return true;
}
else if (flags & TypeFormatFlags.InFirstTypeArgument) {
// Add parenthesis around function type for the first type argument to avoid ambiguity
const typeParameters = callSignature.target && (flags & TypeFormatFlags.WriteTypeArgumentsOfSignature) ?
callSignature.target.typeParameters : callSignature.typeParameters;
return typeParameters && typeParameters.length !== 0;
}
return false;
}
function writeLiteralType(type: ObjectType, flags: TypeFormatFlags) {
if (type.objectFlags & ObjectFlags.Mapped) {
if (getConstraintTypeFromMappedType(<MappedType>type).flags & (TypeFlags.TypeParameter | TypeFlags.Index)) {
writeMappedType(<MappedType>type);
return;
}
}
const resolved = resolveStructuredTypeMembers(type);
if (!resolved.properties.length && !resolved.stringIndexInfo && !resolved.numberIndexInfo) {
if (!resolved.callSignatures.length && !resolved.constructSignatures.length) {
writePunctuation(writer, SyntaxKind.OpenBraceToken);
writePunctuation(writer, SyntaxKind.CloseBraceToken);
return;
}
if (resolved.callSignatures.length === 1 && !resolved.constructSignatures.length) {
const parenthesizeSignature = shouldAddParenthesisAroundFunctionType(resolved.callSignatures[0], flags);
if (parenthesizeSignature) {
writePunctuation(writer, SyntaxKind.OpenParenToken);
}
buildSignatureDisplay(resolved.callSignatures[0], writer, enclosingDeclaration, globalFlagsToPass | TypeFormatFlags.WriteArrowStyleSignature, /*kind*/ undefined, symbolStack);
if (parenthesizeSignature) {
writePunctuation(writer, SyntaxKind.CloseParenToken);
}
return;
}
if (resolved.constructSignatures.length === 1 && !resolved.callSignatures.length) {
if (flags & TypeFormatFlags.InElementType) {
writePunctuation(writer, SyntaxKind.OpenParenToken);
}
writeKeyword(writer, SyntaxKind.NewKeyword);
writeSpace(writer);
buildSignatureDisplay(resolved.constructSignatures[0], writer, enclosingDeclaration, globalFlagsToPass | TypeFormatFlags.WriteArrowStyleSignature, /*kind*/ undefined, symbolStack);
if (flags & TypeFormatFlags.InElementType) {
writePunctuation(writer, SyntaxKind.CloseParenToken);
}
return;
}
}
const saveInObjectTypeLiteral = inObjectTypeLiteral;
inObjectTypeLiteral = true;
writePunctuation(writer, SyntaxKind.OpenBraceToken);
writer.writeLine();
writer.increaseIndent();
writeObjectLiteralType(resolved);
writer.decreaseIndent();
writePunctuation(writer, SyntaxKind.CloseBraceToken);
inObjectTypeLiteral = saveInObjectTypeLiteral;
}
function writeObjectLiteralType(resolved: ResolvedType) {
for (const signature of resolved.callSignatures) {
buildSignatureDisplay(signature, writer, enclosingDeclaration, globalFlagsToPass, /*kind*/ undefined, symbolStack);
writePunctuation(writer, SyntaxKind.SemicolonToken);
writer.writeLine();
}
for (const signature of resolved.constructSignatures) {
buildSignatureDisplay(signature, writer, enclosingDeclaration, globalFlagsToPass, SignatureKind.Construct, symbolStack);
writePunctuation(writer, SyntaxKind.SemicolonToken);
writer.writeLine();
}
buildIndexSignatureDisplay(resolved.stringIndexInfo, writer, IndexKind.String, enclosingDeclaration, globalFlags, symbolStack);
buildIndexSignatureDisplay(resolved.numberIndexInfo, writer, IndexKind.Number, enclosingDeclaration, globalFlags, symbolStack);
for (const p of resolved.properties) {
if (globalFlags & TypeFormatFlags.WriteClassExpressionAsTypeLiteral) {
if (p.flags & SymbolFlags.Prototype) {
continue;
}
if (getDeclarationModifierFlagsFromSymbol(p) & (ModifierFlags.Private | ModifierFlags.Protected)) {
writer.reportPrivateInBaseOfClassExpression(p.name);
}
}
const t = getTypeOfSymbol(p);
if (p.flags & (SymbolFlags.Function | SymbolFlags.Method) && !getPropertiesOfObjectType(t).length) {
const signatures = getSignaturesOfType(t, SignatureKind.Call);
for (const signature of signatures) {
writePropertyWithModifiers(p);
buildSignatureDisplay(signature, writer, enclosingDeclaration, globalFlagsToPass, /*kind*/ undefined, symbolStack);
writePunctuation(writer, SyntaxKind.SemicolonToken);
writer.writeLine();
}
}
else {
writePropertyWithModifiers(p);
writePunctuation(writer, SyntaxKind.ColonToken);
writeSpace(writer);
writeType(t, globalFlags & TypeFormatFlags.WriteClassExpressionAsTypeLiteral);
writePunctuation(writer, SyntaxKind.SemicolonToken);
writer.writeLine();
}
}
}
function writeMappedType(type: MappedType) {
writePunctuation(writer, SyntaxKind.OpenBraceToken);
writer.writeLine();
writer.increaseIndent();
if (type.declaration.readonlyToken) {
writeKeyword(writer, SyntaxKind.ReadonlyKeyword);
writeSpace(writer);
}
writePunctuation(writer, SyntaxKind.OpenBracketToken);
appendSymbolNameOnly(getTypeParameterFromMappedType(type).symbol, writer);
writeSpace(writer);
writeKeyword(writer, SyntaxKind.InKeyword);
writeSpace(writer);
writeType(getConstraintTypeFromMappedType(type), TypeFormatFlags.None);
writePunctuation(writer, SyntaxKind.CloseBracketToken);
if (type.declaration.questionToken) {
writePunctuation(writer, SyntaxKind.QuestionToken);
}
writePunctuation(writer, SyntaxKind.ColonToken);
writeSpace(writer);
writeType(getTemplateTypeFromMappedType(type), TypeFormatFlags.None);
writePunctuation(writer, SyntaxKind.SemicolonToken);
writer.writeLine();
writer.decreaseIndent();
writePunctuation(writer, SyntaxKind.CloseBraceToken);
}
}
function buildTypeParameterDisplayFromSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags) {
const targetSymbol = getTargetSymbol(symbol);
if (targetSymbol.flags & SymbolFlags.Class || targetSymbol.flags & SymbolFlags.Interface || targetSymbol.flags & SymbolFlags.TypeAlias) {
buildDisplayForTypeParametersAndDelimiters(getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol), writer, enclosingDeclaration, flags);
}
}
function buildTypeParameterDisplay(tp: TypeParameter, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) {
appendSymbolNameOnly(tp.symbol, writer);
const constraint = getConstraintOfTypeParameter(tp);
if (constraint) {
writeSpace(writer);
writeKeyword(writer, SyntaxKind.ExtendsKeyword);
writeSpace(writer);
buildTypeDisplay(constraint, writer, enclosingDeclaration, flags, symbolStack);
}
const defaultType = getDefaultFromTypeParameter(tp);
if (defaultType) {
writeSpace(writer);
writePunctuation(writer, SyntaxKind.EqualsToken);
writeSpace(writer);
buildTypeDisplay(defaultType, writer, enclosingDeclaration, flags, symbolStack);
}
}
function buildParameterDisplay(p: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) {
const parameterNode = <ParameterDeclaration>p.valueDeclaration;
if (parameterNode ? isRestParameter(parameterNode) : isTransientSymbol(p) && p.isRestParameter) {
writePunctuation(writer, SyntaxKind.DotDotDotToken);
}
if (parameterNode && isBindingPattern(parameterNode.name)) {
buildBindingPatternDisplay(<BindingPattern>parameterNode.name, writer, enclosingDeclaration, flags, symbolStack);
}
else {
appendSymbolNameOnly(p, writer);
}
if (parameterNode && isOptionalParameter(parameterNode)) {
writePunctuation(writer, SyntaxKind.QuestionToken);
}
writePunctuation(writer, SyntaxKind.ColonToken);
writeSpace(writer);
let type = getTypeOfSymbol(p);
if (parameterNode && isRequiredInitializedParameter(parameterNode)) {
type = getNullableType(type, TypeFlags.Undefined);
}
buildTypeDisplay(type, writer, enclosingDeclaration, flags, symbolStack);
}
function buildBindingPatternDisplay(bindingPattern: BindingPattern, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) {
// We have to explicitly emit square bracket and bracket because these tokens are not stored inside the node.
if (bindingPattern.kind === SyntaxKind.ObjectBindingPattern) {
writePunctuation(writer, SyntaxKind.OpenBraceToken);
buildDisplayForCommaSeparatedList(bindingPattern.elements, writer, e => buildBindingElementDisplay(e, writer, enclosingDeclaration, flags, symbolStack));
writePunctuation(writer, SyntaxKind.CloseBraceToken);
}
else if (bindingPattern.kind === SyntaxKind.ArrayBindingPattern) {
writePunctuation(writer, SyntaxKind.OpenBracketToken);
const elements = bindingPattern.elements;
buildDisplayForCommaSeparatedList(elements, writer, e => buildBindingElementDisplay(e, writer, enclosingDeclaration, flags, symbolStack));
if (elements && elements.hasTrailingComma) {
writePunctuation(writer, SyntaxKind.CommaToken);
}
writePunctuation(writer, SyntaxKind.CloseBracketToken);
}
}
function buildBindingElementDisplay(bindingElement: ArrayBindingElement, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) {
if (isOmittedExpression(bindingElement)) {
return;
}
Debug.assert(bindingElement.kind === SyntaxKind.BindingElement);
if (bindingElement.propertyName) {
writer.writeProperty(getTextOfNode(bindingElement.propertyName));
writePunctuation(writer, SyntaxKind.ColonToken);
writeSpace(writer);
}
if (isBindingPattern(bindingElement.name)) {
buildBindingPatternDisplay(<BindingPattern>bindingElement.name, writer, enclosingDeclaration, flags, symbolStack);
}
else {
if (bindingElement.dotDotDotToken) {
writePunctuation(writer, SyntaxKind.DotDotDotToken);
}
appendSymbolNameOnly(bindingElement.symbol, writer);
}
}
function buildDisplayForTypeParametersAndDelimiters(typeParameters: TypeParameter[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) {
if (typeParameters && typeParameters.length) {
writePunctuation(writer, SyntaxKind.LessThanToken);
buildDisplayForCommaSeparatedList(typeParameters, writer, p => buildTypeParameterDisplay(p, writer, enclosingDeclaration, flags, symbolStack));
writePunctuation(writer, SyntaxKind.GreaterThanToken);
}
}
function buildDisplayForCommaSeparatedList<T>(list: T[], writer: SymbolWriter, action: (item: T) => void) {
for (let i = 0; i < list.length; i++) {
if (i > 0) {
writePunctuation(writer, SyntaxKind.CommaToken);
writeSpace(writer);
}
action(list[i]);
}
}
function buildDisplayForTypeArgumentsAndDelimiters(typeParameters: TypeParameter[], mapper: TypeMapper, writer: SymbolWriter, enclosingDeclaration?: Node) {
if (typeParameters && typeParameters.length) {
writePunctuation(writer, SyntaxKind.LessThanToken);
let flags = TypeFormatFlags.InFirstTypeArgument;
for (let i = 0; i < typeParameters.length; i++) {
if (i > 0) {
writePunctuation(writer, SyntaxKind.CommaToken);
writeSpace(writer);
flags = TypeFormatFlags.None;
}
buildTypeDisplay(mapper(typeParameters[i]), writer, enclosingDeclaration, flags);
}
writePunctuation(writer, SyntaxKind.GreaterThanToken);
}
}
function buildDisplayForParametersAndDelimiters(thisParameter: Symbol | undefined, parameters: Symbol[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) {
writePunctuation(writer, SyntaxKind.OpenParenToken);
if (thisParameter) {
buildParameterDisplay(thisParameter, writer, enclosingDeclaration, flags, symbolStack);
}
for (let i = 0; i < parameters.length; i++) {
if (i > 0 || thisParameter) {
writePunctuation(writer, SyntaxKind.CommaToken);
writeSpace(writer);
}
buildParameterDisplay(parameters[i], writer, enclosingDeclaration, flags, symbolStack);
}
writePunctuation(writer, SyntaxKind.CloseParenToken);
}
function buildTypePredicateDisplay(predicate: TypePredicate, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]): void {
if (isIdentifierTypePredicate(predicate)) {
writer.writeParameter(predicate.parameterName);
}
else {
writeKeyword(writer, SyntaxKind.ThisKeyword);
}
writeSpace(writer);
writeKeyword(writer, SyntaxKind.IsKeyword);
writeSpace(writer);
buildTypeDisplay(predicate.type, writer, enclosingDeclaration, flags, symbolStack);
}
function buildReturnTypeDisplay(signature: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) {
const returnType = getReturnTypeOfSignature(signature);
if (flags & TypeFormatFlags.SuppressAnyReturnType && isTypeAny(returnType)) {
return;
}
if (flags & TypeFormatFlags.WriteArrowStyleSignature) {
writeSpace(writer);
writePunctuation(writer, SyntaxKind.EqualsGreaterThanToken);
}
else {
writePunctuation(writer, SyntaxKind.ColonToken);
}
writeSpace(writer);
if (signature.typePredicate) {
buildTypePredicateDisplay(signature.typePredicate, writer, enclosingDeclaration, flags, symbolStack);
}
else {
buildTypeDisplay(returnType, writer, enclosingDeclaration, flags, symbolStack);
}
}
function buildSignatureDisplay(signature: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, kind?: SignatureKind, symbolStack?: Symbol[]) {
if (kind === SignatureKind.Construct) {
writeKeyword(writer, SyntaxKind.NewKeyword);
writeSpace(writer);
}
if (signature.target && (flags & TypeFormatFlags.WriteTypeArgumentsOfSignature)) {
// Instantiated signature, write type arguments instead
// This is achieved by passing in the mapper separately
buildDisplayForTypeArgumentsAndDelimiters(signature.target.typeParameters, signature.mapper, writer, enclosingDeclaration);
}
else {
buildDisplayForTypeParametersAndDelimiters(signature.typeParameters, writer, enclosingDeclaration, flags, symbolStack);
}
buildDisplayForParametersAndDelimiters(signature.thisParameter, signature.parameters, writer, enclosingDeclaration, flags, symbolStack);
buildReturnTypeDisplay(signature, writer, enclosingDeclaration, flags, symbolStack);
}
function buildIndexSignatureDisplay(info: IndexInfo, writer: SymbolWriter, kind: IndexKind, enclosingDeclaration?: Node, globalFlags?: TypeFormatFlags, symbolStack?: Symbol[]) {
if (info) {
if (info.isReadonly) {
writeKeyword(writer, SyntaxKind.ReadonlyKeyword);
writeSpace(writer);
}
writePunctuation(writer, SyntaxKind.OpenBracketToken);
writer.writeParameter(info.declaration ? declarationNameToString(info.declaration.parameters[0].name) : "x");
writePunctuation(writer, SyntaxKind.ColonToken);
writeSpace(writer);
switch (kind) {
case IndexKind.Number:
writeKeyword(writer, SyntaxKind.NumberKeyword);
break;
case IndexKind.String:
writeKeyword(writer, SyntaxKind.StringKeyword);
break;
}
writePunctuation(writer, SyntaxKind.CloseBracketToken);
writePunctuation(writer, SyntaxKind.ColonToken);
writeSpace(writer);
buildTypeDisplay(info.type, writer, enclosingDeclaration, globalFlags, symbolStack);
writePunctuation(writer, SyntaxKind.SemicolonToken);
writer.writeLine();
}
}
return _displayBuilder || (_displayBuilder = {
buildSymbolDisplay,
buildTypeDisplay,
buildTypeParameterDisplay,
buildTypePredicateDisplay,
buildParameterDisplay,
buildDisplayForParametersAndDelimiters,
buildDisplayForTypeParametersAndDelimiters,
buildTypeParameterDisplayFromSymbol,
buildSignatureDisplay,
buildIndexSignatureDisplay,
buildReturnTypeDisplay
});
}
function isDeclarationVisible(node: Declaration): boolean {
if (node) {
const links = getNodeLinks(node);
if (links.isVisible === undefined) {
links.isVisible = !!determineIfDeclarationIsVisible();
}
return links.isVisible;
}
return false;
function determineIfDeclarationIsVisible() {
switch (node.kind) {
case SyntaxKind.BindingElement:
return isDeclarationVisible(<Declaration>node.parent.parent);
case SyntaxKind.VariableDeclaration:
if (isBindingPattern((node as VariableDeclaration).name) &&
!((node as VariableDeclaration).name as BindingPattern).elements.length) {
// If the binding pattern is empty, this variable declaration is not visible
return false;
}
// falls through
case SyntaxKind.ModuleDeclaration:
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.TypeAliasDeclaration:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.EnumDeclaration:
case SyntaxKind.ImportEqualsDeclaration:
// external module augmentation is always visible
if (isExternalModuleAugmentation(node)) {
return true;
}
const parent = getDeclarationContainer(node);
// If the node is not exported or it is not ambient module element (except import declaration)
if (!(getCombinedModifierFlags(node) & ModifierFlags.Export) &&
!(node.kind !== SyntaxKind.ImportEqualsDeclaration && parent.kind !== SyntaxKind.SourceFile && isInAmbientContext(parent))) {
return isGlobalSourceFile(parent);
}
// Exported members/ambient module elements (exception import declaration) are visible if parent is visible
return isDeclarationVisible(<Declaration>parent);
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.PropertySignature:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
if (getModifierFlags(node) & (ModifierFlags.Private | ModifierFlags.Protected)) {
// Private/protected properties/methods are not visible
return false;
}
// Public properties/methods are visible if its parents are visible, so:
// falls through
case SyntaxKind.Constructor:
case SyntaxKind.ConstructSignature:
case SyntaxKind.CallSignature:
case SyntaxKind.IndexSignature:
case SyntaxKind.Parameter:
case SyntaxKind.ModuleBlock:
case SyntaxKind.FunctionType:
case SyntaxKind.ConstructorType:
case SyntaxKind.TypeLiteral:
case SyntaxKind.TypeReference:
case SyntaxKind.ArrayType:
case SyntaxKind.TupleType:
case SyntaxKind.UnionType:
case SyntaxKind.IntersectionType:
case SyntaxKind.ParenthesizedType:
return isDeclarationVisible(<Declaration>node.parent);
// Default binding, import specifier and namespace import is visible
// only on demand so by default it is not visible
case SyntaxKind.ImportClause:
case SyntaxKind.NamespaceImport:
case SyntaxKind.ImportSpecifier:
return false;
// Type parameters are always visible
case SyntaxKind.TypeParameter:
// Source file and namespace export are always visible
case SyntaxKind.SourceFile:
case SyntaxKind.NamespaceExportDeclaration:
return true;
// Export assignments do not create name bindings outside the module
case SyntaxKind.ExportAssignment:
return false;
default:
return false;
}
}
}
function collectLinkedAliases(node: Identifier): Node[] {
let exportSymbol: Symbol;
if (node.parent && node.parent.kind === SyntaxKind.ExportAssignment) {
exportSymbol = resolveName(node.parent, node.text, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias, Diagnostics.Cannot_find_name_0, node);
}
else if (node.parent.kind === SyntaxKind.ExportSpecifier) {
exportSymbol = getTargetOfExportSpecifier(<ExportSpecifier>node.parent, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias);
}
const result: Node[] = [];
if (exportSymbol) {
buildVisibleNodeList(exportSymbol.declarations);
}
return result;
function buildVisibleNodeList(declarations: Declaration[]) {
forEach(declarations, declaration => {
getNodeLinks(declaration).isVisible = true;
const resultNode = getAnyImportSyntax(declaration) || declaration;
if (!contains(result, resultNode)) {
result.push(resultNode);
}
if (isInternalModuleImportEqualsDeclaration(declaration)) {
// Add the referenced top container visible
const internalModuleReference = <Identifier | QualifiedName>(<ImportEqualsDeclaration>declaration).moduleReference;
const firstIdentifier = getFirstIdentifier(internalModuleReference);
const importSymbol = resolveName(declaration, firstIdentifier.text, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace,
undefined, undefined);
if (importSymbol) {
buildVisibleNodeList(importSymbol.declarations);
}
}
});
}
}
/**
* Push an entry on the type resolution stack. If an entry with the given target and the given property name
* is already on the stack, and no entries in between already have a type, then a circularity has occurred.
* In this case, the result values of the existing entry and all entries pushed after it are changed to false,
* and the value false is returned. Otherwise, the new entry is just pushed onto the stack, and true is returned.
* In order to see if the same query has already been done before, the target object and the propertyName both
* must match the one passed in.
*
* @param target The symbol, type, or signature whose type is being queried
* @param propertyName The property name that should be used to query the target for its type
*/
function pushTypeResolution(target: TypeSystemEntity, propertyName: TypeSystemPropertyName): boolean {
const resolutionCycleStartIndex = findResolutionCycleStartIndex(target, propertyName);
if (resolutionCycleStartIndex >= 0) {
// A cycle was found
const { length } = resolutionTargets;
for (let i = resolutionCycleStartIndex; i < length; i++) {
resolutionResults[i] = false;
}
return false;
}
resolutionTargets.push(target);
resolutionResults.push(/*items*/ true);
resolutionPropertyNames.push(propertyName);
return true;
}
function findResolutionCycleStartIndex(target: TypeSystemEntity, propertyName: TypeSystemPropertyName): number {
for (let i = resolutionTargets.length - 1; i >= 0; i--) {
if (hasType(resolutionTargets[i], resolutionPropertyNames[i])) {
return -1;
}
if (resolutionTargets[i] === target && resolutionPropertyNames[i] === propertyName) {
return i;
}
}
return -1;
}
function hasType(target: TypeSystemEntity, propertyName: TypeSystemPropertyName): Type {
if (propertyName === TypeSystemPropertyName.Type) {
return getSymbolLinks(<Symbol>target).type;
}
if (propertyName === TypeSystemPropertyName.DeclaredType) {
return getSymbolLinks(<Symbol>target).declaredType;
}
if (propertyName === TypeSystemPropertyName.ResolvedBaseConstructorType) {
return (<InterfaceType>target).resolvedBaseConstructorType;
}
if (propertyName === TypeSystemPropertyName.ResolvedReturnType) {
return (<Signature>target).resolvedReturnType;
}
Debug.fail("Unhandled TypeSystemPropertyName " + propertyName);
}
// Pop an entry from the type resolution stack and return its associated result value. The result value will
// be true if no circularities were detected, or false if a circularity was found.
function popTypeResolution(): boolean {
resolutionTargets.pop();
resolutionPropertyNames.pop();
return resolutionResults.pop();
}
function getDeclarationContainer(node: Node): Node {
node = findAncestor(getRootDeclaration(node), node => {
switch (node.kind) {
case SyntaxKind.VariableDeclaration:
case SyntaxKind.VariableDeclarationList:
case SyntaxKind.ImportSpecifier:
case SyntaxKind.NamedImports:
case SyntaxKind.NamespaceImport:
case SyntaxKind.ImportClause:
return false;
default:
return true;
}
});
return node && node.parent;
}
function getTypeOfPrototypeProperty(prototype: Symbol): Type {
// TypeScript 1.0 spec (April 2014): 8.4
// Every class automatically contains a static property member named 'prototype',
// the type of which is an instantiation of the class type with type Any supplied as a type argument for each type parameter.
// It is an error to explicitly declare a static property member with the name 'prototype'.
const classType = <InterfaceType>getDeclaredTypeOfSymbol(getParentOfSymbol(prototype));
return classType.typeParameters ? createTypeReference(<GenericType>classType, map(classType.typeParameters, _ => anyType)) : classType;
}
// Return the type of the given property in the given type, or undefined if no such property exists
function getTypeOfPropertyOfType(type: Type, name: string): Type {
const prop = getPropertyOfType(type, name);
return prop ? getTypeOfSymbol(prop) : undefined;
}
function isTypeAny(type: Type) {
return type && (type.flags & TypeFlags.Any) !== 0;
}
// Return the type of a binding element parent. We check SymbolLinks first to see if a type has been
// assigned by contextual typing.
function getTypeForBindingElementParent(node: VariableLikeDeclaration) {
const symbol = getSymbolOfNode(node);
return symbol && getSymbolLinks(symbol).type || getTypeForVariableLikeDeclaration(node, /*includeOptionality*/ false);
}
function isComputedNonLiteralName(name: PropertyName): boolean {
return name.kind === SyntaxKind.ComputedPropertyName && !isStringOrNumericLiteral((<ComputedPropertyName>name).expression);
}
function getRestType(source: Type, properties: PropertyName[], symbol: Symbol): Type {
source = filterType(source, t => !(t.flags & TypeFlags.Nullable));
if (source.flags & TypeFlags.Never) {
return emptyObjectType;
}
if (source.flags & TypeFlags.Union) {
return mapType(source, t => getRestType(t, properties, symbol));
}
const members = createMap<Symbol>();
const names = createMap<true>();
for (const name of properties) {
names.set(getTextOfPropertyName(name), true);
}
for (const prop of getPropertiesOfType(source)) {
const inNamesToRemove = names.has(prop.name);
const isPrivate = getDeclarationModifierFlagsFromSymbol(prop) & (ModifierFlags.Private | ModifierFlags.Protected);
const isSetOnlyAccessor = prop.flags & SymbolFlags.SetAccessor && !(prop.flags & SymbolFlags.GetAccessor);
if (!inNamesToRemove && !isPrivate && !isClassMethod(prop) && !isSetOnlyAccessor) {
members.set(prop.name, prop);
}
}
const stringIndexInfo = getIndexInfoOfType(source, IndexKind.String);
const numberIndexInfo = getIndexInfoOfType(source, IndexKind.Number);
return createAnonymousType(symbol, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo);
}
/** Return the inferred type for a binding element */
function getTypeForBindingElement(declaration: BindingElement): Type {
const pattern = <BindingPattern>declaration.parent;
const parentType = getTypeForBindingElementParent(<VariableLikeDeclaration>pattern.parent);
// If parent has the unknown (error) type, then so does this binding element
if (parentType === unknownType) {
return unknownType;
}
// If no type was specified or inferred for parent, or if the specified or inferred type is any,
// infer from the initializer of the binding element if one is present. Otherwise, go with the
// undefined or any type of the parent.
if (!parentType || isTypeAny(parentType)) {
if (declaration.initializer) {
return checkDeclarationInitializer(declaration);
}
return parentType;
}
let type: Type;
if (pattern.kind === SyntaxKind.ObjectBindingPattern) {
if (declaration.dotDotDotToken) {
if (!isValidSpreadType(parentType)) {
error(declaration, Diagnostics.Rest_types_may_only_be_created_from_object_types);
return unknownType;
}
const literalMembers: PropertyName[] = [];
for (const element of pattern.elements) {
if (!(element as BindingElement).dotDotDotToken) {
literalMembers.push(element.propertyName || element.name as Identifier);
}
}
type = getRestType(parentType, literalMembers, declaration.symbol);
}
else {
// Use explicitly specified property name ({ p: xxx } form), or otherwise the implied name ({ p } form)
const name = declaration.propertyName || <Identifier>declaration.name;
if (isComputedNonLiteralName(name)) {
// computed properties with non-literal names are treated as 'any'
return anyType;
}
if (declaration.initializer) {
getContextualType(declaration.initializer);
}
// Use type of the specified property, or otherwise, for a numeric name, the type of the numeric index signature,
// or otherwise the type of the string index signature.
const text = getTextOfPropertyName(name);
type = getTypeOfPropertyOfType(parentType, text) ||
isNumericLiteralName(text) && getIndexTypeOfType(parentType, IndexKind.Number) ||
getIndexTypeOfType(parentType, IndexKind.String);
if (!type) {
error(name, Diagnostics.Type_0_has_no_property_1_and_no_string_index_signature, typeToString(parentType), declarationNameToString(name));
return unknownType;
}
}
}
else {
// This elementType will be used if the specific property corresponding to this index is not
// present (aka the tuple element property). This call also checks that the parentType is in
// fact an iterable or array (depending on target language).
const elementType = checkIteratedTypeOrElementType(parentType, pattern, /*allowStringInput*/ false, /*allowAsyncIterable*/ false);
if (declaration.dotDotDotToken) {
// Rest element has an array type with the same element type as the parent type
type = createArrayType(elementType);
}
else {
// Use specific property type when parent is a tuple or numeric index type when parent is an array
const propName = "" + indexOf(pattern.elements, declaration);
type = isTupleLikeType(parentType)
? getTypeOfPropertyOfType(parentType, propName)
: elementType;
if (!type) {
if (isTupleType(parentType)) {
error(declaration, Diagnostics.Tuple_type_0_with_length_1_cannot_be_assigned_to_tuple_with_length_2, typeToString(parentType), getTypeReferenceArity(<TypeReference>parentType), pattern.elements.length);
}
else {
error(declaration, Diagnostics.Type_0_has_no_property_1, typeToString(parentType), propName);
}
return unknownType;
}
}
}
// In strict null checking mode, if a default value of a non-undefined type is specified, remove
// undefined from the final type.
if (strictNullChecks && declaration.initializer && !(getFalsyFlags(checkExpressionCached(declaration.initializer)) & TypeFlags.Undefined)) {
type = getTypeWithFacts(type, TypeFacts.NEUndefined);
}
return declaration.initializer ?
getUnionType([type, checkExpressionCached(declaration.initializer)], /*subtypeReduction*/ true) :
type;
}
function getTypeForDeclarationFromJSDocComment(declaration: Node) {
const jsdocType = getJSDocType(declaration);
if (jsdocType) {
return getTypeFromTypeNode(jsdocType);
}
return undefined;
}
function isNullOrUndefined(node: Expression) {
const expr = skipParentheses(node);
return expr.kind === SyntaxKind.NullKeyword || expr.kind === SyntaxKind.Identifier && getResolvedSymbol(<Identifier>expr) === undefinedSymbol;
}
function isEmptyArrayLiteral(node: Expression) {
const expr = skipParentheses(node);
return expr.kind === SyntaxKind.ArrayLiteralExpression && (<ArrayLiteralExpression>expr).elements.length === 0;
}
function addOptionality(type: Type, optional: boolean): Type {
return strictNullChecks && optional ? getNullableType(type, TypeFlags.Undefined) : type;
}
// Return the inferred type for a variable, parameter, or property declaration
function getTypeForVariableLikeDeclaration(declaration: VariableLikeDeclaration, includeOptionality: boolean): Type {
if (declaration.flags & NodeFlags.JavaScriptFile) {
// If this is a variable in a JavaScript file, then use the JSDoc type (if it has
// one as its type), otherwise fallback to the below standard TS codepaths to
// try to figure it out.
const type = getTypeForDeclarationFromJSDocComment(declaration);
if (type && type !== unknownType) {
return type;
}
}
// A variable declared in a for..in statement is of type string, or of type keyof T when the
// right hand expression is of a type parameter type.
if (declaration.parent.parent.kind === SyntaxKind.ForInStatement) {
const indexType = getIndexType(checkNonNullExpression((<ForInStatement>declaration.parent.parent).expression));
return indexType.flags & (TypeFlags.TypeParameter | TypeFlags.Index) ? indexType : stringType;
}
if (declaration.parent.parent.kind === SyntaxKind.ForOfStatement) {
// checkRightHandSideOfForOf will return undefined if the for-of expression type was
// missing properties/signatures required to get its iteratedType (like
// [Symbol.iterator] or next). This may be because we accessed properties from anyType,
// or it may have led to an error inside getElementTypeOfIterable.
const forOfStatement = <ForOfStatement>declaration.parent.parent;
return checkRightHandSideOfForOf(forOfStatement.expression, forOfStatement.awaitModifier) || anyType;
}
if (isBindingPattern(declaration.parent)) {
return getTypeForBindingElement(<BindingElement>declaration);
}
// Use type from type annotation if one is present
if (declaration.type) {
const declaredType = getTypeFromTypeNode(declaration.type);
return addOptionality(declaredType, /*optional*/ declaration.questionToken && includeOptionality);
}
if ((noImplicitAny || declaration.flags & NodeFlags.JavaScriptFile) &&
declaration.kind === SyntaxKind.VariableDeclaration && !isBindingPattern(declaration.name) &&
!(getCombinedModifierFlags(declaration) & ModifierFlags.Export) && !isInAmbientContext(declaration)) {
// If --noImplicitAny is on or the declaration is in a Javascript file,
// use control flow tracked 'any' type for non-ambient, non-exported var or let variables with no
// initializer or a 'null' or 'undefined' initializer.
if (!(getCombinedNodeFlags(declaration) & NodeFlags.Const) && (!declaration.initializer || isNullOrUndefined(declaration.initializer))) {
return autoType;
}
// Use control flow tracked 'any[]' type for non-ambient, non-exported variables with an empty array
// literal initializer.
if (declaration.initializer && isEmptyArrayLiteral(declaration.initializer)) {
return autoArrayType;
}
}
if (declaration.kind === SyntaxKind.Parameter) {
const func = <FunctionLikeDeclaration>declaration.parent;
// For a parameter of a set accessor, use the type of the get accessor if one is present
if (func.kind === SyntaxKind.SetAccessor && !hasDynamicName(func)) {
const getter = getDeclarationOfKind<AccessorDeclaration>(declaration.parent.symbol, SyntaxKind.GetAccessor);
if (getter) {
const getterSignature = getSignatureFromDeclaration(getter);
const thisParameter = getAccessorThisParameter(func as AccessorDeclaration);
if (thisParameter && declaration === thisParameter) {
// Use the type from the *getter*
Debug.assert(!thisParameter.type);
return getTypeOfSymbol(getterSignature.thisParameter);
}
return getReturnTypeOfSignature(getterSignature);
}
}
// Use contextual parameter type if one is available
let type: Type;
if (declaration.symbol.name === "this") {
type = getContextualThisParameterType(func);
}
else {
type = getContextuallyTypedParameterType(<ParameterDeclaration>declaration);
}
if (type) {
return addOptionality(type, /*optional*/ declaration.questionToken && includeOptionality);
}
}
// Use the type of the initializer expression if one is present
if (declaration.initializer) {
const type = checkDeclarationInitializer(declaration);
return addOptionality(type, /*optional*/ declaration.questionToken && includeOptionality);
}
if (isJsxAttribute(declaration)) {
// if JSX attribute doesn't have initializer, by default the attribute will have boolean value of true.
// I.e <Elem attr /> is sugar for <Elem attr={true} />
return trueType;
}
// If it is a short-hand property assignment, use the type of the identifier
if (declaration.kind === SyntaxKind.ShorthandPropertyAssignment) {
return checkIdentifier(<Identifier>declaration.name);
}
// If the declaration specifies a binding pattern, use the type implied by the binding pattern
if (isBindingPattern(declaration.name)) {
return getTypeFromBindingPattern(<BindingPattern>declaration.name, /*includePatternInType*/ false, /*reportErrors*/ true);
}
// No type specified and nothing can be inferred
return undefined;
}
function getWidenedTypeFromJSSpecialPropertyDeclarations(symbol: Symbol) {
const types: Type[] = [];
let definedInConstructor = false;
let definedInMethod = false;
let jsDocType: Type;
for (const declaration of symbol.declarations) {
const expression = declaration.kind === SyntaxKind.BinaryExpression ? <BinaryExpression>declaration :
declaration.kind === SyntaxKind.PropertyAccessExpression ? <BinaryExpression>getAncestor(declaration, SyntaxKind.BinaryExpression) :
undefined;
if (!expression) {
return unknownType;
}
if (isPropertyAccessExpression(expression.left) && expression.left.expression.kind === SyntaxKind.ThisKeyword) {
if (getThisContainer(expression, /*includeArrowFunctions*/ false).kind === SyntaxKind.Constructor) {
definedInConstructor = true;
}
else {
definedInMethod = true;
}
}
// If there is a JSDoc type, use it
const type = getTypeForDeclarationFromJSDocComment(expression.parent);
if (type) {
const declarationType = getWidenedType(type);
if (!jsDocType) {
jsDocType = declarationType;
}
else if (jsDocType !== unknownType && declarationType !== unknownType && !isTypeIdenticalTo(jsDocType, declarationType)) {
const name = getNameOfDeclaration(declaration);
error(name, Diagnostics.Subsequent_variable_declarations_must_have_the_same_type_Variable_0_must_be_of_type_1_but_here_has_type_2, declarationNameToString(name), typeToString(jsDocType), typeToString(declarationType));
}
}
else if (!jsDocType) {
// If we don't have an explicit JSDoc type, get the type from the expression.
types.push(getWidenedLiteralType(checkExpressionCached(expression.right)));
}
}
const type = jsDocType || getUnionType(types, /*subtypeReduction*/ true);
return getWidenedType(addOptionality(type, definedInMethod && !definedInConstructor));
}
// Return the type implied by a binding pattern element. This is the type of the initializer of the element if
// one is present. Otherwise, if the element is itself a binding pattern, it is the type implied by the binding
// pattern. Otherwise, it is the type any.
function getTypeFromBindingElement(element: BindingElement, includePatternInType?: boolean, reportErrors?: boolean): Type {
if (element.initializer) {
return checkDeclarationInitializer(element);
}
if (isBindingPattern(element.name)) {
return getTypeFromBindingPattern(<BindingPattern>element.name, includePatternInType, reportErrors);
}
if (reportErrors && noImplicitAny && !declarationBelongsToPrivateAmbientMember(element)) {
reportImplicitAnyError(element, anyType);
}
return anyType;
}
// Return the type implied by an object binding pattern
function getTypeFromObjectBindingPattern(pattern: ObjectBindingPattern, includePatternInType: boolean, reportErrors: boolean): Type {
const members = createMap<Symbol>();
let stringIndexInfo: IndexInfo;
let hasComputedProperties = false;
forEach(pattern.elements, e => {
const name = e.propertyName || <Identifier>e.name;
if (isComputedNonLiteralName(name)) {
// do not include computed properties in the implied type
hasComputedProperties = true;
return;
}
if (e.dotDotDotToken) {
stringIndexInfo = createIndexInfo(anyType, /*isReadonly*/ false);
return;
}
const text = getTextOfPropertyName(name);
const flags = SymbolFlags.Property | (e.initializer ? SymbolFlags.Optional : 0);
const symbol = createSymbol(flags, text);
symbol.type = getTypeFromBindingElement(e, includePatternInType, reportErrors);
symbol.bindingElement = e;
members.set(symbol.name, symbol);
});
const result = createAnonymousType(undefined, members, emptyArray, emptyArray, stringIndexInfo, undefined);
if (includePatternInType) {
result.pattern = pattern;
}
if (hasComputedProperties) {
result.objectFlags |= ObjectFlags.ObjectLiteralPatternWithComputedProperties;
}
return result;
}
// Return the type implied by an array binding pattern
function getTypeFromArrayBindingPattern(pattern: BindingPattern, includePatternInType: boolean, reportErrors: boolean): Type {
const elements = pattern.elements;
const lastElement = lastOrUndefined(elements);
if (elements.length === 0 || (!isOmittedExpression(lastElement) && lastElement.dotDotDotToken)) {
return languageVersion >= ScriptTarget.ES2015 ? createIterableType(anyType) : anyArrayType;
}
// If the pattern has at least one element, and no rest element, then it should imply a tuple type.
const elementTypes = map(elements, e => isOmittedExpression(e) ? anyType : getTypeFromBindingElement(e, includePatternInType, reportErrors));
let result = createTupleType(elementTypes);
if (includePatternInType) {
result = cloneTypeReference(result);
result.pattern = pattern;
}
return result;
}
// Return the type implied by a binding pattern. This is the type implied purely by the binding pattern itself
// and without regard to its context (i.e. without regard any type annotation or initializer associated with the
// declaration in which the binding pattern is contained). For example, the implied type of [x, y] is [any, any]
// and the implied type of { x, y: z = 1 } is { x: any; y: number; }. The type implied by a binding pattern is
// used as the contextual type of an initializer associated with the binding pattern. Also, for a destructuring
// parameter with no type annotation or initializer, the type implied by the binding pattern becomes the type of
// the parameter.
function getTypeFromBindingPattern(pattern: BindingPattern, includePatternInType?: boolean, reportErrors?: boolean): Type {
return pattern.kind === SyntaxKind.ObjectBindingPattern
? getTypeFromObjectBindingPattern(<ObjectBindingPattern>pattern, includePatternInType, reportErrors)
: getTypeFromArrayBindingPattern(<ArrayBindingPattern>pattern, includePatternInType, reportErrors);
}
// Return the type associated with a variable, parameter, or property declaration. In the simple case this is the type
// specified in a type annotation or inferred from an initializer. However, in the case of a destructuring declaration it
// is a bit more involved. For example:
//
// var [x, s = ""] = [1, "one"];
//
// Here, the array literal [1, "one"] is contextually typed by the type [any, string], which is the implied type of the
// binding pattern [x, s = ""]. Because the contextual type is a tuple type, the resulting type of [1, "one"] is the
// tuple type [number, string]. Thus, the type inferred for 'x' is number and the type inferred for 's' is string.
function getWidenedTypeForVariableLikeDeclaration(declaration: VariableLikeDeclaration, reportErrors?: boolean): Type {
let type = getTypeForVariableLikeDeclaration(declaration, /*includeOptionality*/ true);
if (type) {
if (reportErrors) {
reportErrorsFromWidening(declaration, type);
}
// During a normal type check we'll never get to here with a property assignment (the check of the containing
// object literal uses a different path). We exclude widening only so that language services and type verification
// tools see the actual type.
if (declaration.kind === SyntaxKind.PropertyAssignment) {
return type;
}
return getWidenedType(type);
}
// Rest parameters default to type any[], other parameters default to type any
type = declaration.dotDotDotToken ? anyArrayType : anyType;
// Report implicit any errors unless this is a private property within an ambient declaration
if (reportErrors && noImplicitAny) {
if (!declarationBelongsToPrivateAmbientMember(declaration)) {
reportImplicitAnyError(declaration, type);
}
}
return type;
}
function declarationBelongsToPrivateAmbientMember(declaration: VariableLikeDeclaration) {
const root = getRootDeclaration(declaration);
const memberDeclaration = root.kind === SyntaxKind.Parameter ? root.parent : root;
return isPrivateWithinAmbient(memberDeclaration);
}
function getTypeOfVariableOrParameterOrProperty(symbol: Symbol): Type {
const links = getSymbolLinks(symbol);
if (!links.type) {
// Handle prototype property
if (symbol.flags & SymbolFlags.Prototype) {
return links.type = getTypeOfPrototypeProperty(symbol);
}
// Handle catch clause variables
const declaration = symbol.valueDeclaration;
if (isCatchClauseVariableDeclarationOrBindingElement(declaration)) {
return links.type = anyType;
}
// Handle export default expressions
if (declaration.kind === SyntaxKind.ExportAssignment) {
return links.type = checkExpression((<ExportAssignment>declaration).expression);
}
if (declaration.flags & NodeFlags.JavaScriptFile && declaration.kind === SyntaxKind.JSDocPropertyTag && (<JSDocPropertyTag>declaration).typeExpression) {
return links.type = getTypeFromTypeNode((<JSDocPropertyTag>declaration).typeExpression.type);
}
// Handle variable, parameter or property
if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) {
return unknownType;
}
let type: Type;
// Handle certain special assignment kinds, which happen to union across multiple declarations:
// * module.exports = expr
// * exports.p = expr
// * this.p = expr
// * className.prototype.method = expr
if (declaration.kind === SyntaxKind.BinaryExpression ||
declaration.kind === SyntaxKind.PropertyAccessExpression && declaration.parent.kind === SyntaxKind.BinaryExpression) {
type = getWidenedTypeFromJSSpecialPropertyDeclarations(symbol);
}
else {
type = getWidenedTypeForVariableLikeDeclaration(<VariableLikeDeclaration>declaration, /*reportErrors*/ true);
}
if (!popTypeResolution()) {
type = reportCircularityError(symbol);
}
links.type = type;
}
return links.type;
}
function getAnnotatedAccessorType(accessor: AccessorDeclaration): Type {
if (accessor) {
if (accessor.kind === SyntaxKind.GetAccessor) {
return accessor.type && getTypeFromTypeNode(accessor.type);
}
else {
const setterTypeAnnotation = getSetAccessorTypeAnnotationNode(accessor);
return setterTypeAnnotation && getTypeFromTypeNode(setterTypeAnnotation);
}
}
return undefined;
}
function getAnnotatedAccessorThisParameter(accessor: AccessorDeclaration): Symbol | undefined {
const parameter = getAccessorThisParameter(accessor);
return parameter && parameter.symbol;
}
function getThisTypeOfDeclaration(declaration: SignatureDeclaration): Type | undefined {
return getThisTypeOfSignature(getSignatureFromDeclaration(declaration));
}
function getTypeOfAccessors(symbol: Symbol): Type {
const links = getSymbolLinks(symbol);
if (!links.type) {
const getter = getDeclarationOfKind<AccessorDeclaration>(symbol, SyntaxKind.GetAccessor);
const setter = getDeclarationOfKind<AccessorDeclaration>(symbol, SyntaxKind.SetAccessor);
if (getter && getter.flags & NodeFlags.JavaScriptFile) {
const jsDocType = getTypeForDeclarationFromJSDocComment(getter);
if (jsDocType) {
return links.type = jsDocType;
}
}
if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) {
return unknownType;
}
let type: Type;
// First try to see if the user specified a return type on the get-accessor.
const 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.
const setterParameterType = getAnnotatedAccessorType(setter);
if (setterParameterType) {
type = setterParameterType;
}
else {
// If there are no specified types, try to infer it from the body of the get accessor if it exists.
if (getter && getter.body) {
type = getReturnTypeFromBody(getter);
}
// Otherwise, fall back to 'any'.
else {
if (noImplicitAny) {
if (setter) {
error(setter, Diagnostics.Property_0_implicitly_has_type_any_because_its_set_accessor_lacks_a_parameter_type_annotation, symbolToString(symbol));
}
else {
Debug.assert(!!getter, "there must existed getter as we are current checking either setter or getter in this function");
error(getter, Diagnostics.Property_0_implicitly_has_type_any_because_its_get_accessor_lacks_a_return_type_annotation, symbolToString(symbol));
}
}
type = anyType;
}
}
}
if (!popTypeResolution()) {
type = anyType;
if (noImplicitAny) {
const getter = getDeclarationOfKind<AccessorDeclaration>(symbol, SyntaxKind.GetAccessor);
error(getter, Diagnostics._0_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions, symbolToString(symbol));
}
}
links.type = type;
}
return links.type;
}
function getBaseTypeVariableOfClass(symbol: Symbol) {
const baseConstructorType = getBaseConstructorTypeOfClass(getDeclaredTypeOfClassOrInterface(symbol));
return baseConstructorType.flags & TypeFlags.TypeVariable ? baseConstructorType : undefined;
}
function getTypeOfFuncClassEnumModule(symbol: Symbol): Type {
const links = getSymbolLinks(symbol);
if (!links.type) {
if (symbol.flags & SymbolFlags.Module && isShorthandAmbientModuleSymbol(symbol)) {
links.type = anyType;
}
else {
const type = createObjectType(ObjectFlags.Anonymous, symbol);
if (symbol.flags & SymbolFlags.Class) {
const baseTypeVariable = getBaseTypeVariableOfClass(symbol);
links.type = baseTypeVariable ? getIntersectionType([type, baseTypeVariable]) : type;
}
else {
links.type = strictNullChecks && symbol.flags & SymbolFlags.Optional ? getNullableType(type, TypeFlags.Undefined) : type;
}
}
}
return links.type;
}
function getTypeOfEnumMember(symbol: Symbol): Type {
const links = getSymbolLinks(symbol);
if (!links.type) {
links.type = getDeclaredTypeOfEnumMember(symbol);
}
return links.type;
}
function getTypeOfAlias(symbol: Symbol): Type {
const links = getSymbolLinks(symbol);
if (!links.type) {
const targetSymbol = resolveAlias(symbol);
// It only makes sense to get the type of a value symbol. If the result of resolving
// the alias is not a value, then it has no type. To get the type associated with a
// type symbol, call getDeclaredTypeOfSymbol.
// This check is important because without it, a call to getTypeOfSymbol could end
// up recursively calling getTypeOfAlias, causing a stack overflow.
links.type = targetSymbol.flags & SymbolFlags.Value
? getTypeOfSymbol(targetSymbol)
: unknownType;
}
return links.type;
}
function getTypeOfInstantiatedSymbol(symbol: Symbol): Type {
const links = getSymbolLinks(symbol);
if (!links.type) {
if (symbolInstantiationDepth === 100) {
error(symbol.valueDeclaration, Diagnostics.Generic_type_instantiation_is_excessively_deep_and_possibly_infinite);
links.type = unknownType;
}
else {
if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) {
return unknownType;
}
symbolInstantiationDepth++;
let type = instantiateType(getTypeOfSymbol(links.target), links.mapper);
symbolInstantiationDepth--;
if (!popTypeResolution()) {
type = reportCircularityError(symbol);
}
links.type = type;
}
}
return links.type;
}
function reportCircularityError(symbol: Symbol) {
// Check if variable has type annotation that circularly references the variable itself
if ((<VariableLikeDeclaration>symbol.valueDeclaration).type) {
error(symbol.valueDeclaration, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_type_annotation,
symbolToString(symbol));
return unknownType;
}
// Otherwise variable has initializer that circularly references the variable itself
if (noImplicitAny) {
error(symbol.valueDeclaration, Diagnostics._0_implicitly_has_type_any_because_it_does_not_have_a_type_annotation_and_is_referenced_directly_or_indirectly_in_its_own_initializer,
symbolToString(symbol));
}
return anyType;
}
function getTypeOfSymbol(symbol: Symbol): Type {
if (getCheckFlags(symbol) & CheckFlags.Instantiated) {
return getTypeOfInstantiatedSymbol(symbol);
}
if (symbol.flags & (SymbolFlags.Variable | SymbolFlags.Property)) {
return getTypeOfVariableOrParameterOrProperty(symbol);
}
if (symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.Enum | SymbolFlags.ValueModule)) {
return getTypeOfFuncClassEnumModule(symbol);
}
if (symbol.flags & SymbolFlags.EnumMember) {
return getTypeOfEnumMember(symbol);
}
if (symbol.flags & SymbolFlags.Accessor) {
return getTypeOfAccessors(symbol);
}
if (symbol.flags & SymbolFlags.Alias) {
return getTypeOfAlias(symbol);
}
return unknownType;
}
function isReferenceToType(type: Type, target: Type) {
return type !== undefined
&& target !== undefined
&& (getObjectFlags(type) & ObjectFlags.Reference) !== 0
&& (<TypeReference>type).target === target;
}
function getTargetType(type: Type): Type {
return getObjectFlags(type) & ObjectFlags.Reference ? (<TypeReference>type).target : type;
}
function hasBaseType(type: Type, checkBase: Type) {
return check(type);
function check(type: Type): boolean {
if (getObjectFlags(type) & (ObjectFlags.ClassOrInterface | ObjectFlags.Reference)) {
const target = <InterfaceType>getTargetType(type);
return target === checkBase || forEach(getBaseTypes(target), check);
}
else if (type.flags & TypeFlags.Intersection) {
return forEach((<IntersectionType>type).types, check);
}
}
}
// Appends the type parameters given by a list of declarations to a set of type parameters and returns the resulting set.
// The function allocates a new array if the input type parameter set is undefined, but otherwise it modifies the set
// in-place and returns the same array.
function appendTypeParameters(typeParameters: TypeParameter[], declarations: TypeParameterDeclaration[]): TypeParameter[] {
for (const declaration of declarations) {
const tp = getDeclaredTypeOfTypeParameter(getSymbolOfNode(declaration));
if (!typeParameters) {
typeParameters = [tp];
}
else if (!contains(typeParameters, tp)) {
typeParameters.push(tp);
}
}
return typeParameters;
}
// Appends the outer type parameters of a node to a set of type parameters and returns the resulting set. The function
// allocates a new array if the input type parameter set is undefined, but otherwise it modifies the set in-place and
// returns the same array.
function appendOuterTypeParameters(typeParameters: TypeParameter[], node: Node): TypeParameter[] {
while (true) {
node = node.parent;
if (!node) {
return typeParameters;
}
if (node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.ClassExpression ||
node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.FunctionExpression ||
node.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.ArrowFunction) {
const declarations = (<ClassLikeDeclaration | FunctionLikeDeclaration>node).typeParameters;
if (declarations) {
return appendTypeParameters(appendOuterTypeParameters(typeParameters, node), declarations);
}
}
}
}
// The outer type parameters are those defined by enclosing generic classes, methods, or functions.
function getOuterTypeParametersOfClassOrInterface(symbol: Symbol): TypeParameter[] {
const declaration = symbol.flags & SymbolFlags.Class ? symbol.valueDeclaration : getDeclarationOfKind(symbol, SyntaxKind.InterfaceDeclaration);
return appendOuterTypeParameters(/*typeParameters*/ undefined, declaration);
}
// The local type parameters are the combined set of type parameters from all declarations of the class,
// interface, or type alias.
function getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol: Symbol): TypeParameter[] {
let result: TypeParameter[];
for (const node of symbol.declarations) {
if (node.kind === SyntaxKind.InterfaceDeclaration || node.kind === SyntaxKind.ClassDeclaration ||
node.kind === SyntaxKind.ClassExpression || node.kind === SyntaxKind.TypeAliasDeclaration) {
const declaration = <InterfaceDeclaration | TypeAliasDeclaration>node;
if (declaration.typeParameters) {
result = appendTypeParameters(result, declaration.typeParameters);
}
}
}
return result;
}
// The full set of type parameters for a generic class or interface type consists of its outer type parameters plus
// its locally declared type parameters.
function getTypeParametersOfClassOrInterface(symbol: Symbol): TypeParameter[] {
return concatenate(getOuterTypeParametersOfClassOrInterface(symbol), getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol));
}
// A type is a mixin constructor if it has a single construct signature taking no type parameters and a single
// rest parameter of type any[].
function isMixinConstructorType(type: Type) {
const signatures = getSignaturesOfType(type, SignatureKind.Construct);
if (signatures.length === 1) {
const s = signatures[0];
return !s.typeParameters && s.parameters.length === 1 && s.hasRestParameter && getTypeOfParameter(s.parameters[0]) === anyArrayType;
}
return false;
}
function isConstructorType(type: Type): boolean {
if (isValidBaseType(type) && getSignaturesOfType(type, SignatureKind.Construct).length > 0) {
return true;
}
if (type.flags & TypeFlags.TypeVariable) {
const constraint = getBaseConstraintOfType(type);
return constraint && isValidBaseType(constraint) && isMixinConstructorType(constraint);
}
return false;
}
function getBaseTypeNodeOfClass(type: InterfaceType): ExpressionWithTypeArguments {
return getClassExtendsHeritageClauseElement(<ClassLikeDeclaration>type.symbol.valueDeclaration);
}
function getConstructorsForTypeArguments(type: Type, typeArgumentNodes: TypeNode[], location: Node): Signature[] {
const typeArgCount = length(typeArgumentNodes);
const isJavaScript = isInJavaScriptFile(location);
return filter(getSignaturesOfType(type, SignatureKind.Construct),
sig => (isJavaScript || typeArgCount >= getMinTypeArgumentCount(sig.typeParameters)) && typeArgCount <= length(sig.typeParameters));
}
function getInstantiatedConstructorsForTypeArguments(type: Type, typeArgumentNodes: TypeNode[], location: Node): Signature[] {
let signatures = getConstructorsForTypeArguments(type, typeArgumentNodes, location);
if (typeArgumentNodes) {
const typeArguments = map(typeArgumentNodes, getTypeFromTypeNode);
signatures = map(signatures, sig => getSignatureInstantiation(sig, typeArguments));
}
return signatures;
}
/**
* The base constructor of a class can resolve to
* * undefinedType if the class has no extends clause,
* * unknownType if an error occurred during resolution of the extends expression,
* * nullType if the extends expression is the null value,
* * anyType if the extends expression has type any, or
* * an object type with at least one construct signature.
*/
function getBaseConstructorTypeOfClass(type: InterfaceType): Type {
if (!type.resolvedBaseConstructorType) {
const baseTypeNode = getBaseTypeNodeOfClass(type);
if (!baseTypeNode) {
return type.resolvedBaseConstructorType = undefinedType;
}
if (!pushTypeResolution(type, TypeSystemPropertyName.ResolvedBaseConstructorType)) {
return unknownType;
}
const baseConstructorType = checkExpression(baseTypeNode.expression);
if (baseConstructorType.flags & (TypeFlags.Object | TypeFlags.Intersection)) {
// Resolving the members of a class requires us to resolve the base class of that class.
// We force resolution here such that we catch circularities now.
resolveStructuredTypeMembers(<ObjectType>baseConstructorType);
}
if (!popTypeResolution()) {
error(type.symbol.valueDeclaration, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_base_expression, symbolToString(type.symbol));
return type.resolvedBaseConstructorType = unknownType;
}
if (!(baseConstructorType.flags & TypeFlags.Any) && baseConstructorType !== nullWideningType && !isConstructorType(baseConstructorType)) {
error(baseTypeNode.expression, Diagnostics.Type_0_is_not_a_constructor_function_type, typeToString(baseConstructorType));
return type.resolvedBaseConstructorType = unknownType;
}
type.resolvedBaseConstructorType = baseConstructorType;
}
return type.resolvedBaseConstructorType;
}
function getBaseTypes(type: InterfaceType): BaseType[] {
if (!type.resolvedBaseTypes) {
if (type.objectFlags & ObjectFlags.Tuple) {
type.resolvedBaseTypes = [createArrayType(getUnionType(type.typeParameters))];
}
else if (type.symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) {
if (type.symbol.flags & SymbolFlags.Class) {
resolveBaseTypesOfClass(type);
}
if (type.symbol.flags & SymbolFlags.Interface) {
resolveBaseTypesOfInterface(type);
}
}
else {
Debug.fail("type must be class or interface");
}
}
return type.resolvedBaseTypes;
}
function resolveBaseTypesOfClass(type: InterfaceType): void {
type.resolvedBaseTypes = type.resolvedBaseTypes || emptyArray;
const baseConstructorType = getApparentType(getBaseConstructorTypeOfClass(type));
if (!(baseConstructorType.flags & (TypeFlags.Object | TypeFlags.Intersection | TypeFlags.Any))) {
return;
}
const baseTypeNode = getBaseTypeNodeOfClass(type);
const typeArgs = typeArgumentsFromTypeReferenceNode(baseTypeNode);
let baseType: Type;
const originalBaseType = baseConstructorType && baseConstructorType.symbol ? getDeclaredTypeOfSymbol(baseConstructorType.symbol) : undefined;
if (baseConstructorType.symbol && baseConstructorType.symbol.flags & SymbolFlags.Class &&
areAllOuterTypeParametersApplied(originalBaseType)) {
// When base constructor type is a class with no captured type arguments we know that the constructors all have the same type parameters as the
// class and all return the instance type of the class. There is no need for further checks and we can apply the
// type arguments in the same manner as a type reference to get the same error reporting experience.
baseType = getTypeFromClassOrInterfaceReference(baseTypeNode, baseConstructorType.symbol, typeArgs);
}
else if (baseConstructorType.flags & TypeFlags.Any) {
baseType = baseConstructorType;
}
else {
// The class derives from a "class-like" constructor function, check that we have at least one construct signature
// with a matching number of type parameters and use the return type of the first instantiated signature. Elsewhere
// we check that all instantiated signatures return the same type.
const constructors = getInstantiatedConstructorsForTypeArguments(baseConstructorType, baseTypeNode.typeArguments, baseTypeNode);
if (!constructors.length) {
error(baseTypeNode.expression, Diagnostics.No_base_constructor_has_the_specified_number_of_type_arguments);
return;
}
baseType = getReturnTypeOfSignature(constructors[0]);
}
// In a JS file, you can use the @augments jsdoc tag to specify a base type with type parameters
const valueDecl = type.symbol.valueDeclaration;
if (valueDecl && isInJavaScriptFile(valueDecl)) {
const augTag = getJSDocAugmentsTag(type.symbol.valueDeclaration);
if (augTag) {
baseType = getTypeFromTypeNode(augTag.typeExpression.type);
}
}
if (baseType === unknownType) {
return;
}
if (!isValidBaseType(baseType)) {
error(baseTypeNode.expression, Diagnostics.Base_constructor_return_type_0_is_not_a_class_or_interface_type, typeToString(baseType));
return;
}
if (type === baseType || hasBaseType(<BaseType>baseType, type)) {
error(valueDecl, Diagnostics.Type_0_recursively_references_itself_as_a_base_type,
typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType));
return;
}
if (type.resolvedBaseTypes === emptyArray) {
type.resolvedBaseTypes = [<ObjectType>baseType];
}
else {
type.resolvedBaseTypes.push(<ObjectType>baseType);
}
}
function areAllOuterTypeParametersApplied(type: Type): boolean {
// An unapplied type parameter has its symbol still the same as the matching argument symbol.
// Since parameters are applied outer-to-inner, only the last outer parameter needs to be checked.
const outerTypeParameters = (<InterfaceType>type).outerTypeParameters;
if (outerTypeParameters) {
const last = outerTypeParameters.length - 1;
const typeArguments = (<TypeReference>type).typeArguments;
return outerTypeParameters[last].symbol !== typeArguments[last].symbol;
}
return true;
}
// A valid base type is `any`, any non-generic object type or intersection of non-generic
// object types.
function isValidBaseType(type: Type): boolean {
return type.flags & (TypeFlags.Object | TypeFlags.NonPrimitive | TypeFlags.Any) && !isGenericMappedType(type) ||
type.flags & TypeFlags.Intersection && !forEach((<IntersectionType>type).types, t => !isValidBaseType(t));
}
function resolveBaseTypesOfInterface(type: InterfaceType): void {
type.resolvedBaseTypes = type.resolvedBaseTypes || emptyArray;
for (const declaration of type.symbol.declarations) {
if (declaration.kind === SyntaxKind.InterfaceDeclaration && getInterfaceBaseTypeNodes(<InterfaceDeclaration>declaration)) {
for (const node of getInterfaceBaseTypeNodes(<InterfaceDeclaration>declaration)) {
const baseType = getTypeFromTypeNode(node);
if (baseType !== unknownType) {
if (isValidBaseType(baseType)) {
if (type !== baseType && !hasBaseType(<BaseType>baseType, type)) {
if (type.resolvedBaseTypes === emptyArray) {
type.resolvedBaseTypes = [<ObjectType>baseType];
}
else {
type.resolvedBaseTypes.push(<ObjectType>baseType);
}
}
else {
error(declaration, Diagnostics.Type_0_recursively_references_itself_as_a_base_type, typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType));
}
}
else {
error(node, Diagnostics.An_interface_may_only_extend_a_class_or_another_interface);
}
}
}
}
}
}
// Returns true if the interface given by the symbol is free of "this" references. Specifically, the result is
// true if the interface itself contains no references to "this" in its body, if all base types are interfaces,
// and if none of the base interfaces have a "this" type.
function isIndependentInterface(symbol: Symbol): boolean {
for (const declaration of symbol.declarations) {
if (declaration.kind === SyntaxKind.InterfaceDeclaration) {
if (declaration.flags & NodeFlags.ContainsThis) {
return false;
}
const baseTypeNodes = getInterfaceBaseTypeNodes(<InterfaceDeclaration>declaration);
if (baseTypeNodes) {
for (const node of baseTypeNodes) {
if (isEntityNameExpression(node.expression)) {
const baseSymbol = resolveEntityName(node.expression, SymbolFlags.Type, /*ignoreErrors*/ true);
if (!baseSymbol || !(baseSymbol.flags & SymbolFlags.Interface) || getDeclaredTypeOfClassOrInterface(baseSymbol).thisType) {
return false;
}
}
}
}
}
}
return true;
}
function getDeclaredTypeOfClassOrInterface(symbol: Symbol): InterfaceType {
const links = getSymbolLinks(symbol);
if (!links.declaredType) {
const kind = symbol.flags & SymbolFlags.Class ? ObjectFlags.Class : ObjectFlags.Interface;
const type = links.declaredType = <InterfaceType>createObjectType(kind, symbol);
const outerTypeParameters = getOuterTypeParametersOfClassOrInterface(symbol);
const localTypeParameters = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol);
// A class or interface is generic if it has type parameters or a "this" type. We always give classes a "this" type
// because it is not feasible to analyze all members to determine if the "this" type escapes the class (in particular,
// property types inferred from initializers and method return types inferred from return statements are very hard
// to exhaustively analyze). We give interfaces a "this" type if we can't definitely determine that they are free of
// "this" references.
if (outerTypeParameters || localTypeParameters || kind === ObjectFlags.Class || !isIndependentInterface(symbol)) {
type.objectFlags |= ObjectFlags.Reference;
type.typeParameters = concatenate(outerTypeParameters, localTypeParameters);
type.outerTypeParameters = outerTypeParameters;
type.localTypeParameters = localTypeParameters;
(<GenericType>type).instantiations = createMap<TypeReference>();
(<GenericType>type).instantiations.set(getTypeListId(type.typeParameters), <GenericType>type);
(<GenericType>type).target = <GenericType>type;
(<GenericType>type).typeArguments = type.typeParameters;
type.thisType = <TypeParameter>createType(TypeFlags.TypeParameter);
type.thisType.isThisType = true;
type.thisType.symbol = symbol;
type.thisType.constraint = type;
}
}
return <InterfaceType>links.declaredType;
}
function getDeclaredTypeOfTypeAlias(symbol: Symbol): Type {
const links = getSymbolLinks(symbol);
if (!links.declaredType) {
// Note that we use the links object as the target here because the symbol object is used as the unique
// identity for resolution of the 'type' property in SymbolLinks.
if (!pushTypeResolution(symbol, TypeSystemPropertyName.DeclaredType)) {
return unknownType;
}
let declaration: JSDocTypedefTag | TypeAliasDeclaration = getDeclarationOfKind<JSDocTypedefTag>(symbol, SyntaxKind.JSDocTypedefTag);
let type: Type;
if (declaration) {
if (declaration.jsDocTypeLiteral) {
type = getTypeFromTypeNode(declaration.jsDocTypeLiteral);
}
else {
type = getTypeFromTypeNode(declaration.typeExpression.type);
}
}
else {
declaration = getDeclarationOfKind<TypeAliasDeclaration>(symbol, SyntaxKind.TypeAliasDeclaration);
type = getTypeFromTypeNode(declaration.type);
}
if (popTypeResolution()) {
const typeParameters = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol);
if (typeParameters) {
// Initialize the instantiation cache for generic type aliases. The declared type corresponds to
// an instantiation of the type alias with the type parameters supplied as type arguments.
links.typeParameters = typeParameters;
links.instantiations = createMap<Type>();
links.instantiations.set(getTypeListId(typeParameters), type);
}
}
else {
type = unknownType;
error(declaration.name, Diagnostics.Type_alias_0_circularly_references_itself, symbolToString(symbol));
}
links.declaredType = type;
}
return links.declaredType;
}
function isLiteralEnumMember(member: EnumMember) {
const expr = member.initializer;
if (!expr) {
return !isInAmbientContext(member);
}
return expr.kind === SyntaxKind.StringLiteral || expr.kind === SyntaxKind.NumericLiteral ||
expr.kind === SyntaxKind.PrefixUnaryExpression && (<PrefixUnaryExpression>expr).operator === SyntaxKind.MinusToken &&
(<PrefixUnaryExpression>expr).operand.kind === SyntaxKind.NumericLiteral ||
expr.kind === SyntaxKind.Identifier && (nodeIsMissing(expr) || !!getSymbolOfNode(member.parent).exports.get((<Identifier>expr).text));
}
function getEnumKind(symbol: Symbol): EnumKind {
const links = getSymbolLinks(symbol);
if (links.enumKind !== undefined) {
return links.enumKind;
}
let hasNonLiteralMember = false;
for (const declaration of symbol.declarations) {
if (declaration.kind === SyntaxKind.EnumDeclaration) {
for (const member of (<EnumDeclaration>declaration).members) {
if (member.initializer && member.initializer.kind === SyntaxKind.StringLiteral) {
return links.enumKind = EnumKind.Literal;
}
if (!isLiteralEnumMember(member)) {
hasNonLiteralMember = true;
}
}
}
}
return links.enumKind = hasNonLiteralMember ? EnumKind.Numeric : EnumKind.Literal;
}
function getBaseTypeOfEnumLiteralType(type: Type) {
return type.flags & TypeFlags.EnumLiteral && !(type.flags & TypeFlags.Union) ? getDeclaredTypeOfSymbol(getParentOfSymbol(type.symbol)) : type;
}
function getDeclaredTypeOfEnum(symbol: Symbol): Type {
const links = getSymbolLinks(symbol);
if (links.declaredType) {
return links.declaredType;
}
if (getEnumKind(symbol) === EnumKind.Literal) {
enumCount++;
const memberTypeList: Type[] = [];
for (const declaration of symbol.declarations) {
if (declaration.kind === SyntaxKind.EnumDeclaration) {
for (const member of (<EnumDeclaration>declaration).members) {
const memberType = getLiteralType(getEnumMemberValue(member), enumCount, getSymbolOfNode(member));
getSymbolLinks(getSymbolOfNode(member)).declaredType = memberType;
memberTypeList.push(memberType);
}
}
}
if (memberTypeList.length) {
const enumType = getUnionType(memberTypeList, /*subtypeReduction*/ false, symbol, /*aliasTypeArguments*/ undefined);
if (enumType.flags & TypeFlags.Union) {
enumType.flags |= TypeFlags.EnumLiteral;
enumType.symbol = symbol;
}
return links.declaredType = enumType;
}
}
const enumType = createType(TypeFlags.Enum);
enumType.symbol = symbol;
return links.declaredType = enumType;
}
function getDeclaredTypeOfEnumMember(symbol: Symbol): Type {
const links = getSymbolLinks(symbol);
if (!links.declaredType) {
const enumType = getDeclaredTypeOfEnum(getParentOfSymbol(symbol));
if (!links.declaredType) {
links.declaredType = enumType;
}
}
return links.declaredType;
}
function getDeclaredTypeOfTypeParameter(symbol: Symbol): TypeParameter {
const links = getSymbolLinks(symbol);
if (!links.declaredType) {
const type = <TypeParameter>createType(TypeFlags.TypeParameter);
type.symbol = symbol;
links.declaredType = type;
}
return <TypeParameter>links.declaredType;
}
function getDeclaredTypeOfAlias(symbol: Symbol): Type {
const links = getSymbolLinks(symbol);
if (!links.declaredType) {
links.declaredType = getDeclaredTypeOfSymbol(resolveAlias(symbol));
}
return links.declaredType;
}
function getDeclaredTypeOfSymbol(symbol: Symbol): Type {
if (symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) {
return getDeclaredTypeOfClassOrInterface(symbol);
}
if (symbol.flags & SymbolFlags.TypeAlias) {
return getDeclaredTypeOfTypeAlias(symbol);
}
if (symbol.flags & SymbolFlags.TypeParameter) {
return getDeclaredTypeOfTypeParameter(symbol);
}
if (symbol.flags & SymbolFlags.Enum) {
return getDeclaredTypeOfEnum(symbol);
}
if (symbol.flags & SymbolFlags.EnumMember) {
return getDeclaredTypeOfEnumMember(symbol);
}
if (symbol.flags & SymbolFlags.Alias) {
return getDeclaredTypeOfAlias(symbol);
}
return unknownType;
}
// A type reference is considered independent if each type argument is considered independent.
function isIndependentTypeReference(node: TypeReferenceNode): boolean {
if (node.typeArguments) {
for (const typeNode of node.typeArguments) {
if (!isIndependentType(typeNode)) {
return false;
}
}
}
return true;
}
// A type is considered independent if it the any, string, number, boolean, symbol, or void keyword, a string
// literal type, an array with an element type that is considered independent, or a type reference that is
// considered independent.
function isIndependentType(node: TypeNode): boolean {
switch (node.kind) {
case SyntaxKind.AnyKeyword:
case SyntaxKind.StringKeyword:
case SyntaxKind.NumberKeyword:
case SyntaxKind.BooleanKeyword:
case SyntaxKind.SymbolKeyword:
case SyntaxKind.ObjectKeyword:
case SyntaxKind.VoidKeyword:
case SyntaxKind.UndefinedKeyword:
case SyntaxKind.NullKeyword:
case SyntaxKind.NeverKeyword:
case SyntaxKind.LiteralType:
return true;
case SyntaxKind.ArrayType:
return isIndependentType((<ArrayTypeNode>node).elementType);
case SyntaxKind.TypeReference:
return isIndependentTypeReference(<TypeReferenceNode>node);
}
return false;
}
// A variable-like declaration is considered independent (free of this references) if it has a type annotation
// that specifies an independent type, or if it has no type annotation and no initializer (and thus of type any).
function isIndependentVariableLikeDeclaration(node: VariableLikeDeclaration): boolean {
return node.type && isIndependentType(node.type) || !node.type && !node.initializer;
}
// A function-like declaration is considered independent (free of this references) if it has a return type
// annotation that is considered independent and if each parameter is considered independent.
function isIndependentFunctionLikeDeclaration(node: FunctionLikeDeclaration): boolean {
if (node.kind !== SyntaxKind.Constructor && (!node.type || !isIndependentType(node.type))) {
return false;
}
for (const parameter of node.parameters) {
if (!isIndependentVariableLikeDeclaration(parameter)) {
return false;
}
}
return true;
}
// Returns true if the class or interface member given by the symbol is free of "this" references. The
// function may return false for symbols that are actually free of "this" references because it is not
// feasible to perform a complete analysis in all cases. In particular, property members with types
// inferred from their initializers and function members with inferred return types are conservatively
// assumed not to be free of "this" references.
function isIndependentMember(symbol: Symbol): boolean {
if (symbol.declarations && symbol.declarations.length === 1) {
const declaration = symbol.declarations[0];
if (declaration) {
switch (declaration.kind) {
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.PropertySignature:
return isIndependentVariableLikeDeclaration(<VariableLikeDeclaration>declaration);
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
case SyntaxKind.Constructor:
return isIndependentFunctionLikeDeclaration(<FunctionLikeDeclaration>declaration);
}
}
}
return false;
}
function createSymbolTable(symbols: Symbol[]): SymbolTable {
const result = createMap<Symbol>();
for (const symbol of symbols) {
result.set(symbol.name, symbol);
}
return result;
}
// The mappingThisOnly flag indicates that the only type parameter being mapped is "this". When the flag is true,
// we check symbols to see if we can quickly conclude they are free of "this" references, thus needing no instantiation.
function createInstantiatedSymbolTable(symbols: Symbol[], mapper: TypeMapper, mappingThisOnly: boolean): SymbolTable {
const result = createMap<Symbol>();
for (const symbol of symbols) {
result.set(symbol.name, mappingThisOnly && isIndependentMember(symbol) ? symbol : instantiateSymbol(symbol, mapper));
}
return result;
}
function addInheritedMembers(symbols: SymbolTable, baseSymbols: Symbol[]) {
for (const s of baseSymbols) {
if (!symbols.has(s.name)) {
symbols.set(s.name, s);
}
}
}
function resolveDeclaredMembers(type: InterfaceType): InterfaceTypeWithDeclaredMembers {
if (!(<InterfaceTypeWithDeclaredMembers>type).declaredProperties) {
const symbol = type.symbol;
(<InterfaceTypeWithDeclaredMembers>type).declaredProperties = getNamedMembers(symbol.members);
(<InterfaceTypeWithDeclaredMembers>type).declaredCallSignatures = getSignaturesOfSymbol(symbol.members.get("__call"));
(<InterfaceTypeWithDeclaredMembers>type).declaredConstructSignatures = getSignaturesOfSymbol(symbol.members.get("__new"));
(<InterfaceTypeWithDeclaredMembers>type).declaredStringIndexInfo = getIndexInfoOfSymbol(symbol, IndexKind.String);
(<InterfaceTypeWithDeclaredMembers>type).declaredNumberIndexInfo = getIndexInfoOfSymbol(symbol, IndexKind.Number);
}
return <InterfaceTypeWithDeclaredMembers>type;
}
function getTypeWithThisArgument(type: Type, thisArgument?: Type): Type {
if (getObjectFlags(type) & ObjectFlags.Reference) {
const target = (<TypeReference>type).target;
const typeArguments = (<TypeReference>type).typeArguments;
if (length(target.typeParameters) === length(typeArguments)) {
return createTypeReference(target, concatenate(typeArguments, [thisArgument || target.thisType]));
}
}
else if (type.flags & TypeFlags.Intersection) {
return getIntersectionType(map((<IntersectionType>type).types, t => getTypeWithThisArgument(t, thisArgument)));
}
return type;
}
function resolveObjectTypeMembers(type: ObjectType, source: InterfaceTypeWithDeclaredMembers, typeParameters: TypeParameter[], typeArguments: Type[]) {
let mapper: TypeMapper;
let members: SymbolTable;
let callSignatures: Signature[];
let constructSignatures: Signature[];
let stringIndexInfo: IndexInfo;
let numberIndexInfo: IndexInfo;
if (rangeEquals(typeParameters, typeArguments, 0, typeParameters.length)) {
mapper = identityMapper;
members = source.symbol ? source.symbol.members : createSymbolTable(source.declaredProperties);
callSignatures = source.declaredCallSignatures;
constructSignatures = source.declaredConstructSignatures;
stringIndexInfo = source.declaredStringIndexInfo;
numberIndexInfo = source.declaredNumberIndexInfo;
}
else {
mapper = createTypeMapper(typeParameters, typeArguments);
members = createInstantiatedSymbolTable(source.declaredProperties, mapper, /*mappingThisOnly*/ typeParameters.length === 1);
callSignatures = instantiateSignatures(source.declaredCallSignatures, mapper);
constructSignatures = instantiateSignatures(source.declaredConstructSignatures, mapper);
stringIndexInfo = instantiateIndexInfo(source.declaredStringIndexInfo, mapper);
numberIndexInfo = instantiateIndexInfo(source.declaredNumberIndexInfo, mapper);
}
const baseTypes = getBaseTypes(source);
if (baseTypes.length) {
if (source.symbol && members === source.symbol.members) {
members = createSymbolTable(source.declaredProperties);
}
const thisArgument = lastOrUndefined(typeArguments);
for (const baseType of baseTypes) {
const instantiatedBaseType = thisArgument ? getTypeWithThisArgument(instantiateType(baseType, mapper), thisArgument) : baseType;
addInheritedMembers(members, getPropertiesOfType(instantiatedBaseType));
callSignatures = concatenate(callSignatures, getSignaturesOfType(instantiatedBaseType, SignatureKind.Call));
constructSignatures = concatenate(constructSignatures, getSignaturesOfType(instantiatedBaseType, SignatureKind.Construct));
if (!stringIndexInfo) {
stringIndexInfo = instantiatedBaseType === anyType ?
createIndexInfo(anyType, /*isReadonly*/ false) :
getIndexInfoOfType(instantiatedBaseType, IndexKind.String);
}
numberIndexInfo = numberIndexInfo || getIndexInfoOfType(instantiatedBaseType, IndexKind.Number);
}
}
setStructuredTypeMembers(type, members, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo);
}
function resolveClassOrInterfaceMembers(type: InterfaceType): void {
resolveObjectTypeMembers(type, resolveDeclaredMembers(type), emptyArray, emptyArray);
}
function resolveTypeReferenceMembers(type: TypeReference): void {
const source = resolveDeclaredMembers(type.target);
const typeParameters = concatenate(source.typeParameters, [source.thisType]);
const typeArguments = type.typeArguments && type.typeArguments.length === typeParameters.length ?
type.typeArguments : concatenate(type.typeArguments, [type]);
resolveObjectTypeMembers(type, source, typeParameters, typeArguments);
}
function createSignature(declaration: SignatureDeclaration, typeParameters: TypeParameter[], thisParameter: Symbol | undefined, parameters: Symbol[],
resolvedReturnType: Type, typePredicate: TypePredicate, minArgumentCount: number, hasRestParameter: boolean, hasLiteralTypes: boolean): Signature {
const sig = new Signature(checker);
sig.declaration = declaration;
sig.typeParameters = typeParameters;
sig.parameters = parameters;
sig.thisParameter = thisParameter;
sig.resolvedReturnType = resolvedReturnType;
sig.typePredicate = typePredicate;
sig.minArgumentCount = minArgumentCount;
sig.hasRestParameter = hasRestParameter;
sig.hasLiteralTypes = hasLiteralTypes;
return sig;
}
function cloneSignature(sig: Signature): Signature {
return createSignature(sig.declaration, sig.typeParameters, sig.thisParameter, sig.parameters, sig.resolvedReturnType,
sig.typePredicate, sig.minArgumentCount, sig.hasRestParameter, sig.hasLiteralTypes);
}
function getDefaultConstructSignatures(classType: InterfaceType): Signature[] {
const baseConstructorType = getBaseConstructorTypeOfClass(classType);
const baseSignatures = getSignaturesOfType(baseConstructorType, SignatureKind.Construct);
if (baseSignatures.length === 0) {
return [createSignature(undefined, classType.localTypeParameters, undefined, emptyArray, classType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false)];
}
const baseTypeNode = getBaseTypeNodeOfClass(classType);
const isJavaScript = isInJavaScriptFile(baseTypeNode);
const typeArguments = typeArgumentsFromTypeReferenceNode(baseTypeNode);
const typeArgCount = length(typeArguments);
const result: Signature[] = [];
for (const baseSig of baseSignatures) {
const minTypeArgumentCount = getMinTypeArgumentCount(baseSig.typeParameters);
const typeParamCount = length(baseSig.typeParameters);
if ((isJavaScript || typeArgCount >= minTypeArgumentCount) && typeArgCount <= typeParamCount) {
const sig = typeParamCount ? createSignatureInstantiation(baseSig, fillMissingTypeArguments(typeArguments, baseSig.typeParameters, minTypeArgumentCount, baseTypeNode)) : cloneSignature(baseSig);
sig.typeParameters = classType.localTypeParameters;
sig.resolvedReturnType = classType;
result.push(sig);
}
}
return result;
}
function findMatchingSignature(signatureList: Signature[], signature: Signature, partialMatch: boolean, ignoreThisTypes: boolean, ignoreReturnTypes: boolean): Signature {
for (const s of signatureList) {
if (compareSignaturesIdentical(s, signature, partialMatch, ignoreThisTypes, ignoreReturnTypes, compareTypesIdentical)) {
return s;
}
}
}
function findMatchingSignatures(signatureLists: Signature[][], signature: Signature, listIndex: number): Signature[] {
if (signature.typeParameters) {
// We require an exact match for generic signatures, so we only return signatures from the first
// signature list and only if they have exact matches in the other signature lists.
if (listIndex > 0) {
return undefined;
}
for (let i = 1; i < signatureLists.length; i++) {
if (!findMatchingSignature(signatureLists[i], signature, /*partialMatch*/ false, /*ignoreThisTypes*/ false, /*ignoreReturnTypes*/ false)) {
return undefined;
}
}
return [signature];
}
let result: Signature[] = undefined;
for (let i = 0; i < signatureLists.length; i++) {
// Allow matching non-generic signatures to have excess parameters and different return types
const match = i === listIndex ? signature : findMatchingSignature(signatureLists[i], signature, /*partialMatch*/ true, /*ignoreThisTypes*/ true, /*ignoreReturnTypes*/ true);
if (!match) {
return undefined;
}
if (!contains(result, match)) {
(result || (result = [])).push(match);
}
}
return result;
}
// The signatures of a union type are those signatures that are present in each of the constituent types.
// Generic signatures must match exactly, but non-generic signatures are allowed to have extra optional
// parameters and may differ in return types. When signatures differ in return types, the resulting return
// type is the union of the constituent return types.
function getUnionSignatures(types: Type[], kind: SignatureKind): Signature[] {
const signatureLists = map(types, t => getSignaturesOfType(t, kind));
let result: Signature[] = undefined;
for (let i = 0; i < signatureLists.length; i++) {
for (const signature of signatureLists[i]) {
// Only process signatures with parameter lists that aren't already in the result list
if (!result || !findMatchingSignature(result, signature, /*partialMatch*/ false, /*ignoreThisTypes*/ true, /*ignoreReturnTypes*/ true)) {
const unionSignatures = findMatchingSignatures(signatureLists, signature, i);
if (unionSignatures) {
let s = signature;
// Union the result types when more than one signature matches
if (unionSignatures.length > 1) {
s = cloneSignature(signature);
if (forEach(unionSignatures, sig => sig.thisParameter)) {
const thisType = getUnionType(map(unionSignatures, sig => getTypeOfSymbol(sig.thisParameter) || anyType), /*subtypeReduction*/ true);
s.thisParameter = createSymbolWithType(signature.thisParameter, thisType);
}
// Clear resolved return type we possibly got from cloneSignature
s.resolvedReturnType = undefined;
s.unionSignatures = unionSignatures;
}
(result || (result = [])).push(s);
}
}
}
}
return result || emptyArray;
}
function getUnionIndexInfo(types: Type[], kind: IndexKind): IndexInfo {
const indexTypes: Type[] = [];
let isAnyReadonly = false;
for (const type of types) {
const indexInfo = getIndexInfoOfType(type, kind);
if (!indexInfo) {
return undefined;
}
indexTypes.push(indexInfo.type);
isAnyReadonly = isAnyReadonly || indexInfo.isReadonly;
}
return createIndexInfo(getUnionType(indexTypes, /*subtypeReduction*/ true), isAnyReadonly);
}
function resolveUnionTypeMembers(type: UnionType) {
// The members and properties collections are empty for union types. To get all properties of a union
// type use getPropertiesOfType (only the language service uses this).
const callSignatures = getUnionSignatures(type.types, SignatureKind.Call);
const constructSignatures = getUnionSignatures(type.types, SignatureKind.Construct);
const stringIndexInfo = getUnionIndexInfo(type.types, IndexKind.String);
const numberIndexInfo = getUnionIndexInfo(type.types, IndexKind.Number);
setStructuredTypeMembers(type, emptySymbols, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo);
}
function intersectTypes(type1: Type, type2: Type): Type {
return !type1 ? type2 : !type2 ? type1 : getIntersectionType([type1, type2]);
}
function intersectIndexInfos(info1: IndexInfo, info2: IndexInfo): IndexInfo {
return !info1 ? info2 : !info2 ? info1 : createIndexInfo(
getIntersectionType([info1.type, info2.type]), info1.isReadonly && info2.isReadonly);
}
function unionSpreadIndexInfos(info1: IndexInfo, info2: IndexInfo): IndexInfo {
return info1 && info2 && createIndexInfo(
getUnionType([info1.type, info2.type]), info1.isReadonly || info2.isReadonly);
}
function includeMixinType(type: Type, types: Type[], index: number): Type {
const mixedTypes: Type[] = [];
for (let i = 0; i < types.length; i++) {
if (i === index) {
mixedTypes.push(type);
}
else if (isMixinConstructorType(types[i])) {
mixedTypes.push(getReturnTypeOfSignature(getSignaturesOfType(types[i], SignatureKind.Construct)[0]));
}
}
return getIntersectionType(mixedTypes);
}
function resolveIntersectionTypeMembers(type: IntersectionType) {
// The members and properties collections are empty for intersection types. To get all properties of an
// intersection type use getPropertiesOfType (only the language service uses this).
let callSignatures: Signature[] = emptyArray;
let constructSignatures: Signature[] = emptyArray;
let stringIndexInfo: IndexInfo;
let numberIndexInfo: IndexInfo;
const types = type.types;
const mixinCount = countWhere(types, isMixinConstructorType);
for (let i = 0; i < types.length; i++) {
const t = type.types[i];
// When an intersection type contains mixin constructor types, the construct signatures from
// those types are discarded and their return types are mixed into the return types of all
// other construct signatures in the intersection type. For example, the intersection type
// '{ new(...args: any[]) => A } & { new(s: string) => B }' has a single construct signature
// 'new(s: string) => A & B'.
if (mixinCount === 0 || mixinCount === types.length && i === 0 || !isMixinConstructorType(t)) {
let signatures = getSignaturesOfType(t, SignatureKind.Construct);
if (signatures.length && mixinCount > 0) {
signatures = map(signatures, s => {
const clone = cloneSignature(s);
clone.resolvedReturnType = includeMixinType(getReturnTypeOfSignature(s), types, i);
return clone;
});
}
constructSignatures = concatenate(constructSignatures, signatures);
}
callSignatures = concatenate(callSignatures, getSignaturesOfType(t, SignatureKind.Call));
stringIndexInfo = intersectIndexInfos(stringIndexInfo, getIndexInfoOfType(t, IndexKind.String));
numberIndexInfo = intersectIndexInfos(numberIndexInfo, getIndexInfoOfType(t, IndexKind.Number));
}
setStructuredTypeMembers(type, emptySymbols, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo);
}
/**
* Converts an AnonymousType to a ResolvedType.
*/
function resolveAnonymousTypeMembers(type: AnonymousType) {
const symbol = type.symbol;
if (type.target) {
const members = createInstantiatedSymbolTable(getPropertiesOfObjectType(type.target), type.mapper, /*mappingThisOnly*/ false);
const callSignatures = instantiateSignatures(getSignaturesOfType(type.target, SignatureKind.Call), type.mapper);
const constructSignatures = instantiateSignatures(getSignaturesOfType(type.target, SignatureKind.Construct), type.mapper);
const stringIndexInfo = instantiateIndexInfo(getIndexInfoOfType(type.target, IndexKind.String), type.mapper);
const numberIndexInfo = instantiateIndexInfo(getIndexInfoOfType(type.target, IndexKind.Number), type.mapper);
setStructuredTypeMembers(type, members, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo);
}
else if (symbol.flags & SymbolFlags.TypeLiteral) {
const members = symbol.members;
const callSignatures = getSignaturesOfSymbol(members.get("__call"));
const constructSignatures = getSignaturesOfSymbol(members.get("__new"));
const stringIndexInfo = getIndexInfoOfSymbol(symbol, IndexKind.String);
const numberIndexInfo = getIndexInfoOfSymbol(symbol, IndexKind.Number);
setStructuredTypeMembers(type, members, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo);
}
else {
// Combinations of function, class, enum and module
let members = emptySymbols;
let constructSignatures: Signature[] = emptyArray;
let stringIndexInfo: IndexInfo = undefined;
if (symbol.exports) {
members = getExportsOfSymbol(symbol);
}
if (symbol.flags & SymbolFlags.Class) {
const classType = getDeclaredTypeOfClassOrInterface(symbol);
constructSignatures = getSignaturesOfSymbol(symbol.members.get("__constructor"));
if (!constructSignatures.length) {
constructSignatures = getDefaultConstructSignatures(classType);
}
const baseConstructorType = getBaseConstructorTypeOfClass(classType);
if (baseConstructorType.flags & (TypeFlags.Object | TypeFlags.Intersection | TypeFlags.TypeVariable)) {
members = createSymbolTable(getNamedMembers(members));
addInheritedMembers(members, getPropertiesOfType(baseConstructorType));
}
else if (baseConstructorType === anyType) {
stringIndexInfo = createIndexInfo(anyType, /*isReadonly*/ false);
}
}
const numberIndexInfo = symbol.flags & SymbolFlags.Enum ? enumNumberIndexInfo : undefined;
setStructuredTypeMembers(type, members, emptyArray, constructSignatures, stringIndexInfo, numberIndexInfo);
// We resolve the members before computing the signatures because a signature may use
// typeof with a qualified name expression that circularly references the type we are
// in the process of resolving (see issue #6072). The temporarily empty signature list
// will never be observed because a qualified name can't reference signatures.
if (symbol.flags & (SymbolFlags.Function | SymbolFlags.Method)) {
(<ResolvedType>type).callSignatures = getSignaturesOfSymbol(symbol);
}
}
}
/** Resolve the members of a mapped type { [P in K]: T } */
function resolveMappedTypeMembers(type: MappedType) {
const members: SymbolTable = createMap<Symbol>();
let stringIndexInfo: IndexInfo;
// Resolve upfront such that recursive references see an empty object type.
setStructuredTypeMembers(type, emptySymbols, emptyArray, emptyArray, undefined, undefined);
// In { [P in K]: T }, we refer to P as the type parameter type, K as the constraint type,
// and T as the template type.
const typeParameter = getTypeParameterFromMappedType(type);
const constraintType = getConstraintTypeFromMappedType(type);
const templateType = getTemplateTypeFromMappedType(type);
const modifiersType = getApparentType(getModifiersTypeFromMappedType(type)); // The 'T' in 'keyof T'
const templateReadonly = !!type.declaration.readonlyToken;
const templateOptional = !!type.declaration.questionToken;
if (type.declaration.typeParameter.constraint.kind === SyntaxKind.TypeOperator) {
// We have a { [P in keyof T]: X }
for (const propertySymbol of getPropertiesOfType(modifiersType)) {
addMemberForKeyType(getLiteralTypeFromPropertyName(propertySymbol), propertySymbol);
}
if (getIndexInfoOfType(modifiersType, IndexKind.String)) {
addMemberForKeyType(stringType);
}
}
else {
// First, if the constraint type is a type parameter, obtain the base constraint. Then,
// if the key type is a 'keyof X', obtain 'keyof C' where C is the base constraint of X.
// Finally, iterate over the constituents of the resulting iteration type.
const keyType = constraintType.flags & TypeFlags.TypeVariable ? getApparentType(constraintType) : constraintType;
const iterationType = keyType.flags & TypeFlags.Index ? getIndexType(getApparentType((<IndexType>keyType).type)) : keyType;
forEachType(iterationType, addMemberForKeyType);
}
setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, undefined);
function addMemberForKeyType(t: Type, propertySymbol?: Symbol) {
// Create a mapper from T to the current iteration type constituent. Then, if the
// mapped type is itself an instantiated type, combine the iteration mapper with the
// instantiation mapper.
const iterationMapper = createTypeMapper([typeParameter], [t]);
const templateMapper = type.mapper ? combineTypeMappers(type.mapper, iterationMapper) : iterationMapper;
const propType = instantiateType(templateType, templateMapper);
// If the current iteration type constituent is a string literal type, create a property.
// Otherwise, for type string create a string index signature.
if (t.flags & TypeFlags.StringLiteral) {
const propName = (<StringLiteralType>t).value;
const modifiersProp = getPropertyOfType(modifiersType, propName);
const isOptional = templateOptional || !!(modifiersProp && modifiersProp.flags & SymbolFlags.Optional);
const prop = createSymbol(SymbolFlags.Property | (isOptional ? SymbolFlags.Optional : 0), propName);
prop.checkFlags = templateReadonly || modifiersProp && isReadonlySymbol(modifiersProp) ? CheckFlags.Readonly : 0;
prop.type = propType;
if (propertySymbol) {
prop.syntheticOrigin = propertySymbol;
}
members.set(propName, prop);
}
else if (t.flags & TypeFlags.String) {
stringIndexInfo = createIndexInfo(propType, templateReadonly);
}
}
}
function getTypeParameterFromMappedType(type: MappedType) {
return type.typeParameter ||
(type.typeParameter = getDeclaredTypeOfTypeParameter(getSymbolOfNode(type.declaration.typeParameter)));
}
function getConstraintTypeFromMappedType(type: MappedType) {
return type.constraintType ||
(type.constraintType = instantiateType(getConstraintOfTypeParameter(getTypeParameterFromMappedType(type)), type.mapper || identityMapper) || unknownType);
}
function getTemplateTypeFromMappedType(type: MappedType) {
return type.templateType ||
(type.templateType = type.declaration.type ?
instantiateType(addOptionality(getTypeFromTypeNode(type.declaration.type), !!type.declaration.questionToken), type.mapper || identityMapper) :
unknownType);
}
function getModifiersTypeFromMappedType(type: MappedType) {
if (!type.modifiersType) {
const constraintDeclaration = type.declaration.typeParameter.constraint;
if (constraintDeclaration.kind === SyntaxKind.TypeOperator) {
// If the constraint declaration is a 'keyof T' node, the modifiers type is T. We check
// AST nodes here because, when T is a non-generic type, the logic below eagerly resolves
// 'keyof T' to a literal union type and we can't recover T from that type.
type.modifiersType = instantiateType(getTypeFromTypeNode((<TypeOperatorNode>constraintDeclaration).type), type.mapper || identityMapper);
}
else {
// Otherwise, get the declared constraint type, and if the constraint type is a type parameter,
// get the constraint of that type parameter. If the resulting type is an indexed type 'keyof T',
// the modifiers type is T. Otherwise, the modifiers type is {}.
const declaredType = <MappedType>getTypeFromMappedTypeNode(type.declaration);
const constraint = getConstraintTypeFromMappedType(declaredType);
const extendedConstraint = constraint && constraint.flags & TypeFlags.TypeParameter ? getConstraintOfTypeParameter(<TypeParameter>constraint) : constraint;
type.modifiersType = extendedConstraint && extendedConstraint.flags & TypeFlags.Index ? instantiateType((<IndexType>extendedConstraint).type, type.mapper || identityMapper) : emptyObjectType;
}
}
return type.modifiersType;
}
function isGenericMappedType(type: Type) {
if (getObjectFlags(type) & ObjectFlags.Mapped) {
const constraintType = getConstraintTypeFromMappedType(<MappedType>type);
return maybeTypeOfKind(constraintType, TypeFlags.TypeVariable | TypeFlags.Index);
}
return false;
}
function resolveStructuredTypeMembers(type: StructuredType): ResolvedType {
if (!(<ResolvedType>type).members) {
if (type.flags & TypeFlags.Object) {
if ((<ObjectType>type).objectFlags & ObjectFlags.Reference) {
resolveTypeReferenceMembers(<TypeReference>type);
}
else if ((<ObjectType>type).objectFlags & ObjectFlags.ClassOrInterface) {
resolveClassOrInterfaceMembers(<InterfaceType>type);
}
else if ((<ObjectType>type).objectFlags & ObjectFlags.Anonymous) {
resolveAnonymousTypeMembers(<AnonymousType>type);
}
else if ((<MappedType>type).objectFlags & ObjectFlags.Mapped) {
resolveMappedTypeMembers(<MappedType>type);
}
}
else if (type.flags & TypeFlags.Union) {
resolveUnionTypeMembers(<UnionType>type);
}
else if (type.flags & TypeFlags.Intersection) {
resolveIntersectionTypeMembers(<IntersectionType>type);
}
}
return <ResolvedType>type;
}
/** Return properties of an object type or an empty array for other types */
function getPropertiesOfObjectType(type: Type): Symbol[] {
if (type.flags & TypeFlags.Object) {
return resolveStructuredTypeMembers(<ObjectType>type).properties;
}
return emptyArray;
}
/** If the given type is an object type and that type has a property by the given name,
* return the symbol for that property. Otherwise return undefined.
*/
function getPropertyOfObjectType(type: Type, name: string): Symbol {
if (type.flags & TypeFlags.Object) {
const resolved = resolveStructuredTypeMembers(<ObjectType>type);
const symbol = resolved.members.get(name);
if (symbol && symbolIsValue(symbol)) {
return symbol;
}
}
}
function getPropertiesOfUnionOrIntersectionType(type: UnionOrIntersectionType): Symbol[] {
if (!type.resolvedProperties) {
const members = createMap<Symbol>();
for (const current of type.types) {
for (const prop of getPropertiesOfType(current)) {
if (!members.has(prop.name)) {
const combinedProp = getPropertyOfUnionOrIntersectionType(type, prop.name);
if (combinedProp) {
members.set(prop.name, combinedProp);
}
}
}
// The properties of a union type are those that are present in all constituent types, so
// we only need to check the properties of the first type
if (type.flags & TypeFlags.Union) {
break;
}
}
type.resolvedProperties = getNamedMembers(members);
}
return type.resolvedProperties;
}
function getPropertiesOfType(type: Type): Symbol[] {
type = getApparentType(type);
return type.flags & TypeFlags.UnionOrIntersection ?
getPropertiesOfUnionOrIntersectionType(<UnionType>type) :
getPropertiesOfObjectType(type);
}
function getAllPossiblePropertiesOfType(type: Type): Symbol[] {
if (type.flags & TypeFlags.Union) {
const props = createMap<Symbol>();
for (const memberType of (type as UnionType).types) {
if (memberType.flags & TypeFlags.Primitive) {
continue;
}
for (const { name } of getPropertiesOfType(memberType)) {
if (!props.has(name)) {
props.set(name, createUnionOrIntersectionProperty(type as UnionType, name));
}
}
}
return arrayFrom(props.values());
} else {
return getPropertiesOfType(type);
}
}
function getConstraintOfType(type: TypeVariable | UnionOrIntersectionType): Type {
return type.flags & TypeFlags.TypeParameter ? getConstraintOfTypeParameter(<TypeParameter>type) :
type.flags & TypeFlags.IndexedAccess ? getConstraintOfIndexedAccess(<IndexedAccessType>type) :
getBaseConstraintOfType(type);
}
function getConstraintOfTypeParameter(typeParameter: TypeParameter): Type {
return hasNonCircularBaseConstraint(typeParameter) ? getConstraintFromTypeParameter(typeParameter) : undefined;
}
function getConstraintOfIndexedAccess(type: IndexedAccessType) {
const baseObjectType = getBaseConstraintOfType(type.objectType);
const baseIndexType = getBaseConstraintOfType(type.indexType);
return baseObjectType || baseIndexType ? getIndexedAccessType(baseObjectType || type.objectType, baseIndexType || type.indexType) : undefined;
}
function getBaseConstraintOfType(type: Type): Type {
if (type.flags & (TypeFlags.TypeVariable | TypeFlags.UnionOrIntersection)) {
const constraint = getResolvedBaseConstraint(<TypeVariable | UnionOrIntersectionType>type);
if (constraint !== noConstraintType && constraint !== circularConstraintType) {
return constraint;
}
}
else if (type.flags & TypeFlags.Index) {
return stringType;
}
return undefined;
}
function hasNonCircularBaseConstraint(type: TypeVariable): boolean {
return getResolvedBaseConstraint(type) !== circularConstraintType;
}
/**
* Return the resolved base constraint of a type variable. The noConstraintType singleton is returned if the
* type variable has no constraint, and the circularConstraintType singleton is returned if the constraint
* circularly references the type variable.
*/
function getResolvedBaseConstraint(type: TypeVariable | UnionOrIntersectionType): Type {
let typeStack: Type[];
let circular: boolean;
if (!type.resolvedBaseConstraint) {
typeStack = [];
const constraint = getBaseConstraint(type);
type.resolvedBaseConstraint = circular ? circularConstraintType : getTypeWithThisArgument(constraint || noConstraintType, type);
}
return type.resolvedBaseConstraint;
function getBaseConstraint(t: Type): Type {
if (contains(typeStack, t)) {
circular = true;
return undefined;
}
typeStack.push(t);
const result = computeBaseConstraint(t);
typeStack.pop();
return result;
}
function computeBaseConstraint(t: Type): Type {
if (t.flags & TypeFlags.TypeParameter) {
const constraint = getConstraintFromTypeParameter(<TypeParameter>t);
return (<TypeParameter>t).isThisType ? constraint :
constraint ? getBaseConstraint(constraint) : undefined;
}
if (t.flags & TypeFlags.UnionOrIntersection) {
const types = (<UnionOrIntersectionType>t).types;
const baseTypes: Type[] = [];
for (const type of types) {
const baseType = getBaseConstraint(type);
if (baseType) {
baseTypes.push(baseType);
}
}
return t.flags & TypeFlags.Union && baseTypes.length === types.length ? getUnionType(baseTypes) :
t.flags & TypeFlags.Intersection && baseTypes.length ? getIntersectionType(baseTypes) :
undefined;
}
if (t.flags & TypeFlags.Index) {
return stringType;
}
if (t.flags & TypeFlags.IndexedAccess) {
const baseObjectType = getBaseConstraint((<IndexedAccessType>t).objectType);
const baseIndexType = getBaseConstraint((<IndexedAccessType>t).indexType);
const baseIndexedAccess = baseObjectType && baseIndexType ? getIndexedAccessType(baseObjectType, baseIndexType) : undefined;
return baseIndexedAccess && baseIndexedAccess !== unknownType ? getBaseConstraint(baseIndexedAccess) : undefined;
}
return t;
}
}
function getApparentTypeOfIntersectionType(type: IntersectionType) {
return type.resolvedApparentType || (type.resolvedApparentType = getTypeWithThisArgument(type, type));
}
/**
* Gets the default type for a type parameter.
*
* If the type parameter is the result of an instantiation, this gets the instantiated
* default type of its target. If the type parameter has no default type, `undefined`
* is returned.
*
* This function *does not* perform a circularity check.
*/
function getDefaultFromTypeParameter(typeParameter: TypeParameter): Type | undefined {
if (!typeParameter.default) {
if (typeParameter.target) {
const targetDefault = getDefaultFromTypeParameter(typeParameter.target);
typeParameter.default = targetDefault ? instantiateType(targetDefault, typeParameter.mapper) : noConstraintType;
}
else {
const defaultDeclaration = typeParameter.symbol && forEach(typeParameter.symbol.declarations, decl => isTypeParameter(decl) && decl.default);
typeParameter.default = defaultDeclaration ? getTypeFromTypeNode(defaultDeclaration) : noConstraintType;
}
}
return typeParameter.default === noConstraintType ? undefined : typeParameter.default;
}
/**
* For a type parameter, return the base constraint of the type parameter. For the string, number,
* boolean, and symbol primitive types, return the corresponding object types. Otherwise return the
* type itself. Note that the apparent type of a union type is the union type itself.
*/
function getApparentType(type: Type): Type {
const t = type.flags & TypeFlags.TypeVariable ? getBaseConstraintOfType(type) || emptyObjectType : type;
return t.flags & TypeFlags.Intersection ? getApparentTypeOfIntersectionType(<IntersectionType>t) :
t.flags & TypeFlags.StringLike ? globalStringType :
t.flags & TypeFlags.NumberLike ? globalNumberType :
t.flags & TypeFlags.BooleanLike ? globalBooleanType :
t.flags & TypeFlags.ESSymbol ? getGlobalESSymbolType(/*reportErrors*/ languageVersion >= ScriptTarget.ES2015) :
t.flags & TypeFlags.NonPrimitive ? emptyObjectType :
t;
}
function createUnionOrIntersectionProperty(containingType: UnionOrIntersectionType, name: string): Symbol {
let props: Symbol[];
const types = containingType.types;
const isUnion = containingType.flags & TypeFlags.Union;
const excludeModifiers = isUnion ? ModifierFlags.NonPublicAccessibilityModifier : 0;
// Flags we want to propagate to the result if they exist in all source symbols
let commonFlags = isUnion ? SymbolFlags.None : SymbolFlags.Optional;
let syntheticFlag = CheckFlags.SyntheticMethod;
let checkFlags = 0;
for (const current of types) {
const type = getApparentType(current);
if (type !== unknownType) {
const prop = getPropertyOfType(type, name);
const modifiers = prop ? getDeclarationModifierFlagsFromSymbol(prop) : 0;
if (prop && !(modifiers & excludeModifiers)) {
commonFlags &= prop.flags;
if (!props) {
props = [prop];
}
else if (!contains(props, prop)) {
props.push(prop);
}
checkFlags |= (isReadonlySymbol(prop) ? CheckFlags.Readonly : 0) |
(!(modifiers & ModifierFlags.NonPublicAccessibilityModifier) ? CheckFlags.ContainsPublic : 0) |
(modifiers & ModifierFlags.Protected ? CheckFlags.ContainsProtected : 0) |
(modifiers & ModifierFlags.Private ? CheckFlags.ContainsPrivate : 0) |
(modifiers & ModifierFlags.Static ? CheckFlags.ContainsStatic : 0);
if (!isMethodLike(prop)) {
syntheticFlag = CheckFlags.SyntheticProperty;
}
}
else if (isUnion) {
checkFlags |= CheckFlags.Partial;
}
}
}
if (!props) {
return undefined;
}
if (props.length === 1 && !(checkFlags & CheckFlags.Partial)) {
return props[0];
}
const propTypes: Type[] = [];
const declarations: Declaration[] = [];
let commonType: Type = undefined;
for (const prop of props) {
if (prop.declarations) {
addRange(declarations, prop.declarations);
}
const type = getTypeOfSymbol(prop);
if (!commonType) {
commonType = type;
}
else if (type !== commonType) {
checkFlags |= CheckFlags.HasNonUniformType;
}
propTypes.push(type);
}
const result = createSymbol(SymbolFlags.Property | commonFlags, name);
result.checkFlags = syntheticFlag | checkFlags;
result.containingType = containingType;
result.declarations = declarations;
result.type = isUnion ? getUnionType(propTypes) : getIntersectionType(propTypes);
return result;
}
// Return the symbol for a given property in a union or intersection type, or undefined if the property
// does not exist in any constituent type. Note that the returned property may only be present in some
// constituents, in which case the isPartial flag is set when the containing type is union type. We need
// these partial properties when identifying discriminant properties, but otherwise they are filtered out
// and do not appear to be present in the union type.
function getUnionOrIntersectionProperty(type: UnionOrIntersectionType, name: string): Symbol {
const properties = type.propertyCache || (type.propertyCache = createMap<Symbol>());
let property = properties.get(name);
if (!property) {
property = createUnionOrIntersectionProperty(type, name);
if (property) {
properties.set(name, property);
}
}
return property;
}
function getPropertyOfUnionOrIntersectionType(type: UnionOrIntersectionType, name: string): Symbol {
const property = getUnionOrIntersectionProperty(type, name);
// We need to filter out partial properties in union types
return property && !(getCheckFlags(property) & CheckFlags.Partial) ? property : undefined;
}
/**
* Return the symbol for the property with the given name in the given type. Creates synthetic union properties when
* necessary, maps primitive types and type parameters are to their apparent types, and augments with properties from
* Object and Function as appropriate.
*
* @param type a type to look up property from
* @param name a name of property to look up in a given type
*/
function getPropertyOfType(type: Type, name: string): Symbol | undefined {
type = getApparentType(type);
if (type.flags & TypeFlags.Object) {
const resolved = resolveStructuredTypeMembers(<ObjectType>type);
const symbol = resolved.members.get(name);
if (symbol && symbolIsValue(symbol)) {
return symbol;
}
if (resolved === anyFunctionType || resolved.callSignatures.length || resolved.constructSignatures.length) {
const symbol = getPropertyOfObjectType(globalFunctionType, name);
if (symbol) {
return symbol;
}
}
return getPropertyOfObjectType(globalObjectType, name);
}
if (type.flags & TypeFlags.UnionOrIntersection) {
return getPropertyOfUnionOrIntersectionType(<UnionOrIntersectionType>type, name);
}
return undefined;
}
function getSignaturesOfStructuredType(type: Type, kind: SignatureKind): Signature[] {
if (type.flags & TypeFlags.StructuredType) {
const resolved = resolveStructuredTypeMembers(<ObjectType>type);
return kind === SignatureKind.Call ? resolved.callSignatures : resolved.constructSignatures;
}
return emptyArray;
}
/**
* Return the signatures of the given kind in the given type. Creates synthetic union signatures when necessary and
* maps primitive types and type parameters are to their apparent types.
*/
function getSignaturesOfType(type: Type, kind: SignatureKind): Signature[] {
return getSignaturesOfStructuredType(getApparentType(type), kind);
}
function getIndexInfoOfStructuredType(type: Type, kind: IndexKind): IndexInfo | undefined {
if (type.flags & TypeFlags.StructuredType) {
const resolved = resolveStructuredTypeMembers(<ObjectType>type);
return kind === IndexKind.String ? resolved.stringIndexInfo : resolved.numberIndexInfo;
}
}
function getIndexTypeOfStructuredType(type: Type, kind: IndexKind): Type | undefined {
const info = getIndexInfoOfStructuredType(type, kind);
return info && info.type;
}
// Return the indexing info of the given kind in the given type. Creates synthetic union index types when necessary and
// maps primitive types and type parameters are to their apparent types.
function getIndexInfoOfType(type: Type, kind: IndexKind): IndexInfo {
return getIndexInfoOfStructuredType(getApparentType(type), kind);
}
// Return the index type of the given kind in the given type. Creates synthetic union index types when necessary and
// maps primitive types and type parameters are to their apparent types.
function getIndexTypeOfType(type: Type, kind: IndexKind): Type {
return getIndexTypeOfStructuredType(getApparentType(type), kind);
}
function getImplicitIndexTypeOfType(type: Type, kind: IndexKind): Type {
if (isObjectLiteralType(type)) {
const propTypes: Type[] = [];
for (const prop of getPropertiesOfType(type)) {
if (kind === IndexKind.String || isNumericLiteralName(prop.name)) {
propTypes.push(getTypeOfSymbol(prop));
}
}
if (propTypes.length) {
return getUnionType(propTypes, /*subtypeReduction*/ true);
}
}
return undefined;
}
function getTypeParametersFromJSDocTemplate(declaration: SignatureDeclaration): TypeParameter[] {
if (declaration.flags & NodeFlags.JavaScriptFile) {
const templateTag = getJSDocTemplateTag(declaration);
if (templateTag) {
return getTypeParametersFromDeclaration(templateTag.typeParameters);
}
}
return undefined;
}
// Return list of type parameters with duplicates removed (duplicate identifier errors are generated in the actual
// type checking functions).
function getTypeParametersFromDeclaration(typeParameterDeclarations: TypeParameterDeclaration[]): TypeParameter[] {
const result: TypeParameter[] = [];
forEach(typeParameterDeclarations, node => {
const tp = getDeclaredTypeOfTypeParameter(node.symbol);
if (!contains(result, tp)) {
result.push(tp);
}
});
return result;
}
function symbolsToArray(symbols: SymbolTable): Symbol[] {
const result: Symbol[] = [];
symbols.forEach((symbol, id) => {
if (!isReservedMemberName(id)) {
result.push(symbol);
}
});
return result;
}
function isJSDocOptionalParameter(node: ParameterDeclaration) {
if (node.flags & NodeFlags.JavaScriptFile) {
if (node.type && node.type.kind === SyntaxKind.JSDocOptionalType) {
return true;
}
const paramTags = getJSDocParameterTags(node);
if (paramTags) {
for (const paramTag of paramTags) {
if (paramTag.isBracketed) {
return true;
}
if (paramTag.typeExpression) {
return paramTag.typeExpression.type.kind === SyntaxKind.JSDocOptionalType;
}
}
}
}
}
function tryFindAmbientModule(moduleName: string, withAugmentations: boolean) {
if (isExternalModuleNameRelative(moduleName)) {
return undefined;
}
const symbol = getSymbol(globals, `"${moduleName}"`, SymbolFlags.ValueModule);
// merged symbol is module declaration symbol combined with all augmentations
return symbol && withAugmentations ? getMergedSymbol(symbol) : symbol;
}
function isOptionalParameter(node: ParameterDeclaration) {
if (hasQuestionToken(node) || isJSDocOptionalParameter(node)) {
return true;
}
if (node.initializer) {
const signatureDeclaration = <SignatureDeclaration>node.parent;
const signature = getSignatureFromDeclaration(signatureDeclaration);
const parameterIndex = ts.indexOf(signatureDeclaration.parameters, node);
Debug.assert(parameterIndex >= 0);
return parameterIndex >= signature.minArgumentCount;
}
const iife = getImmediatelyInvokedFunctionExpression(node.parent);
if (iife) {
return !node.type &&
!node.dotDotDotToken &&
indexOf((node.parent as SignatureDeclaration).parameters, node) >= iife.arguments.length;
}
return false;
}
function createTypePredicateFromTypePredicateNode(node: TypePredicateNode): IdentifierTypePredicate | ThisTypePredicate {
if (node.parameterName.kind === SyntaxKind.Identifier) {
const parameterName = node.parameterName as Identifier;
return {
kind: TypePredicateKind.Identifier,
parameterName: parameterName ? parameterName.text : undefined,
parameterIndex: parameterName ? getTypePredicateParameterIndex((node.parent as SignatureDeclaration).parameters, parameterName) : undefined,
type: getTypeFromTypeNode(node.type)
} as IdentifierTypePredicate;
}
else {
return {
kind: TypePredicateKind.This,
type: getTypeFromTypeNode(node.type)
} as ThisTypePredicate;
}
}
/**
* Gets the minimum number of type arguments needed to satisfy all non-optional type
* parameters.
*/
function getMinTypeArgumentCount(typeParameters: TypeParameter[] | undefined): number {
let minTypeArgumentCount = 0;
if (typeParameters) {
for (let i = 0; i < typeParameters.length; i++) {
if (!getDefaultFromTypeParameter(typeParameters[i])) {
minTypeArgumentCount = i + 1;
}
}
}
return minTypeArgumentCount;
}
/**
* Fill in default types for unsupplied type arguments. If `typeArguments` is undefined
* when a default type is supplied, a new array will be created and returned.
*
* @param typeArguments The supplied type arguments.
* @param typeParameters The requested type parameters.
* @param minTypeArgumentCount The minimum number of required type arguments.
*/
function fillMissingTypeArguments(typeArguments: Type[] | undefined, typeParameters: TypeParameter[] | undefined, minTypeArgumentCount: number, location?: Node) {
const numTypeParameters = length(typeParameters);
if (numTypeParameters) {
const numTypeArguments = length(typeArguments);
const isJavaScript = isInJavaScriptFile(location);
if ((isJavaScript || numTypeArguments >= minTypeArgumentCount) && numTypeArguments <= numTypeParameters) {
if (!typeArguments) {
typeArguments = [];
}
// Map an unsatisfied type parameter with a default type.
// If a type parameter does not have a default type, or if the default type
// is a forward reference, the empty object type is used.
for (let i = numTypeArguments; i < numTypeParameters; i++) {
typeArguments[i] = isJavaScript ? anyType : emptyObjectType;
}
for (let i = numTypeArguments; i < numTypeParameters; i++) {
const mapper = createTypeMapper(typeParameters, typeArguments);
const defaultType = getDefaultFromTypeParameter(typeParameters[i]);
typeArguments[i] = defaultType ? instantiateType(defaultType, mapper) : isJavaScript ? anyType : emptyObjectType;
}
}
}
return typeArguments;
}
function getSignatureFromDeclaration(declaration: SignatureDeclaration): Signature {
const links = getNodeLinks(declaration);
if (!links.resolvedSignature) {
const parameters: Symbol[] = [];
let hasLiteralTypes = false;
let minArgumentCount = 0;
let thisParameter: Symbol = undefined;
let hasThisParameter: boolean;
const iife = getImmediatelyInvokedFunctionExpression(declaration);
const isJSConstructSignature = isJSDocConstructSignature(declaration);
const isUntypedSignatureInJSFile = !iife && !isJSConstructSignature && isInJavaScriptFile(declaration) && !hasJSDocParameterTags(declaration);
// If this is a JSDoc construct signature, then skip the first parameter in the
// parameter list. The first parameter represents the return type of the construct
// signature.
for (let i = isJSConstructSignature ? 1 : 0; i < declaration.parameters.length; i++) {
const param = declaration.parameters[i];
let paramSymbol = param.symbol;
// Include parameter symbol instead of property symbol in the signature
if (paramSymbol && !!(paramSymbol.flags & SymbolFlags.Property) && !isBindingPattern(param.name)) {
const resolvedSymbol = resolveName(param, paramSymbol.name, SymbolFlags.Value, undefined, undefined);
paramSymbol = resolvedSymbol;
}
if (i === 0 && paramSymbol.name === "this") {
hasThisParameter = true;
thisParameter = param.symbol;
}
else {
parameters.push(paramSymbol);
}
if (param.type && param.type.kind === SyntaxKind.LiteralType) {
hasLiteralTypes = true;
}
// Record a new minimum argument count if this is not an optional parameter
const isOptionalParameter = param.initializer || param.questionToken || param.dotDotDotToken ||
iife && parameters.length > iife.arguments.length && !param.type ||
isJSDocOptionalParameter(param) ||
isUntypedSignatureInJSFile;
if (!isOptionalParameter) {
minArgumentCount = parameters.length;
}
}
// If only one accessor includes a this-type annotation, the other behaves as if it had the same type annotation
if ((declaration.kind === SyntaxKind.GetAccessor || declaration.kind === SyntaxKind.SetAccessor) &&
!hasDynamicName(declaration) &&
(!hasThisParameter || !thisParameter)) {
const otherKind = declaration.kind === SyntaxKind.GetAccessor ? SyntaxKind.SetAccessor : SyntaxKind.GetAccessor;
const other = getDeclarationOfKind<AccessorDeclaration>(declaration.symbol, otherKind);
if (other) {
thisParameter = getAnnotatedAccessorThisParameter(other);
}
}
const classType = declaration.kind === SyntaxKind.Constructor ?
getDeclaredTypeOfClassOrInterface(getMergedSymbol((<ClassDeclaration>declaration.parent).symbol))
: undefined;
const typeParameters = classType ? classType.localTypeParameters :
declaration.typeParameters ? getTypeParametersFromDeclaration(declaration.typeParameters) :
getTypeParametersFromJSDocTemplate(declaration);
const returnType = getSignatureReturnTypeFromDeclaration(declaration, isJSConstructSignature, classType);
const typePredicate = declaration.type && declaration.type.kind === SyntaxKind.TypePredicate ?
createTypePredicateFromTypePredicateNode(declaration.type as TypePredicateNode) :
undefined;
links.resolvedSignature = createSignature(declaration, typeParameters, thisParameter, parameters, returnType, typePredicate, minArgumentCount, hasRestParameter(declaration), hasLiteralTypes);
}
return links.resolvedSignature;
}
function getSignatureReturnTypeFromDeclaration(declaration: SignatureDeclaration, isJSConstructSignature: boolean, classType: Type) {
if (isJSConstructSignature) {
return getTypeFromTypeNode(declaration.parameters[0].type);
}
else if (classType) {
return classType;
}
else if (declaration.type) {
return getTypeFromTypeNode(declaration.type);
}
if (declaration.flags & NodeFlags.JavaScriptFile) {
const type = getReturnTypeFromJSDocComment(declaration);
if (type && type !== unknownType) {
return type;
}
}
// TypeScript 1.0 spec (April 2014):
// If only one accessor includes a type annotation, the other behaves as if it had the same type annotation.
if (declaration.kind === SyntaxKind.GetAccessor && !hasDynamicName(declaration)) {
const setter = getDeclarationOfKind<AccessorDeclaration>(declaration.symbol, SyntaxKind.SetAccessor);
return getAnnotatedAccessorType(setter);
}
if (nodeIsMissing((<FunctionLikeDeclaration>declaration).body)) {
return anyType;
}
}
function containsArgumentsReference(declaration: FunctionLikeDeclaration): boolean {
const links = getNodeLinks(declaration);
if (links.containsArgumentsReference === undefined) {
if (links.flags & NodeCheckFlags.CaptureArguments) {
links.containsArgumentsReference = true;
}
else {
links.containsArgumentsReference = traverse(declaration.body);
}
}
return links.containsArgumentsReference;
function traverse(node: Node): boolean {
if (!node) return false;
switch (node.kind) {
case SyntaxKind.Identifier:
return (<Identifier>node).text === "arguments" && isPartOfExpression(node);
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
return (<NamedDeclaration>node).name.kind === SyntaxKind.ComputedPropertyName
&& traverse((<NamedDeclaration>node).name);
default:
return !nodeStartsNewLexicalEnvironment(node) && !isPartOfTypeNode(node) && forEachChild(node, traverse);
}
}
}
function getSignaturesOfSymbol(symbol: Symbol): Signature[] {
if (!symbol) return emptyArray;
const result: Signature[] = [];
for (let i = 0; i < symbol.declarations.length; i++) {
const node = symbol.declarations[i];
switch (node.kind) {
case SyntaxKind.FunctionType:
case SyntaxKind.ConstructorType:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
case SyntaxKind.Constructor:
case SyntaxKind.CallSignature:
case SyntaxKind.ConstructSignature:
case SyntaxKind.IndexSignature:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
case SyntaxKind.JSDocFunctionType:
// Don't include signature if node is the implementation of an overloaded function. A node is considered
// an implementation node if it has a body and the previous node is of the same kind and immediately
// precedes the implementation node (i.e. has the same parent and ends where the implementation starts).
if (i > 0 && (<FunctionLikeDeclaration>node).body) {
const previous = symbol.declarations[i - 1];
if (node.parent === previous.parent && node.kind === previous.kind && node.pos === previous.end) {
break;
}
}
result.push(getSignatureFromDeclaration(<SignatureDeclaration>node));
}
}
return result;
}
function resolveExternalModuleTypeByLiteral(name: StringLiteral) {
const moduleSym = resolveExternalModuleName(name, name);
if (moduleSym) {
const resolvedModuleSymbol = resolveExternalModuleSymbol(moduleSym);
if (resolvedModuleSymbol) {
return getTypeOfSymbol(resolvedModuleSymbol);
}
}
return anyType;
}
function getThisTypeOfSignature(signature: Signature): Type | undefined {
if (signature.thisParameter) {
return getTypeOfSymbol(signature.thisParameter);
}
}
function getReturnTypeOfSignature(signature: Signature): Type {
if (!signature.resolvedReturnType) {
if (!pushTypeResolution(signature, TypeSystemPropertyName.ResolvedReturnType)) {
return unknownType;
}
let type: Type;
if (signature.target) {
type = instantiateType(getReturnTypeOfSignature(signature.target), signature.mapper);
}
else if (signature.unionSignatures) {
type = getUnionType(map(signature.unionSignatures, getReturnTypeOfSignature), /*subtypeReduction*/ true);
}
else {
type = getReturnTypeFromBody(<FunctionLikeDeclaration>signature.declaration);
}
if (!popTypeResolution()) {
type = anyType;
if (noImplicitAny) {
const declaration = <Declaration>signature.declaration;
const name = getNameOfDeclaration(declaration);
if (name) {
error(name, Diagnostics._0_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions, declarationNameToString(name));
}
else {
error(declaration, Diagnostics.Function_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions);
}
}
}
signature.resolvedReturnType = type;
}
return signature.resolvedReturnType;
}
function getRestTypeOfSignature(signature: Signature): Type {
if (signature.hasRestParameter) {
const type = getTypeOfSymbol(lastOrUndefined(signature.parameters));
if (getObjectFlags(type) & ObjectFlags.Reference && (<TypeReference>type).target === globalArrayType) {
return (<TypeReference>type).typeArguments[0];
}
}
return anyType;
}
function getSignatureInstantiation(signature: Signature, typeArguments: Type[]): Signature {
typeArguments = fillMissingTypeArguments(typeArguments, signature.typeParameters, getMinTypeArgumentCount(signature.typeParameters));
const instantiations = signature.instantiations || (signature.instantiations = createMap<Signature>());
const id = getTypeListId(typeArguments);
let instantiation = instantiations.get(id);
if (!instantiation) {
instantiations.set(id, instantiation = createSignatureInstantiation(signature, typeArguments));
}
return instantiation;
}
function createSignatureInstantiation(signature: Signature, typeArguments: Type[]): Signature {
return instantiateSignature(signature, createTypeMapper(signature.typeParameters, typeArguments), /*eraseTypeParameters*/ true);
}
function getErasedSignature(signature: Signature): Signature {
if (!signature.typeParameters) return signature;
if (!signature.erasedSignatureCache) {
signature.erasedSignatureCache = instantiateSignature(signature, createTypeEraser(signature.typeParameters), /*eraseTypeParameters*/ 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) {
const isConstructor = signature.declaration.kind === SyntaxKind.Constructor || signature.declaration.kind === SyntaxKind.ConstructSignature;
const type = <ResolvedType>createObjectType(ObjectFlags.Anonymous);
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.get("__index");
}
function getIndexDeclarationOfSymbol(symbol: Symbol, kind: IndexKind): SignatureDeclaration {
const syntaxKind = kind === IndexKind.Number ? SyntaxKind.NumberKeyword : SyntaxKind.StringKeyword;
const indexSymbol = getIndexSymbol(symbol);
if (indexSymbol) {
for (const decl of indexSymbol.declarations) {
const node = <SignatureDeclaration>decl;
if (node.parameters.length === 1) {
const parameter = node.parameters[0];
if (parameter && parameter.type && parameter.type.kind === syntaxKind) {
return node;
}
}
}
}
return undefined;
}
function createIndexInfo(type: Type, isReadonly: boolean, declaration?: SignatureDeclaration): IndexInfo {
return { type, isReadonly, declaration };
}
function getIndexInfoOfSymbol(symbol: Symbol, kind: IndexKind): IndexInfo {
const declaration = getIndexDeclarationOfSymbol(symbol, kind);
if (declaration) {
return createIndexInfo(declaration.type ? getTypeFromTypeNode(declaration.type) : anyType,
(getModifierFlags(declaration) & ModifierFlags.Readonly) !== 0, declaration);
}
return undefined;
}
function getConstraintDeclaration(type: TypeParameter) {
return getDeclarationOfKind<TypeParameterDeclaration>(type.symbol, SyntaxKind.TypeParameter).constraint;
}
function getConstraintFromTypeParameter(typeParameter: TypeParameter): Type {
if (!typeParameter.constraint) {
if (typeParameter.target) {
const targetConstraint = getConstraintOfTypeParameter(typeParameter.target);
typeParameter.constraint = targetConstraint ? instantiateType(targetConstraint, typeParameter.mapper) : noConstraintType;
}
else {
const constraintDeclaration = getConstraintDeclaration(typeParameter);
typeParameter.constraint = constraintDeclaration ? getTypeFromTypeNode(constraintDeclaration) : noConstraintType;
}
}
return typeParameter.constraint === noConstraintType ? undefined : typeParameter.constraint;
}
function getParentSymbolOfTypeParameter(typeParameter: TypeParameter): Symbol {
return getSymbolOfNode(getDeclarationOfKind(typeParameter.symbol, SyntaxKind.TypeParameter).parent);
}
function getTypeListId(types: Type[]) {
let result = "";
if (types) {
const length = types.length;
let i = 0;
while (i < length) {
const startId = types[i].id;
let count = 1;
while (i + count < length && types[i + count].id === startId + count) {
count++;
}
if (result.length) {
result += ",";
}
result += startId;
if (count > 1) {
result += ":" + count;
}
i += count;
}
}
return result;
}
// This function is used to propagate certain flags when creating new object type references and union types.
// It is only necessary to do so if a constituent type might be the undefined type, the null type, the type
// of an object literal or the anyFunctionType. This is because there are operations in the type checker
// that care about the presence of such types at arbitrary depth in a containing type.
function getPropagatingFlagsOfTypes(types: Type[], excludeKinds: TypeFlags): TypeFlags {
let result: TypeFlags = 0;
for (const type of types) {
if (!(type.flags & excludeKinds)) {
result |= type.flags;
}
}
return result & TypeFlags.PropagatingFlags;
}
function createTypeReference(target: GenericType, typeArguments: Type[]): TypeReference {
const id = getTypeListId(typeArguments);
let type = target.instantiations.get(id);
if (!type) {
type = <TypeReference>createObjectType(ObjectFlags.Reference, target.symbol);
target.instantiations.set(id, type);
type.flags |= typeArguments ? getPropagatingFlagsOfTypes(typeArguments, /*excludeKinds*/ 0) : 0;
type.target = target;
type.typeArguments = typeArguments;
}
return type;
}
function cloneTypeReference(source: TypeReference): TypeReference {
const type = <TypeReference>createType(source.flags);
type.symbol = source.symbol;
type.objectFlags = source.objectFlags;
type.target = source.target;
type.typeArguments = source.typeArguments;
return type;
}
function getTypeReferenceArity(type: TypeReference): number {
return length(type.target.typeParameters);
}
// Get type from reference to class or interface
function getTypeFromClassOrInterfaceReference(node: TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference, symbol: Symbol, typeArgs: Type[]): Type {
const type = <InterfaceType>getDeclaredTypeOfSymbol(getMergedSymbol(symbol));
const typeParameters = type.localTypeParameters;
if (typeParameters) {
const numTypeArguments = length(node.typeArguments);
const minTypeArgumentCount = getMinTypeArgumentCount(typeParameters);
if (!isInJavaScriptFile(node) && (numTypeArguments < minTypeArgumentCount || numTypeArguments > typeParameters.length)) {
error(node,
minTypeArgumentCount === typeParameters.length
? Diagnostics.Generic_type_0_requires_1_type_argument_s
: Diagnostics.Generic_type_0_requires_between_1_and_2_type_arguments,
typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType),
minTypeArgumentCount,
typeParameters.length);
return unknownType;
}
// In a type reference, the outer type parameters of the referenced class or interface are automatically
// supplied as type arguments and the type reference only specifies arguments for the local type parameters
// of the class or interface.
const typeArguments = concatenate(type.outerTypeParameters, fillMissingTypeArguments(typeArgs, typeParameters, minTypeArgumentCount, node));
return createTypeReference(<GenericType>type, typeArguments);
}
if (node.typeArguments) {
error(node, Diagnostics.Type_0_is_not_generic, typeToString(type));
return unknownType;
}
return type;
}
function getTypeAliasInstantiation(symbol: Symbol, typeArguments: Type[]): Type {
const type = getDeclaredTypeOfSymbol(symbol);
const links = getSymbolLinks(symbol);
const typeParameters = links.typeParameters;
const id = getTypeListId(typeArguments);
let instantiation = links.instantiations.get(id);
if (!instantiation) {
links.instantiations.set(id, instantiation = instantiateTypeNoAlias(type, createTypeMapper(typeParameters, fillMissingTypeArguments(typeArguments, typeParameters, getMinTypeArgumentCount(typeParameters)))));
}
return instantiation;
}
// Get type from reference to type alias. When a type alias is generic, the declared type of the type alias may include
// references to the type parameters of the alias. We replace those with the actual type arguments by instantiating the
// declared type. Instantiations are cached using the type identities of the type arguments as the key.
function getTypeFromTypeAliasReference(node: TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference, symbol: Symbol, typeArguments: Type[]): Type {
const type = getDeclaredTypeOfSymbol(symbol);
const typeParameters = getSymbolLinks(symbol).typeParameters;
if (typeParameters) {
const numTypeArguments = length(node.typeArguments);
const minTypeArgumentCount = getMinTypeArgumentCount(typeParameters);
if (numTypeArguments < minTypeArgumentCount || numTypeArguments > typeParameters.length) {
error(node,
minTypeArgumentCount === typeParameters.length
? Diagnostics.Generic_type_0_requires_1_type_argument_s
: Diagnostics.Generic_type_0_requires_between_1_and_2_type_arguments,
symbolToString(symbol),
minTypeArgumentCount,
typeParameters.length);
return unknownType;
}
return getTypeAliasInstantiation(symbol, typeArguments);
}
if (node.typeArguments) {
error(node, Diagnostics.Type_0_is_not_generic, symbolToString(symbol));
return unknownType;
}
return type;
}
// Get type from reference to named type that cannot be generic (enum or type parameter)
function getTypeFromNonGenericTypeReference(node: TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference, symbol: Symbol): Type {
if (node.typeArguments) {
error(node, Diagnostics.Type_0_is_not_generic, symbolToString(symbol));
return unknownType;
}
return getDeclaredTypeOfSymbol(symbol);
}
function getTypeReferenceName(node: TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference): EntityNameOrEntityNameExpression | undefined {
switch (node.kind) {
case SyntaxKind.TypeReference:
return (<TypeReferenceNode>node).typeName;
case SyntaxKind.JSDocTypeReference:
return (<JSDocTypeReference>node).name;
case SyntaxKind.ExpressionWithTypeArguments:
// We only support expressions that are simple qualified names. For other
// expressions this produces undefined.
const expr = (<ExpressionWithTypeArguments>node).expression;
if (isEntityNameExpression(expr)) {
return expr;
}
// fall through;
}
return undefined;
}
function resolveTypeReferenceName(typeReferenceName: EntityNameExpression | EntityName) {
if (!typeReferenceName) {
return unknownSymbol;
}
return resolveEntityName(typeReferenceName, SymbolFlags.Type) || unknownSymbol;
}
function getTypeReferenceType(node: TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference, symbol: Symbol) {
const typeArguments = typeArgumentsFromTypeReferenceNode(node); // Do unconditionally so we mark type arguments as referenced.
if (symbol === unknownSymbol) {
return unknownType;
}
if (symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) {
return getTypeFromClassOrInterfaceReference(node, symbol, typeArguments);
}
if (symbol.flags & SymbolFlags.TypeAlias) {
return getTypeFromTypeAliasReference(node, symbol, typeArguments);
}
if (symbol.flags & SymbolFlags.Value && node.kind === SyntaxKind.JSDocTypeReference) {
// A JSDocTypeReference may have resolved to a value (as opposed to a type). In
// that case, the type of this reference is just the type of the value we resolved
// to.
return getTypeOfSymbol(symbol);
}
return getTypeFromNonGenericTypeReference(node, symbol);
}
function getPrimitiveTypeFromJSDocTypeReference(node: JSDocTypeReference): Type {
if (isIdentifier(node.name)) {
switch (node.name.text) {
case "String":
return stringType;
case "Number":
return numberType;
case "Boolean":
return booleanType;
case "Void":
return voidType;
case "Undefined":
return undefinedType;
case "Null":
return nullType;
case "Object":
return anyType;
case "Function":
return anyFunctionType;
case "Array":
case "array":
return !node.typeArguments || !node.typeArguments.length ? createArrayType(anyType) : undefined;
case "Promise":
case "promise":
return !node.typeArguments || !node.typeArguments.length ? createPromiseType(anyType) : undefined;
}
}
}
function getTypeFromJSDocNullableTypeNode(node: JSDocNullableType) {
const type = getTypeFromTypeNode(node.type);
return strictNullChecks ? getUnionType([type, nullType]) : type;
}
function getTypeFromTypeReference(node: TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference): Type {
const links = getNodeLinks(node);
if (!links.resolvedType) {
let symbol: Symbol;
let type: Type;
if (node.kind === SyntaxKind.JSDocTypeReference) {
type = getPrimitiveTypeFromJSDocTypeReference(<JSDocTypeReference>node);
if (!type) {
const typeReferenceName = getTypeReferenceName(node);
symbol = resolveTypeReferenceName(typeReferenceName);
type = getTypeReferenceType(node, symbol);
}
}
else {
// We only support expressions that are simple qualified names. For other expressions this produces undefined.
const typeNameOrExpression: EntityNameOrEntityNameExpression = node.kind === SyntaxKind.TypeReference
? (<TypeReferenceNode>node).typeName
: isEntityNameExpression((<ExpressionWithTypeArguments>node).expression)
? <EntityNameExpression>(<ExpressionWithTypeArguments>node).expression
: undefined;
symbol = typeNameOrExpression && resolveEntityName(typeNameOrExpression, SymbolFlags.Type) || unknownSymbol;
type = getTypeReferenceType(node, symbol);
}
// Cache both the resolved symbol and the resolved type. The resolved symbol is needed in when we check the
// type reference in checkTypeReferenceOrExpressionWithTypeArguments.
links.resolvedSymbol = symbol;
links.resolvedType = type;
}
return links.resolvedType;
}
function typeArgumentsFromTypeReferenceNode(node: TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference): Type[] {
return map(node.typeArguments, getTypeFromTypeNode);
}
function getTypeFromTypeQueryNode(node: TypeQueryNode): Type {
const links = getNodeLinks(node);
if (!links.resolvedType) {
// TypeScript 1.0 spec (April 2014): 3.6.3
// The expression is processed as an identifier expression (section 4.3)
// or property access expression(section 4.10),
// the widened type(section 3.9) of which becomes the result.
links.resolvedType = getWidenedType(checkExpression(node.exprName));
}
return links.resolvedType;
}
function getTypeOfGlobalSymbol(symbol: Symbol, arity: number): ObjectType {
function getTypeDeclaration(symbol: Symbol): Declaration {
const declarations = symbol.declarations;
for (const declaration of declarations) {
switch (declaration.kind) {
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.EnumDeclaration:
return declaration;
}
}
}
if (!symbol) {
return arity ? emptyGenericType : emptyObjectType;
}
const type = getDeclaredTypeOfSymbol(symbol);
if (!(type.flags & TypeFlags.Object)) {
error(getTypeDeclaration(symbol), Diagnostics.Global_type_0_must_be_a_class_or_interface_type, symbol.name);
return arity ? emptyGenericType : emptyObjectType;
}
if (length((<InterfaceType>type).typeParameters) !== arity) {
error(getTypeDeclaration(symbol), Diagnostics.Global_type_0_must_have_1_type_parameter_s, symbol.name, arity);
return arity ? emptyGenericType : emptyObjectType;
}
return <ObjectType>type;
}
function getGlobalValueSymbol(name: string, reportErrors: boolean): Symbol {
return getGlobalSymbol(name, SymbolFlags.Value, reportErrors ? Diagnostics.Cannot_find_global_value_0 : undefined);
}
function getGlobalTypeSymbol(name: string, reportErrors: boolean): Symbol {
return getGlobalSymbol(name, SymbolFlags.Type, reportErrors ? Diagnostics.Cannot_find_global_type_0 : undefined);
}
function getGlobalSymbol(name: string, meaning: SymbolFlags, diagnostic: DiagnosticMessage): Symbol {
return resolveName(undefined, name, meaning, diagnostic, name);
}
function getGlobalType(name: string, arity: 0, reportErrors: boolean): ObjectType;
function getGlobalType(name: string, arity: number, reportErrors: boolean): GenericType;
function getGlobalType(name: string, arity: number, reportErrors: boolean): ObjectType {
const symbol = getGlobalTypeSymbol(name, reportErrors);
return symbol || reportErrors ? getTypeOfGlobalSymbol(symbol, arity) : undefined;
}
function getGlobalTypedPropertyDescriptorType() {
return deferredGlobalTypedPropertyDescriptorType || (deferredGlobalTypedPropertyDescriptorType = getGlobalType("TypedPropertyDescriptor", /*arity*/ 1, /*reportErrors*/ true)) || emptyGenericType;
}
function getGlobalTemplateStringsArrayType() {
return deferredGlobalTemplateStringsArrayType || (deferredGlobalTemplateStringsArrayType = getGlobalType("TemplateStringsArray", /*arity*/ 0, /*reportErrors*/ true)) || emptyObjectType;
}
function getGlobalESSymbolConstructorSymbol(reportErrors: boolean) {
return deferredGlobalESSymbolConstructorSymbol || (deferredGlobalESSymbolConstructorSymbol = getGlobalValueSymbol("Symbol", reportErrors));
}
function getGlobalESSymbolType(reportErrors: boolean) {
return deferredGlobalESSymbolType || (deferredGlobalESSymbolType = getGlobalType("Symbol", /*arity*/ 0, reportErrors)) || emptyObjectType;
}
function getGlobalPromiseType(reportErrors: boolean) {
return deferredGlobalPromiseType || (deferredGlobalPromiseType = getGlobalType("Promise", /*arity*/ 1, reportErrors)) || emptyGenericType;
}
function getGlobalPromiseConstructorSymbol(reportErrors: boolean): Symbol | undefined {
return deferredGlobalPromiseConstructorSymbol || (deferredGlobalPromiseConstructorSymbol = getGlobalValueSymbol("Promise", reportErrors));
}
function getGlobalPromiseConstructorLikeType(reportErrors: boolean) {
return deferredGlobalPromiseConstructorLikeType || (deferredGlobalPromiseConstructorLikeType = getGlobalType("PromiseConstructorLike", /*arity*/ 0, reportErrors)) || emptyObjectType;
}
function getGlobalAsyncIterableType(reportErrors: boolean) {
return deferredGlobalAsyncIterableType || (deferredGlobalAsyncIterableType = getGlobalType("AsyncIterable", /*arity*/ 1, reportErrors)) || emptyGenericType;
}
function getGlobalAsyncIteratorType(reportErrors: boolean) {
return deferredGlobalAsyncIteratorType || (deferredGlobalAsyncIteratorType = getGlobalType("AsyncIterator", /*arity*/ 1, reportErrors)) || emptyGenericType;
}
function getGlobalAsyncIterableIteratorType(reportErrors: boolean) {
return deferredGlobalAsyncIterableIteratorType || (deferredGlobalAsyncIterableIteratorType = getGlobalType("AsyncIterableIterator", /*arity*/ 1, reportErrors)) || emptyGenericType;
}
function getGlobalIterableType(reportErrors: boolean) {
return deferredGlobalIterableType || (deferredGlobalIterableType = getGlobalType("Iterable", /*arity*/ 1, reportErrors)) || emptyGenericType;
}
function getGlobalIteratorType(reportErrors: boolean) {
return deferredGlobalIteratorType || (deferredGlobalIteratorType = getGlobalType("Iterator", /*arity*/ 1, reportErrors)) || emptyGenericType;
}
function getGlobalIterableIteratorType(reportErrors: boolean) {
return deferredGlobalIterableIteratorType || (deferredGlobalIterableIteratorType = getGlobalType("IterableIterator", /*arity*/ 1, reportErrors)) || emptyGenericType;
}
function getGlobalTypeOrUndefined(name: string, arity = 0): ObjectType {
const symbol = getGlobalSymbol(name, SymbolFlags.Type, /*diagnostic*/ undefined);
return symbol && <GenericType>getTypeOfGlobalSymbol(symbol, arity);
}
/**
* Returns a type that is inside a namespace at the global scope, e.g.
* getExportedTypeFromNamespace('JSX', 'Element') returns the JSX.Element type
*/
function getExportedTypeFromNamespace(namespace: string, name: string): Type {
const namespaceSymbol = getGlobalSymbol(namespace, SymbolFlags.Namespace, /*diagnosticMessage*/ undefined);
const typeSymbol = namespaceSymbol && getSymbol(namespaceSymbol.exports, name, SymbolFlags.Type);
return typeSymbol && getDeclaredTypeOfSymbol(typeSymbol);
}
/**
* Instantiates a global type that is generic with some element type, and returns that instantiation.
*/
function createTypeFromGenericGlobalType(genericGlobalType: GenericType, typeArguments: Type[]): ObjectType {
return genericGlobalType !== emptyGenericType ? createTypeReference(genericGlobalType, typeArguments) : emptyObjectType;
}
function createTypedPropertyDescriptorType(propertyType: Type): Type {
return createTypeFromGenericGlobalType(getGlobalTypedPropertyDescriptorType(), [propertyType]);
}
function createAsyncIterableType(iteratedType: Type): Type {
return createTypeFromGenericGlobalType(getGlobalAsyncIterableType(/*reportErrors*/ true), [iteratedType]);
}
function createAsyncIterableIteratorType(iteratedType: Type): Type {
return createTypeFromGenericGlobalType(getGlobalAsyncIterableIteratorType(/*reportErrors*/ true), [iteratedType]);
}
function createIterableType(iteratedType: Type): Type {
return createTypeFromGenericGlobalType(getGlobalIterableType(/*reportErrors*/ true), [iteratedType]);
}
function createIterableIteratorType(iteratedType: Type): Type {
return createTypeFromGenericGlobalType(getGlobalIterableIteratorType(/*reportErrors*/ true), [iteratedType]);
}
function createArrayType(elementType: Type): ObjectType {
return createTypeFromGenericGlobalType(globalArrayType, [elementType]);
}
function getTypeFromArrayTypeNode(node: ArrayTypeNode): Type {
const links = getNodeLinks(node);
if (!links.resolvedType) {
links.resolvedType = createArrayType(getTypeFromTypeNode(node.elementType));
}
return links.resolvedType;
}
// We represent tuple types as type references to synthesized generic interface types created by
// this function. The types are of the form:
//
// interface Tuple<T0, T1, T2, ...> extends Array<T0 | T1 | T2 | ...> { 0: T0, 1: T1, 2: T2, ... }
//
// Note that the generic type created by this function has no symbol associated with it. The same
// is true for each of the synthesized type parameters.
function createTupleTypeOfArity(arity: number): GenericType {
const typeParameters: TypeParameter[] = [];
const properties: Symbol[] = [];
for (let i = 0; i < arity; i++) {
const typeParameter = <TypeParameter>createType(TypeFlags.TypeParameter);
typeParameters.push(typeParameter);
const property = createSymbol(SymbolFlags.Property, "" + i);
property.type = typeParameter;
properties.push(property);
}
const type = <GenericType & InterfaceTypeWithDeclaredMembers>createObjectType(ObjectFlags.Tuple | ObjectFlags.Reference);
type.typeParameters = typeParameters;
type.outerTypeParameters = undefined;
type.localTypeParameters = typeParameters;
type.instantiations = createMap<TypeReference>();
type.instantiations.set(getTypeListId(type.typeParameters), <GenericType>type);
type.target = <GenericType>type;
type.typeArguments = type.typeParameters;
type.thisType = <TypeParameter>createType(TypeFlags.TypeParameter);
type.thisType.isThisType = true;
type.thisType.constraint = type;
type.declaredProperties = properties;
type.declaredCallSignatures = emptyArray;
type.declaredConstructSignatures = emptyArray;
type.declaredStringIndexInfo = undefined;
type.declaredNumberIndexInfo = undefined;
return type;
}
function getTupleTypeOfArity(arity: number): GenericType {
return tupleTypes[arity] || (tupleTypes[arity] = createTupleTypeOfArity(arity));
}
function createTupleType(elementTypes: Type[]) {
return createTypeReference(getTupleTypeOfArity(elementTypes.length), elementTypes);
}
function getTypeFromTupleTypeNode(node: TupleTypeNode): Type {
const links = getNodeLinks(node);
if (!links.resolvedType) {
links.resolvedType = createTupleType(map(node.elementTypes, getTypeFromTypeNode));
}
return links.resolvedType;
}
interface TypeSet extends Array<Type> {
containsAny?: boolean;
containsUndefined?: boolean;
containsNull?: boolean;
containsNonWideningType?: boolean;
containsString?: boolean;
containsNumber?: boolean;
containsStringOrNumberLiteral?: boolean;
containsObjectType?: boolean;
containsEmptyObject?: boolean;
unionIndex?: number;
}
function binarySearchTypes(types: Type[], type: Type): number {
let low = 0;
let high = types.length - 1;
const typeId = type.id;
while (low <= high) {
const middle = low + ((high - low) >> 1);
const id = types[middle].id;
if (id === typeId) {
return middle;
}
else if (id > typeId) {
high = middle - 1;
}
else {
low = middle + 1;
}
}
return ~low;
}
function containsType(types: Type[], type: Type): boolean {
return binarySearchTypes(types, type) >= 0;
}
function addTypeToUnion(typeSet: TypeSet, type: Type) {
const flags = type.flags;
if (flags & TypeFlags.Union) {
addTypesToUnion(typeSet, (<UnionType>type).types);
}
else if (flags & TypeFlags.Any) {
typeSet.containsAny = true;
}
else if (!strictNullChecks && flags & TypeFlags.Nullable) {
if (flags & TypeFlags.Undefined) typeSet.containsUndefined = true;
if (flags & TypeFlags.Null) typeSet.containsNull = true;
if (!(flags & TypeFlags.ContainsWideningType)) typeSet.containsNonWideningType = true;
}
else if (!(flags & TypeFlags.Never)) {
if (flags & TypeFlags.String) typeSet.containsString = true;
if (flags & TypeFlags.Number) typeSet.containsNumber = true;
if (flags & TypeFlags.StringOrNumberLiteral) typeSet.containsStringOrNumberLiteral = true;
const len = typeSet.length;
const index = len && type.id > typeSet[len - 1].id ? ~len : binarySearchTypes(typeSet, type);
if (index < 0) {
if (!(flags & TypeFlags.Object && (<ObjectType>type).objectFlags & ObjectFlags.Anonymous &&
type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method) && containsIdenticalType(typeSet, type))) {
typeSet.splice(~index, 0, type);
}
}
}
}
// Add the given types to the given type set. Order is preserved, duplicates are removed,
// and nested types of the given kind are flattened into the set.
function addTypesToUnion(typeSet: TypeSet, types: Type[]) {
for (const type of types) {
addTypeToUnion(typeSet, type);
}
}
function containsIdenticalType(types: Type[], type: Type) {
for (const t of types) {
if (isTypeIdenticalTo(t, type)) {
return true;
}
}
return false;
}
function isSubtypeOfAny(candidate: Type, types: Type[]): boolean {
for (const type of types) {
if (candidate !== type && isTypeSubtypeOf(candidate, type)) {
return true;
}
}
return false;
}
function isSetOfLiteralsFromSameEnum(types: TypeSet): boolean {
const first = types[0];
if (first.flags & TypeFlags.EnumLiteral) {
const firstEnum = getParentOfSymbol(first.symbol);
for (let i = 1; i < types.length; i++) {
const other = types[i];
if (!(other.flags & TypeFlags.EnumLiteral) || (firstEnum !== getParentOfSymbol(other.symbol))) {
return false;
}
}
return true;
}
return false;
}
function removeSubtypes(types: TypeSet) {
if (types.length === 0 || isSetOfLiteralsFromSameEnum(types)) {
return;
}
let i = types.length;
while (i > 0) {
i--;
if (isSubtypeOfAny(types[i], types)) {
orderedRemoveItemAt(types, i);
}
}
}
function removeRedundantLiteralTypes(types: TypeSet) {
let i = types.length;
while (i > 0) {
i--;
const t = types[i];
const remove =
t.flags & TypeFlags.StringLiteral && types.containsString ||
t.flags & TypeFlags.NumberLiteral && types.containsNumber ||
t.flags & TypeFlags.StringOrNumberLiteral && t.flags & TypeFlags.FreshLiteral && containsType(types, (<LiteralType>t).regularType);
if (remove) {
orderedRemoveItemAt(types, i);
}
}
}
// We sort and deduplicate the constituent types based on object identity. If the subtypeReduction
// flag is specified we also reduce the constituent type set to only include types that aren't subtypes
// of other types. Subtype reduction is expensive for large union types and is possible only when union
// types are known not to circularly reference themselves (as is the case with union types created by
// expression constructs such as array literals and the || and ?: operators). Named types can
// circularly reference themselves and therefore cannot be subtype reduced during their declaration.
// For example, "type Item = string | (() => Item" is a named type that circularly references itself.
function getUnionType(types: Type[], subtypeReduction?: boolean, aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type {
if (types.length === 0) {
return neverType;
}
if (types.length === 1) {
return types[0];
}
const typeSet = [] as TypeSet;
addTypesToUnion(typeSet, types);
if (typeSet.containsAny) {
return anyType;
}
if (subtypeReduction) {
removeSubtypes(typeSet);
}
else if (typeSet.containsStringOrNumberLiteral) {
removeRedundantLiteralTypes(typeSet);
}
if (typeSet.length === 0) {
return typeSet.containsNull ? typeSet.containsNonWideningType ? nullType : nullWideningType :
typeSet.containsUndefined ? typeSet.containsNonWideningType ? undefinedType : undefinedWideningType :
neverType;
}
return getUnionTypeFromSortedList(typeSet, aliasSymbol, aliasTypeArguments);
}
// This function assumes the constituent type list is sorted and deduplicated.
function getUnionTypeFromSortedList(types: Type[], aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type {
if (types.length === 0) {
return neverType;
}
if (types.length === 1) {
return types[0];
}
const id = getTypeListId(types);
let type = unionTypes.get(id);
if (!type) {
const propagatedFlags = getPropagatingFlagsOfTypes(types, /*excludeKinds*/ TypeFlags.Nullable);
type = <UnionType>createType(TypeFlags.Union | propagatedFlags);
unionTypes.set(id, type);
type.types = types;
type.aliasSymbol = aliasSymbol;
type.aliasTypeArguments = aliasTypeArguments;
}
return type;
}
function getTypeFromUnionTypeNode(node: UnionTypeNode): Type {
const links = getNodeLinks(node);
if (!links.resolvedType) {
links.resolvedType = getUnionType(map(node.types, getTypeFromTypeNode), /*subtypeReduction*/ false,
getAliasSymbolForTypeNode(node), getAliasTypeArgumentsForTypeNode(node));
}
return links.resolvedType;
}
function addTypeToIntersection(typeSet: TypeSet, type: Type) {
if (type.flags & TypeFlags.Intersection) {
addTypesToIntersection(typeSet, (<IntersectionType>type).types);
}
else if (type.flags & TypeFlags.Any) {
typeSet.containsAny = true;
}
else if (getObjectFlags(type) & ObjectFlags.Anonymous && isEmptyObjectType(type)) {
typeSet.containsEmptyObject = true;
}
else if (!(type.flags & TypeFlags.Never) && (strictNullChecks || !(type.flags & TypeFlags.Nullable)) && !contains(typeSet, type)) {
if (type.flags & TypeFlags.Object) {
typeSet.containsObjectType = true;
}
if (type.flags & TypeFlags.Union && typeSet.unionIndex === undefined) {
typeSet.unionIndex = typeSet.length;
}
if (!(type.flags & TypeFlags.Object && (<ObjectType>type).objectFlags & ObjectFlags.Anonymous &&
type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method) && containsIdenticalType(typeSet, type))) {
typeSet.push(type);
}
}
}
// Add the given types to the given type set. Order is preserved, duplicates are removed,
// and nested types of the given kind are flattened into the set.
function addTypesToIntersection(typeSet: TypeSet, types: Type[]) {
for (const type of types) {
addTypeToIntersection(typeSet, type);
}
}
// We normalize combinations of intersection and union types based on the distributive property of the '&'
// operator. Specifically, because X & (A | B) is equivalent to X & A | X & B, we can transform intersection
// types with union type constituents into equivalent union types with intersection type constituents and
// effectively ensure that union types are always at the top level in type representations.
//
// We do not perform structural deduplication on intersection types. Intersection types are created only by the &
// type operator and we can't reduce those because we want to support recursive intersection types. For example,
// a type alias of the form "type List<T> = T & { next: List<T> }" cannot be reduced during its declaration.
// Also, unlike union types, the order of the constituent types is preserved in order that overload resolution
// for intersections of types with signatures can be deterministic.
function getIntersectionType(types: Type[], aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type {
if (types.length === 0) {
return emptyObjectType;
}
const typeSet = [] as TypeSet;
addTypesToIntersection(typeSet, types);
if (typeSet.containsAny) {
return anyType;
}
if (typeSet.containsEmptyObject && !typeSet.containsObjectType) {
typeSet.push(emptyObjectType);
}
if (typeSet.length === 1) {
return typeSet[0];
}
const unionIndex = typeSet.unionIndex;
if (unionIndex !== undefined) {
// We are attempting to construct a type of the form X & (A | B) & Y. Transform this into a type of
// the form X & A & Y | X & B & Y and recursively reduce until no union type constituents remain.
const unionType = <UnionType>typeSet[unionIndex];
return getUnionType(map(unionType.types, t => getIntersectionType(replaceElement(typeSet, unionIndex, t))),
/*subtypeReduction*/ false, aliasSymbol, aliasTypeArguments);
}
const id = getTypeListId(typeSet);
let type = intersectionTypes.get(id);
if (!type) {
const propagatedFlags = getPropagatingFlagsOfTypes(typeSet, /*excludeKinds*/ TypeFlags.Nullable);
type = <IntersectionType>createType(TypeFlags.Intersection | propagatedFlags);
intersectionTypes.set(id, type);
type.types = typeSet;
type.aliasSymbol = aliasSymbol;
type.aliasTypeArguments = aliasTypeArguments;
}
return type;
}
function getTypeFromIntersectionTypeNode(node: IntersectionTypeNode): Type {
const links = getNodeLinks(node);
if (!links.resolvedType) {
links.resolvedType = getIntersectionType(map(node.types, getTypeFromTypeNode),
getAliasSymbolForTypeNode(node), getAliasTypeArgumentsForTypeNode(node));
}
return links.resolvedType;
}
function getIndexTypeForGenericType(type: TypeVariable | UnionOrIntersectionType) {
if (!type.resolvedIndexType) {
type.resolvedIndexType = <IndexType>createType(TypeFlags.Index);
type.resolvedIndexType.type = type;
}
return type.resolvedIndexType;
}
function getLiteralTypeFromPropertyName(prop: Symbol) {
return getDeclarationModifierFlagsFromSymbol(prop) & ModifierFlags.NonPublicAccessibilityModifier || startsWith(prop.name, "__@") ?
neverType :
getLiteralType(unescapeIdentifier(prop.name));
}
function getLiteralTypeFromPropertyNames(type: Type) {
return getUnionType(map(getPropertiesOfType(type), getLiteralTypeFromPropertyName));
}
function getIndexType(type: Type): Type {
return maybeTypeOfKind(type, TypeFlags.TypeVariable) ? getIndexTypeForGenericType(<TypeVariable | UnionOrIntersectionType>type) :
getObjectFlags(type) & ObjectFlags.Mapped ? getConstraintTypeFromMappedType(<MappedType>type) :
type.flags & TypeFlags.Any || getIndexInfoOfType(type, IndexKind.String) ? stringType :
getLiteralTypeFromPropertyNames(type);
}
function getIndexTypeOrString(type: Type): Type {
const indexType = getIndexType(type);
return indexType !== neverType ? indexType : stringType;
}
function getTypeFromTypeOperatorNode(node: TypeOperatorNode) {
const links = getNodeLinks(node);
if (!links.resolvedType) {
links.resolvedType = getIndexType(getTypeFromTypeNode(node.type));
}
return links.resolvedType;
}
function createIndexedAccessType(objectType: Type, indexType: Type) {
const type = <IndexedAccessType>createType(TypeFlags.IndexedAccess);
type.objectType = objectType;
type.indexType = indexType;
return type;
}
function getPropertyTypeForIndexType(objectType: Type, indexType: Type, accessNode: ElementAccessExpression | IndexedAccessTypeNode, cacheSymbol: boolean) {
const accessExpression = accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression ? <ElementAccessExpression>accessNode : undefined;
const propName = indexType.flags & TypeFlags.StringOrNumberLiteral ?
"" + (<LiteralType>indexType).value :
accessExpression && checkThatExpressionIsProperSymbolReference(accessExpression.argumentExpression, indexType, /*reportError*/ false) ?
getPropertyNameForKnownSymbolName((<Identifier>(<PropertyAccessExpression>accessExpression.argumentExpression).name).text) :
undefined;
if (propName !== undefined) {
const prop = getPropertyOfType(objectType, propName);
if (prop) {
if (accessExpression) {
if (isAssignmentTarget(accessExpression) && (isReferenceToReadonlyEntity(accessExpression, prop) || isReferenceThroughNamespaceImport(accessExpression))) {
error(accessExpression.argumentExpression, Diagnostics.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property, symbolToString(prop));
return unknownType;
}
if (cacheSymbol) {
getNodeLinks(accessNode).resolvedSymbol = prop;
}
}
return getTypeOfSymbol(prop);
}
}
if (isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbol)) {
if (isTypeAny(objectType)) {
return anyType;
}
const indexInfo = isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.NumberLike) && getIndexInfoOfType(objectType, IndexKind.Number) ||
getIndexInfoOfType(objectType, IndexKind.String) ||
undefined;
if (indexInfo) {
if (accessExpression && indexInfo.isReadonly && (isAssignmentTarget(accessExpression) || isDeleteTarget(accessExpression))) {
error(accessExpression, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(objectType));
return unknownType;
}
return indexInfo.type;
}
if (accessExpression && !isConstEnumObjectType(objectType)) {
if (noImplicitAny && !compilerOptions.suppressImplicitAnyIndexErrors) {
if (getIndexTypeOfType(objectType, IndexKind.Number)) {
error(accessExpression.argumentExpression, Diagnostics.Element_implicitly_has_an_any_type_because_index_expression_is_not_of_type_number);
}
else {
error(accessExpression, Diagnostics.Element_implicitly_has_an_any_type_because_type_0_has_no_index_signature, typeToString(objectType));
}
}
return anyType;
}
}
if (accessNode) {
const indexNode = accessNode.kind === SyntaxKind.ElementAccessExpression ? (<ElementAccessExpression>accessNode).argumentExpression : (<IndexedAccessTypeNode>accessNode).indexType;
if (indexType.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral)) {
error(indexNode, Diagnostics.Property_0_does_not_exist_on_type_1, "" + (<LiteralType>indexType).value, typeToString(objectType));
}
else if (indexType.flags & (TypeFlags.String | TypeFlags.Number)) {
error(indexNode, Diagnostics.Type_0_has_no_matching_index_signature_for_type_1, typeToString(objectType), typeToString(indexType));
}
else {
error(indexNode, Diagnostics.Type_0_cannot_be_used_as_an_index_type, typeToString(indexType));
}
return unknownType;
}
return anyType;
}
function getIndexedAccessForMappedType(type: MappedType, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode) {
const accessExpression = accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression ? <ElementAccessExpression>accessNode : undefined;
if (accessExpression && isAssignmentTarget(accessExpression) && type.declaration.readonlyToken) {
error(accessExpression, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(type));
return unknownType;
}
const mapper = createTypeMapper([getTypeParameterFromMappedType(type)], [indexType]);
const templateMapper = type.mapper ? combineTypeMappers(type.mapper, mapper) : mapper;
return instantiateType(getTemplateTypeFromMappedType(type), templateMapper);
}
function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode) {
// If the index type is generic, if the object type is generic and doesn't originate in an expression,
// or if the object type is a mapped type with a generic constraint, we are performing a higher-order
// index access where we cannot meaningfully access the properties of the object type. Note that for a
// generic T and a non-generic K, we eagerly resolve T[K] if it originates in an expression. This is to
// preserve backwards compatibility. For example, an element access 'this["foo"]' has always been resolved
// eagerly using the constraint type of 'this' at the given location.
if (maybeTypeOfKind(indexType, TypeFlags.TypeVariable | TypeFlags.Index) ||
maybeTypeOfKind(objectType, TypeFlags.TypeVariable) && !(accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression) ||
isGenericMappedType(objectType)) {
if (objectType.flags & TypeFlags.Any) {
return objectType;
}
// If the object type is a mapped type { [P in K]: E }, we instantiate E using a mapper that substitutes
// the index type for P. For example, for an index access { [P in K]: Box<T[P]> }[X], we construct the
// type Box<T[X]>.
if (isGenericMappedType(objectType)) {
return getIndexedAccessForMappedType(<MappedType>objectType, indexType, accessNode);
}
// Otherwise we defer the operation by creating an indexed access type.
const id = objectType.id + "," + indexType.id;
let type = indexedAccessTypes.get(id);
if (!type) {
indexedAccessTypes.set(id, type = createIndexedAccessType(objectType, indexType));
}
return type;
}
// In the following we resolve T[K] to the type of the property in T selected by K.
const apparentObjectType = getApparentType(objectType);
if (indexType.flags & TypeFlags.Union && !(indexType.flags & TypeFlags.Primitive)) {
const propTypes: Type[] = [];
for (const t of (<UnionType>indexType).types) {
const propType = getPropertyTypeForIndexType(apparentObjectType, t, accessNode, /*cacheSymbol*/ false);
if (propType === unknownType) {
return unknownType;
}
propTypes.push(propType);
}
return getUnionType(propTypes);
}
return getPropertyTypeForIndexType(apparentObjectType, indexType, accessNode, /*cacheSymbol*/ true);
}
function getTypeFromIndexedAccessTypeNode(node: IndexedAccessTypeNode) {
const links = getNodeLinks(node);
if (!links.resolvedType) {
links.resolvedType = getIndexedAccessType(getTypeFromTypeNode(node.objectType), getTypeFromTypeNode(node.indexType), node);
}
return links.resolvedType;
}
function getTypeFromMappedTypeNode(node: MappedTypeNode): Type {
const links = getNodeLinks(node);
if (!links.resolvedType) {
const type = <MappedType>createObjectType(ObjectFlags.Mapped, node.symbol);
type.declaration = node;
type.aliasSymbol = getAliasSymbolForTypeNode(node);
type.aliasTypeArguments = getAliasTypeArgumentsForTypeNode(node);
links.resolvedType = type;
// Eagerly resolve the constraint type which forces an error if the constraint type circularly
// references itself through one or more type aliases.
getConstraintTypeFromMappedType(type);
}
return links.resolvedType;
}
function getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node: TypeNode): Type {
const links = getNodeLinks(node);
if (!links.resolvedType) {
// Deferred resolution of members is handled by resolveObjectTypeMembers
const aliasSymbol = getAliasSymbolForTypeNode(node);
if (node.symbol.members.size === 0 && !aliasSymbol) {
links.resolvedType = emptyTypeLiteralType;
}
else {
const type = createObjectType(ObjectFlags.Anonymous, node.symbol);
type.aliasSymbol = aliasSymbol;
type.aliasTypeArguments = getAliasTypeArgumentsForTypeNode(node);
links.resolvedType = type;
}
}
return links.resolvedType;
}
function getAliasSymbolForTypeNode(node: TypeNode) {
return node.parent.kind === SyntaxKind.TypeAliasDeclaration ? getSymbolOfNode(node.parent) : undefined;
}
function getAliasTypeArgumentsForTypeNode(node: TypeNode) {
const symbol = getAliasSymbolForTypeNode(node);
return symbol ? getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol) : undefined;
}
/**
* Since the source of spread types are object literals, which are not binary,
* this function should be called in a left folding style, with left = previous result of getSpreadType
* and right = the new element to be spread.
*/
function getSpreadType(left: Type, right: Type): Type {
if (left.flags & TypeFlags.Any || right.flags & TypeFlags.Any) {
return anyType;
}
left = filterType(left, t => !(t.flags & TypeFlags.Nullable));
if (left.flags & TypeFlags.Never) {
return right;
}
right = filterType(right, t => !(t.flags & TypeFlags.Nullable));
if (right.flags & TypeFlags.Never) {
return left;
}
if (left.flags & TypeFlags.Union) {
return mapType(left, t => getSpreadType(t, right));
}
if (right.flags & TypeFlags.Union) {
return mapType(right, t => getSpreadType(left, t));
}
if (right.flags & TypeFlags.NonPrimitive) {
return emptyObjectType;
}
const members = createMap<Symbol>();
const skippedPrivateMembers = createMap<boolean>();
let stringIndexInfo: IndexInfo;
let numberIndexInfo: IndexInfo;
if (left === emptyObjectType) {
// for the first spread element, left === emptyObjectType, so take the right's string indexer
stringIndexInfo = getIndexInfoOfType(right, IndexKind.String);
numberIndexInfo = getIndexInfoOfType(right, IndexKind.Number);
}
else {
stringIndexInfo = unionSpreadIndexInfos(getIndexInfoOfType(left, IndexKind.String), getIndexInfoOfType(right, IndexKind.String));
numberIndexInfo = unionSpreadIndexInfos(getIndexInfoOfType(left, IndexKind.Number), getIndexInfoOfType(right, IndexKind.Number));
}
for (const rightProp of getPropertiesOfType(right)) {
// we approximate own properties as non-methods plus methods that are inside the object literal
const isSetterWithoutGetter = rightProp.flags & SymbolFlags.SetAccessor && !(rightProp.flags & SymbolFlags.GetAccessor);
if (getDeclarationModifierFlagsFromSymbol(rightProp) & (ModifierFlags.Private | ModifierFlags.Protected)) {
skippedPrivateMembers.set(rightProp.name, true);
}
else if (!isClassMethod(rightProp) && !isSetterWithoutGetter) {
members.set(rightProp.name, getNonReadonlySymbol(rightProp));
}
}
for (const leftProp of getPropertiesOfType(left)) {
if (leftProp.flags & SymbolFlags.SetAccessor && !(leftProp.flags & SymbolFlags.GetAccessor)
|| skippedPrivateMembers.has(leftProp.name)
|| isClassMethod(leftProp)) {
continue;
}
if (members.has(leftProp.name)) {
const rightProp = members.get(leftProp.name);
const rightType = getTypeOfSymbol(rightProp);
if (rightProp.flags & SymbolFlags.Optional) {
const declarations: Declaration[] = concatenate(leftProp.declarations, rightProp.declarations);
const flags = SymbolFlags.Property | (leftProp.flags & SymbolFlags.Optional);
const result = createSymbol(flags, leftProp.name);
result.type = getUnionType([getTypeOfSymbol(leftProp), rightType]);
result.leftSpread = leftProp;
result.rightSpread = rightProp;
result.declarations = declarations;
members.set(leftProp.name, result);
}
}
else {
members.set(leftProp.name, getNonReadonlySymbol(leftProp));
}
}
return createAnonymousType(undefined, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo);
}
function getNonReadonlySymbol(prop: Symbol) {
if (!isReadonlySymbol(prop)) {
return prop;
}
const flags = SymbolFlags.Property | (prop.flags & SymbolFlags.Optional);
const result = createSymbol(flags, prop.name);
result.type = getTypeOfSymbol(prop);
result.declarations = prop.declarations;
result.syntheticOrigin = prop;
return result;
}
function isClassMethod(prop: Symbol) {
return prop.flags & SymbolFlags.Method && find(prop.declarations, decl => isClassLike(decl.parent));
}
function createLiteralType(flags: TypeFlags, value: string | number, symbol: Symbol) {
const type = <LiteralType>createType(flags);
type.symbol = symbol;
type.value = value;
return type;
}
function getFreshTypeOfLiteralType(type: Type) {
if (type.flags & TypeFlags.StringOrNumberLiteral && !(type.flags & TypeFlags.FreshLiteral)) {
if (!(<LiteralType>type).freshType) {
const freshType = <LiteralType>createLiteralType(type.flags | TypeFlags.FreshLiteral, (<LiteralType>type).value, (<LiteralType>type).symbol);
freshType.regularType = <LiteralType>type;
(<LiteralType>type).freshType = freshType;
}
return (<LiteralType>type).freshType;
}
return type;
}
function getRegularTypeOfLiteralType(type: Type) {
return type.flags & TypeFlags.StringOrNumberLiteral && type.flags & TypeFlags.FreshLiteral ? (<LiteralType>type).regularType : type;
}
function getLiteralType(value: string | number, enumId?: number, symbol?: Symbol) {
// We store all literal types in a single map with keys of the form '#NNN' and '@SSS',
// where NNN is the text representation of a numeric literal and SSS are the characters
// of a string literal. For literal enum members we use 'EEE#NNN' and 'EEE@SSS', where
// EEE is a unique id for the containing enum type.
const qualifier = typeof value === "number" ? "#" : "@";
const key = enumId ? enumId + qualifier + value : qualifier + value;
let type = literalTypes.get(key);
if (!type) {
const flags = (typeof value === "number" ? TypeFlags.NumberLiteral : TypeFlags.StringLiteral) | (enumId ? TypeFlags.EnumLiteral : 0);
literalTypes.set(key, type = createLiteralType(flags, value, symbol));
}
return type;
}
function getTypeFromLiteralTypeNode(node: LiteralTypeNode): Type {
const links = getNodeLinks(node);
if (!links.resolvedType) {
links.resolvedType = getRegularTypeOfLiteralType(checkExpression(node.literal));
}
return links.resolvedType;
}
function getTypeFromJSDocVariadicType(node: JSDocVariadicType): Type {
const links = getNodeLinks(node);
if (!links.resolvedType) {
const type = getTypeFromTypeNode(node.type);
links.resolvedType = type ? createArrayType(type) : unknownType;
}
return links.resolvedType;
}
function getTypeFromJSDocTupleType(node: JSDocTupleType): Type {
const links = getNodeLinks(node);
if (!links.resolvedType) {
const types = map(node.types, getTypeFromTypeNode);
links.resolvedType = createTupleType(types);
}
return links.resolvedType;
}
function getThisType(node: Node): Type {
const container = getThisContainer(node, /*includeArrowFunctions*/ false);
const parent = container && container.parent;
if (parent && (isClassLike(parent) || parent.kind === SyntaxKind.InterfaceDeclaration)) {
if (!(getModifierFlags(container) & ModifierFlags.Static) &&
(container.kind !== SyntaxKind.Constructor || isNodeDescendantOf(node, (<ConstructorDeclaration>container).body))) {
return getDeclaredTypeOfClassOrInterface(getSymbolOfNode(parent)).thisType;
}
}
error(node, Diagnostics.A_this_type_is_available_only_in_a_non_static_member_of_a_class_or_interface);
return unknownType;
}
function getTypeFromThisTypeNode(node: TypeNode): Type {
const links = getNodeLinks(node);
if (!links.resolvedType) {
links.resolvedType = getThisType(node);
}
return links.resolvedType;
}
function getTypeFromTypeNode(node: TypeNode): Type {
switch (node.kind) {
case SyntaxKind.AnyKeyword:
case SyntaxKind.JSDocAllType:
case SyntaxKind.JSDocUnknownType:
return anyType;
case SyntaxKind.StringKeyword:
return stringType;
case SyntaxKind.NumberKeyword:
return numberType;
case SyntaxKind.BooleanKeyword:
return booleanType;
case SyntaxKind.SymbolKeyword:
return esSymbolType;
case SyntaxKind.VoidKeyword:
return voidType;
case SyntaxKind.UndefinedKeyword:
return undefinedType;
case SyntaxKind.NullKeyword:
return nullType;
case SyntaxKind.NeverKeyword:
return neverType;
case SyntaxKind.ObjectKeyword:
return nonPrimitiveType;
case SyntaxKind.ThisType:
case SyntaxKind.ThisKeyword:
return getTypeFromThisTypeNode(node);
case SyntaxKind.LiteralType:
return getTypeFromLiteralTypeNode(<LiteralTypeNode>node);
case SyntaxKind.JSDocLiteralType:
return getTypeFromLiteralTypeNode((<JSDocLiteralType>node).literal);
case SyntaxKind.TypeReference:
case SyntaxKind.JSDocTypeReference:
return getTypeFromTypeReference(<TypeReferenceNode>node);
case SyntaxKind.TypePredicate:
return booleanType;
case SyntaxKind.ExpressionWithTypeArguments:
return getTypeFromTypeReference(<ExpressionWithTypeArguments>node);
case SyntaxKind.TypeQuery:
return getTypeFromTypeQueryNode(<TypeQueryNode>node);
case SyntaxKind.ArrayType:
case SyntaxKind.JSDocArrayType:
return getTypeFromArrayTypeNode(<ArrayTypeNode>node);
case SyntaxKind.TupleType:
return getTypeFromTupleTypeNode(<TupleTypeNode>node);
case SyntaxKind.UnionType:
case SyntaxKind.JSDocUnionType:
return getTypeFromUnionTypeNode(<UnionTypeNode>node);
case SyntaxKind.IntersectionType:
return getTypeFromIntersectionTypeNode(<IntersectionTypeNode>node);
case SyntaxKind.JSDocNullableType:
return getTypeFromJSDocNullableTypeNode(<JSDocNullableType>node);
case SyntaxKind.ParenthesizedType:
case SyntaxKind.JSDocNonNullableType:
case SyntaxKind.JSDocConstructorType:
case SyntaxKind.JSDocThisType:
case SyntaxKind.JSDocOptionalType:
return getTypeFromTypeNode((<ParenthesizedTypeNode | JSDocTypeReferencingNode>node).type);
case SyntaxKind.JSDocRecordType:
return getTypeFromTypeNode((node as JSDocRecordType).literal);
case SyntaxKind.FunctionType:
case SyntaxKind.ConstructorType:
case SyntaxKind.TypeLiteral:
case SyntaxKind.JSDocTypeLiteral:
case SyntaxKind.JSDocFunctionType:
return getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node);
case SyntaxKind.TypeOperator:
return getTypeFromTypeOperatorNode(<TypeOperatorNode>node);
case SyntaxKind.IndexedAccessType:
return getTypeFromIndexedAccessTypeNode(<IndexedAccessTypeNode>node);
case SyntaxKind.MappedType:
return getTypeFromMappedTypeNode(<MappedTypeNode>node);
// This function assumes that an identifier or qualified name is a type expression
// Callers should first ensure this by calling isTypeNode
case SyntaxKind.Identifier:
case SyntaxKind.QualifiedName:
const symbol = getSymbolAtLocation(node);
return symbol && getDeclaredTypeOfSymbol(symbol);
case SyntaxKind.JSDocTupleType:
return getTypeFromJSDocTupleType(<JSDocTupleType>node);
case SyntaxKind.JSDocVariadicType:
return getTypeFromJSDocVariadicType(<JSDocVariadicType>node);
default:
return unknownType;
}
}
function instantiateList<T>(items: T[], mapper: TypeMapper, instantiator: (item: T, mapper: TypeMapper) => T): T[] {
if (items && items.length) {
const result: T[] = [];
for (const v of items) {
result.push(instantiator(v, mapper));
}
return result;
}
return items;
}
function instantiateTypes(types: Type[], mapper: TypeMapper) {
return instantiateList(types, mapper, instantiateType);
}
function instantiateSignatures(signatures: Signature[], mapper: TypeMapper) {
return instantiateList(signatures, mapper, instantiateSignature);
}
function instantiateCached<T extends Type>(type: T, mapper: TypeMapper, instantiator: (item: T, mapper: TypeMapper) => T): T {
const instantiations = mapper.instantiations || (mapper.instantiations = []);
return <T>instantiations[type.id] || (instantiations[type.id] = instantiator(type, mapper));
}
function makeUnaryTypeMapper(source: Type, target: Type) {
return (t: Type) => t === source ? target : t;
}
function makeBinaryTypeMapper(source1: Type, target1: Type, source2: Type, target2: Type) {
return (t: Type) => t === source1 ? target1 : t === source2 ? target2 : t;
}
function makeArrayTypeMapper(sources: Type[], targets: Type[]) {
return (t: Type) => {
for (let i = 0; i < sources.length; i++) {
if (t === sources[i]) {
return targets ? targets[i] : anyType;
}
}
return t;
};
}
function createTypeMapper(sources: Type[], targets: Type[]): TypeMapper {
const mapper: TypeMapper = sources.length === 1 ? makeUnaryTypeMapper(sources[0], targets ? targets[0] : anyType) :
sources.length === 2 ? makeBinaryTypeMapper(sources[0], targets ? targets[0] : anyType, sources[1], targets ? targets[1] : anyType) :
makeArrayTypeMapper(sources, targets);
mapper.mappedTypes = sources;
return mapper;
}
function createTypeEraser(sources: Type[]): TypeMapper {
return createTypeMapper(sources, /*targets*/ undefined);
}
/**
* Maps forward-references to later types parameters to the empty object type.
* This is used during inference when instantiating type parameter defaults.
*/
function createBackreferenceMapper(typeParameters: TypeParameter[], index: number) {
const mapper: TypeMapper = t => indexOf(typeParameters, t) >= index ? emptyObjectType : t;
mapper.mappedTypes = typeParameters;
return mapper;
}
function getInferenceMapper(context: InferenceContext): TypeMapper {
if (!context.mapper) {
const mapper: TypeMapper = t => {
const typeParameters = context.signature.typeParameters;
for (let i = 0; i < typeParameters.length; i++) {
if (t === typeParameters[i]) {
context.inferences[i].isFixed = true;
return getInferredType(context, i);
}
}
return t;
};
mapper.mappedTypes = context.signature.typeParameters;
mapper.context = context;
context.mapper = mapper;
}
return context.mapper;
}
function identityMapper(type: Type): Type {
return type;
}
function combineTypeMappers(mapper1: TypeMapper, mapper2: TypeMapper): TypeMapper {
const mapper: TypeMapper = t => instantiateType(mapper1(t), mapper2);
mapper.mappedTypes = concatenate(mapper1.mappedTypes, mapper2.mappedTypes);
return mapper;
}
function createReplacementMapper(source: Type, target: Type, baseMapper: TypeMapper) {
const mapper: TypeMapper = t => t === source ? target : baseMapper(t);
mapper.mappedTypes = baseMapper.mappedTypes;
return mapper;
}
function cloneTypeParameter(typeParameter: TypeParameter): TypeParameter {
const result = <TypeParameter>createType(TypeFlags.TypeParameter);
result.symbol = typeParameter.symbol;
result.target = typeParameter;
return result;
}
function cloneTypePredicate(predicate: TypePredicate, mapper: TypeMapper): ThisTypePredicate | IdentifierTypePredicate {
if (isIdentifierTypePredicate(predicate)) {
return {
kind: TypePredicateKind.Identifier,
parameterName: predicate.parameterName,
parameterIndex: predicate.parameterIndex,
type: instantiateType(predicate.type, mapper)
} as IdentifierTypePredicate;
}
else {
return {
kind: TypePredicateKind.This,
type: instantiateType(predicate.type, mapper)
} as ThisTypePredicate;
}
}
function instantiateSignature(signature: Signature, mapper: TypeMapper, eraseTypeParameters?: boolean): Signature {
let freshTypeParameters: TypeParameter[];
let freshTypePredicate: TypePredicate;
if (signature.typeParameters && !eraseTypeParameters) {
// First create a fresh set of type parameters, then include a mapping from the old to the
// new type parameters in the mapper function. Finally store this mapper in the new type
// parameters such that we can use it when instantiating constraints.
freshTypeParameters = map(signature.typeParameters, cloneTypeParameter);
mapper = combineTypeMappers(createTypeMapper(signature.typeParameters, freshTypeParameters), mapper);
for (const tp of freshTypeParameters) {
tp.mapper = mapper;
}
}
if (signature.typePredicate) {
freshTypePredicate = cloneTypePredicate(signature.typePredicate, mapper);
}
const result = createSignature(signature.declaration, freshTypeParameters,
signature.thisParameter && instantiateSymbol(signature.thisParameter, mapper),
instantiateList(signature.parameters, mapper, instantiateSymbol),
instantiateType(signature.resolvedReturnType, mapper),
freshTypePredicate,
signature.minArgumentCount, signature.hasRestParameter, signature.hasLiteralTypes);
result.target = signature;
result.mapper = mapper;
return result;
}
function instantiateSymbol(symbol: Symbol, mapper: TypeMapper): Symbol {
if (getCheckFlags(symbol) & CheckFlags.Instantiated) {
const 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.
const result = createSymbol(symbol.flags, symbol.name);
result.checkFlags = CheckFlags.Instantiated;
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: AnonymousType, mapper: TypeMapper): AnonymousType {
const result = <AnonymousType>createObjectType(ObjectFlags.Anonymous | ObjectFlags.Instantiated, type.symbol);
result.target = type.objectFlags & ObjectFlags.Instantiated ? type.target : type;
result.mapper = type.objectFlags & ObjectFlags.Instantiated ? combineTypeMappers(type.mapper, mapper) : mapper;
result.aliasSymbol = type.aliasSymbol;
result.aliasTypeArguments = instantiateTypes(type.aliasTypeArguments, mapper);
return result;
}
function instantiateMappedType(type: MappedType, mapper: TypeMapper): Type {
// Check if we have a homomorphic mapped type, i.e. a type of the form { [P in keyof T]: X } for some
// type variable T. If so, the mapped type is distributive over a union type and when T is instantiated
// to a union type A | B, we produce { [P in keyof A]: X } | { [P in keyof B]: X }. Furthermore, for
// homomorphic mapped types we leave primitive types alone. For example, when T is instantiated to a
// union type A | undefined, we produce { [P in keyof A]: X } | undefined.
const constraintType = getConstraintTypeFromMappedType(type);
if (constraintType.flags & TypeFlags.Index) {
const typeVariable = (<IndexType>constraintType).type;
if (typeVariable.flags & TypeFlags.TypeParameter) {
const mappedTypeVariable = instantiateType(typeVariable, mapper);
if (typeVariable !== mappedTypeVariable) {
return mapType(mappedTypeVariable, t => {
if (isMappableType(t)) {
return instantiateMappedObjectType(type, createReplacementMapper(typeVariable, t, mapper));
}
return t;
});
}
}
}
return instantiateMappedObjectType(type, mapper);
}
function isMappableType(type: Type) {
return type.flags & (TypeFlags.TypeParameter | TypeFlags.Object | TypeFlags.Intersection | TypeFlags.IndexedAccess);
}
function instantiateMappedObjectType(type: MappedType, mapper: TypeMapper): Type {
const result = <MappedType>createObjectType(ObjectFlags.Mapped | ObjectFlags.Instantiated, type.symbol);
result.declaration = type.declaration;
result.mapper = type.mapper ? combineTypeMappers(type.mapper, mapper) : mapper;
result.aliasSymbol = type.aliasSymbol;
result.aliasTypeArguments = instantiateTypes(type.aliasTypeArguments, mapper);
return result;
}
function isSymbolInScopeOfMappedTypeParameter(symbol: Symbol, mapper: TypeMapper) {
if (!(symbol.declarations && symbol.declarations.length)) {
return false;
}
const mappedTypes = mapper.mappedTypes;
// Starting with the parent of the symbol's declaration, check if the mapper maps any of
// the type parameters introduced by enclosing declarations. We just pick the first
// declaration since multiple declarations will all have the same parent anyway.
return !!findAncestor(symbol.declarations[0], node => {
if (node.kind === SyntaxKind.ModuleDeclaration || node.kind === SyntaxKind.SourceFile) {
return "quit";
}
switch (node.kind) {
case SyntaxKind.FunctionType:
case SyntaxKind.ConstructorType:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
case SyntaxKind.Constructor:
case SyntaxKind.CallSignature:
case SyntaxKind.ConstructSignature:
case SyntaxKind.IndexSignature:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
case SyntaxKind.ClassDeclaration:
case SyntaxKind.ClassExpression:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.TypeAliasDeclaration:
const declaration = node as DeclarationWithTypeParameters;
if (declaration.typeParameters) {
for (const d of declaration.typeParameters) {
if (contains(mappedTypes, getDeclaredTypeOfTypeParameter(getSymbolOfNode(d)))) {
return true;
}
}
}
if (isClassLike(node) || node.kind === SyntaxKind.InterfaceDeclaration) {
const thisType = getDeclaredTypeOfClassOrInterface(getSymbolOfNode(node)).thisType;
if (thisType && contains(mappedTypes, thisType)) {
return true;
}
}
break;
case SyntaxKind.MappedType:
if (contains(mappedTypes, getDeclaredTypeOfTypeParameter(getSymbolOfNode((<MappedTypeNode>node).typeParameter)))) {
return true;
}
break;
case SyntaxKind.JSDocFunctionType:
const func = node as JSDocFunctionType;
for (const p of func.parameters) {
if (contains(mappedTypes, getTypeOfNode(p))) {
return true;
}
}
break;
}
});
}
function isTopLevelTypeAlias(symbol: Symbol) {
if (symbol.declarations && symbol.declarations.length) {
const parentKind = symbol.declarations[0].parent.kind;
return parentKind === SyntaxKind.SourceFile || parentKind === SyntaxKind.ModuleBlock;
}
return false;
}
function instantiateType(type: Type, mapper: TypeMapper): Type {
if (type && mapper !== identityMapper) {
// If we are instantiating a type that has a top-level type alias, obtain the instantiation through
// the type alias instead in order to share instantiations for the same type arguments. This can
// dramatically reduce the number of structurally identical types we generate. Note that we can only
// perform this optimization for top-level type aliases. Consider:
//
// function f1<T>(x: T) {
// type Foo<X> = { x: X, t: T };
// let obj: Foo<T> = { x: x };
// return obj;
// }
// function f2<U>(x: U) { return f1(x); }
// let z = f2(42);
//
// Above, the declaration of f2 has an inferred return type that is an instantiation of f1's Foo<X>
// equivalent to { x: U, t: U }. When instantiating this return type, we can't go back to Foo<X>'s
// cache because all cached instantiations are of the form { x: ???, t: T }, i.e. they have not been
// instantiated for T. Instead, we need to further instantiate the { x: U, t: U } form.
if (type.aliasSymbol && isTopLevelTypeAlias(type.aliasSymbol)) {
if (type.aliasTypeArguments) {
return getTypeAliasInstantiation(type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper));
}
return type;
}
return instantiateTypeNoAlias(type, mapper);
}
return type;
}
function instantiateTypeNoAlias(type: Type, mapper: TypeMapper): Type {
if (type.flags & TypeFlags.TypeParameter) {
return mapper(<TypeParameter>type);
}
if (type.flags & TypeFlags.Object) {
if ((<ObjectType>type).objectFlags & ObjectFlags.Anonymous) {
// If the anonymous type originates in a declaration of a function, method, class, or
// interface, in an object type literal, or in an object literal expression, we may need
// to instantiate the type because it might reference a type parameter. We skip instantiation
// if none of the type parameters that are in scope in the type's declaration are mapped by
// the given mapper, however we can only do that analysis if the type isn't itself an
// instantiation.
return type.symbol &&
type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) &&
((<ObjectType>type).objectFlags & ObjectFlags.Instantiated || isSymbolInScopeOfMappedTypeParameter(type.symbol, mapper)) ?
instantiateCached(type, mapper, instantiateAnonymousType) : type;
}
if ((<ObjectType>type).objectFlags & ObjectFlags.Mapped) {
return instantiateCached(type, mapper, instantiateMappedType);
}
if ((<ObjectType>type).objectFlags & ObjectFlags.Reference) {
return createTypeReference((<TypeReference>type).target, instantiateTypes((<TypeReference>type).typeArguments, mapper));
}
}
if (type.flags & TypeFlags.Union && !(type.flags & TypeFlags.Primitive)) {
return getUnionType(instantiateTypes((<UnionType>type).types, mapper), /*subtypeReduction*/ false, type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper));
}
if (type.flags & TypeFlags.Intersection) {
return getIntersectionType(instantiateTypes((<IntersectionType>type).types, mapper), type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper));
}
if (type.flags & TypeFlags.Index) {
return getIndexType(instantiateType((<IndexType>type).type, mapper));
}
if (type.flags & TypeFlags.IndexedAccess) {
return getIndexedAccessType(instantiateType((<IndexedAccessType>type).objectType, mapper), instantiateType((<IndexedAccessType>type).indexType, mapper));
}
return type;
}
function instantiateIndexInfo(info: IndexInfo, mapper: TypeMapper): IndexInfo {
return info && createIndexInfo(instantiateType(info.type, mapper), info.isReadonly, info.declaration);
}
// Returns true if the given expression contains (at any level of nesting) a function or arrow expression
// that is subject to contextual typing.
function isContextSensitive(node: Expression | MethodDeclaration | ObjectLiteralElementLike | JsxAttributeLike): boolean {
Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isObjectLiteralMethod(node));
switch (node.kind) {
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
return isContextSensitiveFunctionLikeDeclaration(<FunctionExpression>node);
case SyntaxKind.ObjectLiteralExpression:
return forEach((<ObjectLiteralExpression>node).properties, isContextSensitive);
case SyntaxKind.ArrayLiteralExpression:
return forEach((<ArrayLiteralExpression>node).elements, isContextSensitive);
case SyntaxKind.ConditionalExpression:
return isContextSensitive((<ConditionalExpression>node).whenTrue) ||
isContextSensitive((<ConditionalExpression>node).whenFalse);
case SyntaxKind.BinaryExpression:
return (<BinaryExpression>node).operatorToken.kind === SyntaxKind.BarBarToken &&
(isContextSensitive((<BinaryExpression>node).left) || isContextSensitive((<BinaryExpression>node).right));
case SyntaxKind.PropertyAssignment:
return isContextSensitive((<PropertyAssignment>node).initializer);
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
return isContextSensitiveFunctionLikeDeclaration(<MethodDeclaration>node);
case SyntaxKind.ParenthesizedExpression:
return isContextSensitive((<ParenthesizedExpression>node).expression);
case SyntaxKind.JsxAttributes:
return forEach((<JsxAttributes>node).properties, isContextSensitive);
case SyntaxKind.JsxAttribute:
// If there is no initializer, JSX attribute has a boolean value of true which is not context sensitive.
return (<JsxAttribute>node).initializer && isContextSensitive((<JsxAttribute>node).initializer);
case SyntaxKind.JsxExpression:
// It is possible to that node.expression is undefined (e.g <div x={} />)
return (<JsxExpression>node).expression && isContextSensitive((<JsxExpression>node).expression);
}
return false;
}
function isContextSensitiveFunctionLikeDeclaration(node: FunctionLikeDeclaration) {
// Functions with type parameters are not context sensitive.
if (node.typeParameters) {
return false;
}
// Functions with any parameters that lack type annotations are context sensitive.
if (forEach(node.parameters, p => !p.type)) {
return true;
}
// For arrow functions we now know we're not context sensitive.
if (node.kind === SyntaxKind.ArrowFunction) {
return false;
}
// If the first parameter is not an explicit 'this' parameter, then the function has
// an implicit 'this' parameter which is subject to contextual typing. Otherwise we
// know that all parameters (including 'this') have type annotations and nothing is
// subject to contextual typing.
const parameter = firstOrUndefined(node.parameters);
return !(parameter && parameterIsThisKeyword(parameter));
}
function isContextSensitiveFunctionOrObjectLiteralMethod(func: Node): func is FunctionExpression | ArrowFunction | MethodDeclaration {
return (isFunctionExpressionOrArrowFunction(func) || isObjectLiteralMethod(func)) && isContextSensitiveFunctionLikeDeclaration(func);
}
function getTypeWithoutSignatures(type: Type): Type {
if (type.flags & TypeFlags.Object) {
const resolved = resolveStructuredTypeMembers(<ObjectType>type);
if (resolved.constructSignatures.length) {
const result = <ResolvedType>createObjectType(ObjectFlags.Anonymous, type.symbol);
result.members = resolved.members;
result.properties = resolved.properties;
result.callSignatures = emptyArray;
result.constructSignatures = emptyArray;
return result;
}
}
else if (type.flags & TypeFlags.Intersection) {
return getIntersectionType(map((<IntersectionType>type).types, getTypeWithoutSignatures));
}
return type;
}
// TYPE CHECKING
function isTypeIdenticalTo(source: Type, target: Type): boolean {
return isTypeRelatedTo(source, target, identityRelation);
}
function compareTypesIdentical(source: Type, target: Type): Ternary {
return isTypeRelatedTo(source, target, identityRelation) ? Ternary.True : Ternary.False;
}
function compareTypesAssignable(source: Type, target: Type): Ternary {
return isTypeRelatedTo(source, target, assignableRelation) ? Ternary.True : Ternary.False;
}
function isTypeSubtypeOf(source: Type, target: Type): boolean {
return isTypeRelatedTo(source, target, subtypeRelation);
}
function isTypeAssignableTo(source: Type, target: Type): boolean {
return isTypeRelatedTo(source, target, assignableRelation);
}
// A type S is considered to be an instance of a type T if S and T are the same type or if S is a
// subtype of T but not structurally identical to T. This specifically means that two distinct but
// structurally identical types (such as two classes) are not considered instances of each other.
function isTypeInstanceOf(source: Type, target: Type): boolean {
return getTargetType(source) === getTargetType(target) || isTypeSubtypeOf(source, target) && !isTypeIdenticalTo(source, target);
}
/**
* This is *not* a bi-directional relationship.
* If one needs to check both directions for comparability, use a second call to this function or 'checkTypeComparableTo'.
*/
function isTypeComparableTo(source: Type, target: Type): boolean {
return isTypeRelatedTo(source, target, comparableRelation);
}
function areTypesComparable(type1: Type, type2: Type): boolean {
return isTypeComparableTo(type1, type2) || isTypeComparableTo(type2, type1);
}
function checkTypeSubtypeOf(source: Type, target: Type, errorNode: Node, headMessage?: DiagnosticMessage, containingMessageChain?: DiagnosticMessageChain): boolean {
return checkTypeRelatedTo(source, target, subtypeRelation, errorNode, headMessage, containingMessageChain);
}
function checkTypeAssignableTo(source: Type, target: Type, errorNode: Node, headMessage?: DiagnosticMessage, containingMessageChain?: DiagnosticMessageChain): boolean {
return checkTypeRelatedTo(source, target, assignableRelation, errorNode, headMessage, containingMessageChain);
}
/**
* This is *not* a bi-directional relationship.
* If one needs to check both directions for comparability, use a second call to this function or 'isTypeComparableTo'.
*/
function checkTypeComparableTo(source: Type, target: Type, errorNode: Node, headMessage?: DiagnosticMessage, containingMessageChain?: DiagnosticMessageChain): boolean {
return checkTypeRelatedTo(source, target, comparableRelation, errorNode, headMessage, containingMessageChain);
}
function isSignatureAssignableTo(source: Signature,
target: Signature,
ignoreReturnTypes: boolean): boolean {
return compareSignaturesRelated(source, target, /*checkAsCallback*/ false, ignoreReturnTypes, /*reportErrors*/ false,
/*errorReporter*/ undefined, compareTypesAssignable) !== Ternary.False;
}
type ErrorReporter = (message: DiagnosticMessage, arg0?: string, arg1?: string) => void;
/**
* See signatureRelatedTo, compareSignaturesIdentical
*/
function compareSignaturesRelated(source: Signature,
target: Signature,
checkAsCallback: boolean,
ignoreReturnTypes: boolean,
reportErrors: boolean,
errorReporter: ErrorReporter,
compareTypes: (s: Type, t: Type, reportErrors?: boolean) => Ternary): Ternary {
// TODO (drosen): De-duplicate code between related functions.
if (source === target) {
return Ternary.True;
}
if (!target.hasRestParameter && source.minArgumentCount > target.parameters.length) {
return Ternary.False;
}
// Spec 1.0 Section 3.8.3 & 3.8.4:
// M and N (the signatures) are instantiated using type Any as the type argument for all type parameters declared by M and N
source = getErasedSignature(source);
target = getErasedSignature(target);
let result = Ternary.True;
const sourceThisType = getThisTypeOfSignature(source);
if (sourceThisType && sourceThisType !== voidType) {
const targetThisType = getThisTypeOfSignature(target);
if (targetThisType) {
// void sources are assignable to anything.
const related = compareTypes(sourceThisType, targetThisType, /*reportErrors*/ false)
|| compareTypes(targetThisType, sourceThisType, reportErrors);
if (!related) {
if (reportErrors) {
errorReporter(Diagnostics.The_this_types_of_each_signature_are_incompatible);
}
return Ternary.False;
}
result &= related;
}
}
const sourceMax = getNumNonRestParameters(source);
const targetMax = getNumNonRestParameters(target);
const checkCount = getNumParametersToCheckForSignatureRelatability(source, sourceMax, target, targetMax);
const sourceParams = source.parameters;
const targetParams = target.parameters;
for (let i = 0; i < checkCount; i++) {
const sourceType = i < sourceMax ? getTypeOfParameter(sourceParams[i]) : getRestTypeOfSignature(source);
const targetType = i < targetMax ? getTypeOfParameter(targetParams[i]) : getRestTypeOfSignature(target);
const sourceSig = getSingleCallSignature(getNonNullableType(sourceType));
const targetSig = getSingleCallSignature(getNonNullableType(targetType));
// In order to ensure that any generic type Foo<T> is at least co-variant with respect to T no matter
// how Foo uses T, we need to relate parameters bi-variantly (given that parameters are input positions,
// they naturally relate only contra-variantly). However, if the source and target parameters both have
// function types with a single call signature, we known we are relating two callback parameters. In
// that case it is sufficient to only relate the parameters of the signatures co-variantly because,
// similar to return values, callback parameters are output positions. This means that a Promise<T>,
// where T is used only in callback parameter positions, will be co-variant (as opposed to bi-variant)
// with respect to T.
const callbacks = sourceSig && targetSig && !sourceSig.typePredicate && !targetSig.typePredicate &&
(getFalsyFlags(sourceType) & TypeFlags.Nullable) === (getFalsyFlags(targetType) & TypeFlags.Nullable);
const related = callbacks ?
compareSignaturesRelated(targetSig, sourceSig, /*checkAsCallback*/ true, /*ignoreReturnTypes*/ false, reportErrors, errorReporter, compareTypes) :
!checkAsCallback && compareTypes(sourceType, targetType, /*reportErrors*/ false) || compareTypes(targetType, sourceType, reportErrors);
if (!related) {
if (reportErrors) {
errorReporter(Diagnostics.Types_of_parameters_0_and_1_are_incompatible,
sourceParams[i < sourceMax ? i : sourceMax].name,
targetParams[i < targetMax ? i : targetMax].name);
}
return Ternary.False;
}
result &= related;
}
if (!ignoreReturnTypes) {
const targetReturnType = getReturnTypeOfSignature(target);
if (targetReturnType === voidType) {
return result;
}
const sourceReturnType = getReturnTypeOfSignature(source);
// The following block preserves behavior forbidding boolean returning functions from being assignable to type guard returning functions
if (target.typePredicate) {
if (source.typePredicate) {
result &= compareTypePredicateRelatedTo(source.typePredicate, target.typePredicate, reportErrors, errorReporter, compareTypes);
}
else if (isIdentifierTypePredicate(target.typePredicate)) {
if (reportErrors) {
errorReporter(Diagnostics.Signature_0_must_have_a_type_predicate, signatureToString(source));
}
return Ternary.False;
}
}
else {
// When relating callback signatures, we still need to relate return types bi-variantly as otherwise
// the containing type wouldn't be co-variant. For example, interface Foo<T> { add(cb: () => T): void }
// wouldn't be co-variant for T without this rule.
result &= checkAsCallback && compareTypes(targetReturnType, sourceReturnType, /*reportErrors*/ false) ||
compareTypes(sourceReturnType, targetReturnType, reportErrors);
}
}
return result;
}
function compareTypePredicateRelatedTo(source: TypePredicate,
target: TypePredicate,
reportErrors: boolean,
errorReporter: ErrorReporter,
compareTypes: (s: Type, t: Type, reportErrors?: boolean) => Ternary): Ternary {
if (source.kind !== target.kind) {
if (reportErrors) {
errorReporter(Diagnostics.A_this_based_type_guard_is_not_compatible_with_a_parameter_based_type_guard);
errorReporter(Diagnostics.Type_predicate_0_is_not_assignable_to_1, typePredicateToString(source), typePredicateToString(target));
}
return Ternary.False;
}
if (source.kind === TypePredicateKind.Identifier) {
const sourceIdentifierPredicate = source as IdentifierTypePredicate;
const targetIdentifierPredicate = target as IdentifierTypePredicate;
if (sourceIdentifierPredicate.parameterIndex !== targetIdentifierPredicate.parameterIndex) {
if (reportErrors) {
errorReporter(Diagnostics.Parameter_0_is_not_in_the_same_position_as_parameter_1, sourceIdentifierPredicate.parameterName, targetIdentifierPredicate.parameterName);
errorReporter(Diagnostics.Type_predicate_0_is_not_assignable_to_1, typePredicateToString(source), typePredicateToString(target));
}
return Ternary.False;
}
}
const related = compareTypes(source.type, target.type, reportErrors);
if (related === Ternary.False && reportErrors) {
errorReporter(Diagnostics.Type_predicate_0_is_not_assignable_to_1, typePredicateToString(source), typePredicateToString(target));
}
return related;
}
function isImplementationCompatibleWithOverload(implementation: Signature, overload: Signature): boolean {
const erasedSource = getErasedSignature(implementation);
const erasedTarget = getErasedSignature(overload);
// First see if the return types are compatible in either direction.
const sourceReturnType = getReturnTypeOfSignature(erasedSource);
const targetReturnType = getReturnTypeOfSignature(erasedTarget);
if (targetReturnType === voidType
|| isTypeRelatedTo(targetReturnType, sourceReturnType, assignableRelation)
|| isTypeRelatedTo(sourceReturnType, targetReturnType, assignableRelation)) {
return isSignatureAssignableTo(erasedSource, erasedTarget, /*ignoreReturnTypes*/ true);
}
return false;
}
function getNumNonRestParameters(signature: Signature) {
const numParams = signature.parameters.length;
return signature.hasRestParameter ?
numParams - 1 :
numParams;
}
function getNumParametersToCheckForSignatureRelatability(source: Signature, sourceNonRestParamCount: number, target: Signature, targetNonRestParamCount: number) {
if (source.hasRestParameter === target.hasRestParameter) {
if (source.hasRestParameter) {
// If both have rest parameters, get the max and add 1 to
// compensate for the rest parameter.
return Math.max(sourceNonRestParamCount, targetNonRestParamCount) + 1;
}
else {
return Math.min(sourceNonRestParamCount, targetNonRestParamCount);
}
}
else {
// Return the count for whichever signature doesn't have rest parameters.
return source.hasRestParameter ?
targetNonRestParamCount :
sourceNonRestParamCount;
}
}
function isEmptyResolvedType(t: ResolvedType) {
return t.properties.length === 0 &&
t.callSignatures.length === 0 &&
t.constructSignatures.length === 0 &&
!t.stringIndexInfo &&
!t.numberIndexInfo;
}
function isEmptyObjectType(type: Type): boolean {
return type.flags & TypeFlags.Object ? isEmptyResolvedType(resolveStructuredTypeMembers(<ObjectType>type)) :
type.flags & TypeFlags.Union ? forEach((<UnionType>type).types, isEmptyObjectType) :
type.flags & TypeFlags.Intersection ? !forEach((<UnionType>type).types, t => !isEmptyObjectType(t)) :
false;
}
function isEnumTypeRelatedTo(sourceSymbol: Symbol, targetSymbol: Symbol, errorReporter?: ErrorReporter) {
if (sourceSymbol === targetSymbol) {
return true;
}
const id = getSymbolId(sourceSymbol) + "," + getSymbolId(targetSymbol);
const relation = enumRelation.get(id);
if (relation !== undefined) {
return relation;
}
if (sourceSymbol.name !== targetSymbol.name || !(sourceSymbol.flags & SymbolFlags.RegularEnum) || !(targetSymbol.flags & SymbolFlags.RegularEnum)) {
enumRelation.set(id, false);
return false;
}
const targetEnumType = getTypeOfSymbol(targetSymbol);
for (const property of getPropertiesOfType(getTypeOfSymbol(sourceSymbol))) {
if (property.flags & SymbolFlags.EnumMember) {
const targetProperty = getPropertyOfType(targetEnumType, property.name);
if (!targetProperty || !(targetProperty.flags & SymbolFlags.EnumMember)) {
if (errorReporter) {
errorReporter(Diagnostics.Property_0_is_missing_in_type_1, property.name,
typeToString(getDeclaredTypeOfSymbol(targetSymbol), /*enclosingDeclaration*/ undefined, TypeFormatFlags.UseFullyQualifiedType));
}
enumRelation.set(id, false);
return false;
}
}
}
enumRelation.set(id, true);
return true;
}
function isSimpleTypeRelatedTo(source: Type, target: Type, relation: Map<RelationComparisonResult>, errorReporter?: ErrorReporter) {
const s = source.flags;
const t = target.flags;
if (t & TypeFlags.Never) return false;
if (t & TypeFlags.Any || s & TypeFlags.Never) return true;
if (s & TypeFlags.StringLike && t & TypeFlags.String) return true;
if (s & TypeFlags.StringLiteral && s & TypeFlags.EnumLiteral &&
t & TypeFlags.StringLiteral && !(t & TypeFlags.EnumLiteral) &&
(<LiteralType>source).value === (<LiteralType>target).value) return true;
if (s & TypeFlags.NumberLike && t & TypeFlags.Number) return true;
if (s & TypeFlags.NumberLiteral && s & TypeFlags.EnumLiteral &&
t & TypeFlags.NumberLiteral && !(t & TypeFlags.EnumLiteral) &&
(<LiteralType>source).value === (<LiteralType>target).value) return true;
if (s & TypeFlags.BooleanLike && t & TypeFlags.Boolean) return true;
if (s & TypeFlags.Enum && t & TypeFlags.Enum && isEnumTypeRelatedTo(source.symbol, target.symbol, errorReporter)) return true;
if (s & TypeFlags.EnumLiteral && t & TypeFlags.EnumLiteral) {
if (s & TypeFlags.Union && t & TypeFlags.Union && isEnumTypeRelatedTo(source.symbol, target.symbol, errorReporter)) return true;
if (s & TypeFlags.Literal && t & TypeFlags.Literal &&
(<LiteralType>source).value === (<LiteralType>target).value &&
isEnumTypeRelatedTo(getParentOfSymbol(source.symbol), getParentOfSymbol(target.symbol), errorReporter)) return true;
}
if (s & TypeFlags.Undefined && (!strictNullChecks || t & (TypeFlags.Undefined | TypeFlags.Void))) return true;
if (s & TypeFlags.Null && (!strictNullChecks || t & TypeFlags.Null)) return true;
if (s & TypeFlags.Object && t & TypeFlags.NonPrimitive) return true;
if (relation === assignableRelation || relation === comparableRelation) {
if (s & TypeFlags.Any) return true;
// Type number or any numeric literal type is assignable to any numeric enum type or any
// numeric enum literal type. This rule exists for backwards compatibility reasons because
// bit-flag enum types sometimes look like literal enum types with numeric literal values.
if (s & (TypeFlags.Number | TypeFlags.NumberLiteral) && !(s & TypeFlags.EnumLiteral) && (
t & TypeFlags.Enum || t & TypeFlags.NumberLiteral && t & TypeFlags.EnumLiteral)) return true;
}
return false;
}
function isTypeRelatedTo(source: Type, target: Type, relation: Map<RelationComparisonResult>) {
if (source.flags & TypeFlags.StringOrNumberLiteral && source.flags & TypeFlags.FreshLiteral) {
source = (<LiteralType>source).regularType;
}
if (target.flags & TypeFlags.StringOrNumberLiteral && target.flags & TypeFlags.FreshLiteral) {
target = (<LiteralType>target).regularType;
}
if (source === target || relation !== identityRelation && isSimpleTypeRelatedTo(source, target, relation)) {
return true;
}
if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Object) {
const id = relation !== identityRelation || source.id < target.id ? source.id + "," + target.id : target.id + "," + source.id;
const related = relation.get(id);
if (related !== undefined) {
return related === RelationComparisonResult.Succeeded;
}
}
if (source.flags & TypeFlags.StructuredOrTypeVariable || target.flags & TypeFlags.StructuredOrTypeVariable) {
return checkTypeRelatedTo(source, target, relation, /*errorNode*/ undefined);
}
return false;
}
/**
* Checks if 'source' is related to 'target' (e.g.: is a assignable to).
* @param source The left-hand-side of the relation.
* @param target The right-hand-side of the relation.
* @param relation The relation considered. One of 'identityRelation', 'subtypeRelation', 'assignableRelation', or 'comparableRelation'.
* Used as both to determine which checks are performed and as a cache of previously computed results.
* @param errorNode The suggested node upon which all errors will be reported, if defined. This may or may not be the actual node used.
* @param headMessage If the error chain should be prepended by a head message, then headMessage will be used.
* @param containingMessageChain A chain of errors to prepend any new errors found.
*/
function checkTypeRelatedTo(
source: Type,
target: Type,
relation: Map<RelationComparisonResult>,
errorNode: Node,
headMessage?: DiagnosticMessage,
containingMessageChain?: DiagnosticMessageChain): boolean {
let errorInfo: DiagnosticMessageChain;
let sourceStack: Type[];
let targetStack: Type[];
let maybeStack: Map<RelationComparisonResult>[];
let expandingFlags: number;
let depth = 0;
let overflow = false;
let disableWeakTypeErrors = false;
Debug.assert(relation !== identityRelation || !errorNode, "no error reporting in identity checking");
const result = isRelatedTo(source, target, /*reportErrors*/ !!errorNode, headMessage);
if (overflow) {
error(errorNode, Diagnostics.Excessive_stack_depth_comparing_types_0_and_1, typeToString(source), typeToString(target));
}
else if (errorInfo) {
if (containingMessageChain) {
errorInfo = concatenateDiagnosticMessageChains(containingMessageChain, errorInfo);
}
diagnostics.add(createDiagnosticForNodeFromMessageChain(errorNode, errorInfo));
}
return result !== Ternary.False;
function reportError(message: DiagnosticMessage, arg0?: string, arg1?: string, arg2?: string): void {
Debug.assert(!!errorNode);
errorInfo = chainDiagnosticMessages(errorInfo, message, arg0, arg1, arg2);
}
function reportRelationError(message: DiagnosticMessage, source: Type, target: Type) {
let sourceType = typeToString(source);
let targetType = typeToString(target);
if (sourceType === targetType) {
sourceType = typeToString(source, /*enclosingDeclaration*/ undefined, TypeFormatFlags.UseFullyQualifiedType);
targetType = typeToString(target, /*enclosingDeclaration*/ undefined, TypeFormatFlags.UseFullyQualifiedType);
}
if (!message) {
if (relation === comparableRelation) {
message = Diagnostics.Type_0_is_not_comparable_to_type_1;
}
else if (sourceType === targetType) {
message = Diagnostics.Type_0_is_not_assignable_to_type_1_Two_different_types_with_this_name_exist_but_they_are_unrelated;
}
else {
message = Diagnostics.Type_0_is_not_assignable_to_type_1;
}
}
reportError(message, sourceType, targetType);
}
function tryElaborateErrorsForPrimitivesAndObjects(source: Type, target: Type) {
const sourceType = typeToString(source);
const targetType = typeToString(target);
if ((globalStringType === source && stringType === target) ||
(globalNumberType === source && numberType === target) ||
(globalBooleanType === source && booleanType === target) ||
(getGlobalESSymbolType(/*reportErrors*/ false) === source && esSymbolType === target)) {
reportError(Diagnostics._0_is_a_primitive_but_1_is_a_wrapper_object_Prefer_using_0_when_possible, targetType, sourceType);
}
}
function isUnionOrIntersectionTypeWithoutNullableConstituents(type: Type): boolean {
if (!(type.flags & TypeFlags.UnionOrIntersection)) {
return false;
}
// at this point we know that this is union or intersection type possibly with nullable constituents.
// check if we still will have compound type if we ignore nullable components.
let seenNonNullable = false;
for (const t of (<UnionOrIntersectionType>type).types) {
if (t.flags & TypeFlags.Nullable) {
continue;
}
if (seenNonNullable) {
return true;
}
seenNonNullable = true;
}
return false;
}
/**
* Compare two types and return
* * Ternary.True if they are related with no assumptions,
* * Ternary.Maybe if they are related with assumptions of other relationships, or
* * Ternary.False if they are not related.
*/
function isRelatedTo(source: Type, target: Type, reportErrors?: boolean, headMessage?: DiagnosticMessage): Ternary {
let result: Ternary;
if (source.flags & TypeFlags.StringOrNumberLiteral && source.flags & TypeFlags.FreshLiteral) {
source = (<LiteralType>source).regularType;
}
if (target.flags & TypeFlags.StringOrNumberLiteral && target.flags & TypeFlags.FreshLiteral) {
target = (<LiteralType>target).regularType;
}
// both types are the same - covers 'they are the same primitive type or both are Any' or the same type parameter cases
if (source === target) return Ternary.True;
if (relation === identityRelation) {
return isIdenticalTo(source, target);
}
if (isSimpleTypeRelatedTo(source, target, relation, reportErrors ? reportError : undefined)) return Ternary.True;
if (getObjectFlags(source) & ObjectFlags.ObjectLiteral && source.flags & TypeFlags.FreshLiteral) {
if (hasExcessProperties(<FreshObjectLiteralType>source, target, reportErrors)) {
if (reportErrors) {
reportRelationError(headMessage, source, target);
}
return Ternary.False;
}
// Above we check for excess properties with respect to the entire target type. When union
// and intersection types are further deconstructed on the target side, we don't want to
// make the check again (as it might fail for a partial target type). Therefore we obtain
// the regular source type and proceed with that.
if (isUnionOrIntersectionTypeWithoutNullableConstituents(target)) {
source = getRegularTypeOfObjectLiteral(source);
}
}
const saveErrorInfo = errorInfo;
// Note that these checks are specifically ordered to produce correct results. In particular,
// we need to deconstruct unions before intersections (because unions are always at the top),
// and we need to handle "each" relations before "some" relations for the same kind of type.
if (source.flags & TypeFlags.Union) {
if (relation === comparableRelation) {
result = someTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive));
}
else {
result = eachTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive));
}
if (result) {
return result;
}
}
else {
if (target.flags & TypeFlags.Union) {
if (result = typeRelatedToSomeType(source, <UnionType>target, reportErrors && !(source.flags & TypeFlags.Primitive) && !(target.flags & TypeFlags.Primitive))) {
return result;
}
}
else if (target.flags & TypeFlags.Intersection) {
if (result = typeRelatedToEachType(source, target as IntersectionType, reportErrors)) {
return result;
}
}
else if (source.flags & TypeFlags.Intersection) {
// Check to see if any constituents of the intersection are immediately related to the target.
//
// Don't report errors though. Checking whether a constituent is related to the source is not actually
// useful and leads to some confusing error messages. Instead it is better to let the below checks
// take care of this, or to not elaborate at all. For instance,
//
// - For an object type (such as 'C = A & B'), users are usually more interested in structural errors.
//
// - For a union type (such as '(A | B) = (C & D)'), it's better to hold onto the whole intersection
// than to report that 'D' is not assignable to 'A' or 'B'.
//
// - For a primitive type or type parameter (such as 'number = A & B') there is no point in
// breaking the intersection apart.
if (result = someTypeRelatedToType(<IntersectionType>source, target, /*reportErrors*/ false)) {
return result;
}
}
if (source.flags & TypeFlags.StructuredOrTypeVariable || target.flags & TypeFlags.StructuredOrTypeVariable) {
if (result = recursiveTypeRelatedTo(source, target, reportErrors)) {
errorInfo = saveErrorInfo;
return result;
}
}
}
if (reportErrors) {
if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Primitive) {
tryElaborateErrorsForPrimitivesAndObjects(source, target);
}
else if (source.symbol && source.flags & TypeFlags.Object && globalObjectType === source) {
reportError(Diagnostics.The_Object_type_is_assignable_to_very_few_other_types_Did_you_mean_to_use_the_any_type_instead);
}
reportRelationError(headMessage, source, target);
}
return Ternary.False;
}
function isIdenticalTo(source: Type, target: Type): Ternary {
let result: Ternary;
if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Object) {
return recursiveTypeRelatedTo(source, target, /*reportErrors*/ false);
}
if (source.flags & TypeFlags.Union && target.flags & TypeFlags.Union ||
source.flags & TypeFlags.Intersection && target.flags & TypeFlags.Intersection) {
if (result = eachTypeRelatedToSomeType(<UnionOrIntersectionType>source, <UnionOrIntersectionType>target)) {
if (result &= eachTypeRelatedToSomeType(<UnionOrIntersectionType>target, <UnionOrIntersectionType>source)) {
return result;
}
}
}
return Ternary.False;
}
function hasExcessProperties(source: FreshObjectLiteralType, target: Type, reportErrors: boolean): boolean {
if (maybeTypeOfKind(target, TypeFlags.Object) && !(getObjectFlags(target) & ObjectFlags.ObjectLiteralPatternWithComputedProperties)) {
const isComparingJsxAttributes = !!(source.flags & TypeFlags.JsxAttributes);
if ((relation === assignableRelation || relation === comparableRelation) &&
(isTypeSubsetOf(globalObjectType, target) || (!isComparingJsxAttributes && isEmptyObjectType(target)))) {
return false;
}
for (const prop of getPropertiesOfObjectType(source)) {
if (!isKnownProperty(target, prop.name, isComparingJsxAttributes)) {
if (reportErrors) {
// We know *exactly* where things went wrong when comparing the types.
// Use this property as the error node as this will be more helpful in
// reasoning about what went wrong.
Debug.assert(!!errorNode);
if (isJsxAttributes(errorNode) || isJsxOpeningLikeElement(errorNode)) {
// JsxAttributes has an object-literal flag and undergo same type-assignablity check as normal object-literal.
// However, using an object-literal error message will be very confusing to the users so we give different a message.
reportError(Diagnostics.Property_0_does_not_exist_on_type_1, symbolToString(prop), typeToString(target));
}
else {
errorNode = prop.valueDeclaration;
reportError(Diagnostics.Object_literal_may_only_specify_known_properties_and_0_does_not_exist_in_type_1,
symbolToString(prop), typeToString(target));
}
}
return true;
}
}
}
return false;
}
function eachTypeRelatedToSomeType(source: UnionOrIntersectionType, target: UnionOrIntersectionType): Ternary {
let result = Ternary.True;
const sourceTypes = source.types;
for (const sourceType of sourceTypes) {
const related = typeRelatedToSomeType(sourceType, target, /*reportErrors*/ false);
if (!related) {
return Ternary.False;
}
result &= related;
}
return result;
}
function typeRelatedToSomeType(source: Type, target: UnionOrIntersectionType, reportErrors: boolean): Ternary {
const targetTypes = target.types;
if (target.flags & TypeFlags.Union && containsType(targetTypes, source)) {
return Ternary.True;
}
for (const type of targetTypes) {
const related = isRelatedTo(source, type, /*reportErrors*/ false);
if (related) {
return related;
}
}
if (reportErrors) {
const discriminantType = findMatchingDiscriminantType(source, target);
isRelatedTo(source, discriminantType || targetTypes[targetTypes.length - 1], /*reportErrors*/ true);
}
return Ternary.False;
}
function findMatchingDiscriminantType(source: Type, target: UnionOrIntersectionType) {
const sourceProperties = getPropertiesOfObjectType(source);
if (sourceProperties) {
for (const sourceProperty of sourceProperties) {
if (isDiscriminantProperty(target, sourceProperty.name)) {
const sourceType = getTypeOfSymbol(sourceProperty);
for (const type of target.types) {
const targetType = getTypeOfPropertyOfType(type, sourceProperty.name);
if (targetType && isRelatedTo(sourceType, targetType)) {
return type;
}
}
}
}
}
}
function typeRelatedToEachType(source: Type, target: IntersectionType, reportErrors: boolean): Ternary {
let result = Ternary.True;
const targetTypes = target.types;
const saveDisableWeakTypeErrors = disableWeakTypeErrors;
disableWeakTypeErrors = true;
for (const targetType of targetTypes) {
const related = isRelatedTo(source, targetType, reportErrors);
if (!related) {
disableWeakTypeErrors = saveDisableWeakTypeErrors;
return Ternary.False;
}
result &= related;
}
disableWeakTypeErrors = saveDisableWeakTypeErrors;
return reportAssignmentToWeakIntersection(source, target, reportErrors) ? Ternary.False : result;
}
function reportAssignmentToWeakIntersection(source: Type, target: IntersectionType, reportErrors: boolean) {
const needsWeakTypeCheck = source !== globalObjectType && getPropertiesOfType(source).length > 0 && every(target.types, isWeak);
if (!needsWeakTypeCheck) {
return false;
}
const hasSharedProperty = forEach(
getPropertiesOfType(source),
p => isKnownProperty(target, p.name, /*isComparingJsxAttributes*/ false));
if (!hasSharedProperty && reportErrors) {
reportError(Diagnostics.Weak_type_0_has_no_properties_in_common_with_1, typeToString(target), typeToString(source));
}
return !hasSharedProperty;
}
function someTypeRelatedToType(source: UnionOrIntersectionType, target: Type, reportErrors: boolean): Ternary {
const sourceTypes = source.types;
if (source.flags & TypeFlags.Union && containsType(sourceTypes, target)) {
return Ternary.True;
}
const len = sourceTypes.length;
for (let i = 0; i < len; i++) {
const related = isRelatedTo(sourceTypes[i], target, reportErrors && i === len - 1);
if (related) {
return related;
}
}
return Ternary.False;
}
function eachTypeRelatedToType(source: UnionOrIntersectionType, target: Type, reportErrors: boolean): Ternary {
let result = Ternary.True;
const sourceTypes = source.types;
for (const sourceType of sourceTypes) {
const related = isRelatedTo(sourceType, target, reportErrors);
if (!related) {
return Ternary.False;
}
result &= related;
}
return result;
}
function typeArgumentsRelatedTo(source: TypeReference, target: TypeReference, reportErrors: boolean): Ternary {
const sources = source.typeArguments || emptyArray;
const targets = target.typeArguments || emptyArray;
if (sources.length !== targets.length && relation === identityRelation) {
return Ternary.False;
}
const length = sources.length <= targets.length ? sources.length : targets.length;
let result = Ternary.True;
for (let i = 0; i < length; i++) {
const related = isRelatedTo(sources[i], targets[i], reportErrors);
if (!related) {
return Ternary.False;
}
result &= related;
}
return result;
}
// Determine if possibly recursive types are related. 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 recursiveTypeRelatedTo(source: Type, target: Type, reportErrors: boolean): Ternary {
if (overflow) {
return Ternary.False;
}
const id = relation !== identityRelation || source.id < target.id ? source.id + "," + target.id : target.id + "," + source.id;
const related = relation.get(id);
if (related !== undefined) {
if (reportErrors && related === RelationComparisonResult.Failed) {
// We are elaborating errors and the cached result is an unreported failure. Record the result as a reported
// failure and continue computing the relation such that errors get reported.
relation.set(id, RelationComparisonResult.FailedAndReported);
}
else {
return related === RelationComparisonResult.Succeeded ? Ternary.True : Ternary.False;
}
}
if (depth > 0) {
for (let i = 0; i < depth; i++) {
// If source and target are already being compared, consider them related with assumptions
if (maybeStack[i].get(id)) {
return Ternary.Maybe;
}
}
if (depth === 100) {
overflow = true;
return Ternary.False;
}
}
else {
sourceStack = [];
targetStack = [];
maybeStack = [];
expandingFlags = 0;
}
sourceStack[depth] = source;
targetStack[depth] = target;
maybeStack[depth] = createMap<RelationComparisonResult>();
maybeStack[depth].set(id, RelationComparisonResult.Succeeded);
depth++;
const saveExpandingFlags = expandingFlags;
if (!(expandingFlags & 1) && isDeeplyNestedType(source, sourceStack, depth)) expandingFlags |= 1;
if (!(expandingFlags & 2) && isDeeplyNestedType(target, targetStack, depth)) expandingFlags |= 2;
const result = expandingFlags !== 3 ? structuredTypeRelatedTo(source, target, reportErrors) : Ternary.Maybe;
expandingFlags = saveExpandingFlags;
depth--;
if (result) {
const maybeCache = maybeStack[depth];
// If result is definitely true, copy assumptions to global cache, else copy to next level up
const destinationCache = (result === Ternary.True || depth === 0) ? relation : maybeStack[depth - 1];
copyEntries(maybeCache, destinationCache);
}
else {
// A false result goes straight into global cache (when something is false under assumptions it
// will also be false without assumptions)
relation.set(id, reportErrors ? RelationComparisonResult.FailedAndReported : RelationComparisonResult.Failed);
}
return result;
}
function structuredTypeRelatedTo(source: Type, target: Type, reportErrors: boolean): Ternary {
let result: Ternary;
const saveErrorInfo = errorInfo;
if (target.flags & TypeFlags.TypeParameter) {
// A source type { [P in keyof T]: X } is related to a target type T if X is related to T[P].
if (getObjectFlags(source) & ObjectFlags.Mapped && getConstraintTypeFromMappedType(<MappedType>source) === getIndexType(target)) {
if (!(<MappedType>source).declaration.questionToken) {
const templateType = getTemplateTypeFromMappedType(<MappedType>source);
const indexedAccessType = getIndexedAccessType(target, getTypeParameterFromMappedType(<MappedType>source));
if (result = isRelatedTo(templateType, indexedAccessType, reportErrors)) {
return result;
}
}
}
}
else if (target.flags & TypeFlags.Index) {
// A keyof S is related to a keyof T if T is related to S.
if (source.flags & TypeFlags.Index) {
if (result = isRelatedTo((<IndexType>target).type, (<IndexType>source).type, /*reportErrors*/ false)) {
return result;
}
}
// A type S is assignable to keyof T if S is assignable to keyof C, where C is the
// constraint of T.
const constraint = getConstraintOfType((<IndexType>target).type);
if (constraint) {
if (result = isRelatedTo(source, getIndexType(constraint), reportErrors)) {
return result;
}
}
}
else if (target.flags & TypeFlags.IndexedAccess) {
// A type S is related to a type T[K] if S is related to A[K], where K is string-like and
// A is the apparent type of S.
const constraint = getConstraintOfType(<IndexedAccessType>target);
if (constraint) {
if (result = isRelatedTo(source, constraint, reportErrors)) {
errorInfo = saveErrorInfo;
return result;
}
}
}
if (source.flags & TypeFlags.TypeParameter) {
// A source type T is related to a target type { [P in keyof T]: X } if T[P] is related to X.
if (getObjectFlags(target) & ObjectFlags.Mapped && getConstraintTypeFromMappedType(<MappedType>target) === getIndexType(source)) {
const indexedAccessType = getIndexedAccessType(source, getTypeParameterFromMappedType(<MappedType>target));
const templateType = getTemplateTypeFromMappedType(<MappedType>target);
if (result = isRelatedTo(indexedAccessType, templateType, reportErrors)) {
errorInfo = saveErrorInfo;
return result;
}
}
else {
let constraint = getConstraintOfTypeParameter(<TypeParameter>source);
// A type parameter with no constraint is not related to the non-primitive object type.
if (constraint || !(target.flags & TypeFlags.NonPrimitive)) {
if (!constraint || constraint.flags & TypeFlags.Any) {
constraint = emptyObjectType;
}
// The constraint may need to be further instantiated with its 'this' type.
constraint = getTypeWithThisArgument(constraint, source);
// Report constraint errors only if the constraint is not the empty object type
const reportConstraintErrors = reportErrors && constraint !== emptyObjectType;
if (result = isRelatedTo(constraint, target, reportConstraintErrors)) {
errorInfo = saveErrorInfo;
return result;
}
}
}
}
else if (source.flags & TypeFlags.IndexedAccess) {
// A type S[K] is related to a type T if A[K] is related to T, where K is string-like and
// A is the apparent type of S.
const constraint = getConstraintOfType(<IndexedAccessType>source);
if (constraint) {
if (result = isRelatedTo(constraint, target, reportErrors)) {
errorInfo = saveErrorInfo;
return result;
}
}
else if (target.flags & TypeFlags.IndexedAccess && (<IndexedAccessType>source).indexType === (<IndexedAccessType>target).indexType) {
// if we have indexed access types with identical index types, see if relationship holds for
// the two object types.
if (result = isRelatedTo((<IndexedAccessType>source).objectType, (<IndexedAccessType>target).objectType, reportErrors)) {
return result;
}
}
}
else {
if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (<TypeReference>source).target === (<TypeReference>target).target) {
// We have type references to same target type, see if relationship holds for all type arguments
if (result = typeArgumentsRelatedTo(<TypeReference>source, <TypeReference>target, reportErrors)) {
return result;
}
}
// Even if relationship doesn't hold for unions, intersections, or generic type references,
// it may hold in a structural comparison.
const sourceIsPrimitive = !!(source.flags & TypeFlags.Primitive);
if (relation !== identityRelation) {
source = getApparentType(source);
}
// In a check of the form X = A & B, we will have previously checked if A relates to X or B relates
// to X. Failing both of those we want to check if the aggregation of A and B's members structurally
// relates to X. Thus, we include intersection types on the source side here.
if (source.flags & (TypeFlags.Object | TypeFlags.Intersection) && target.flags & TypeFlags.Object) {
// Report structural errors only if we haven't reported any errors yet
const reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo && !sourceIsPrimitive;
if (isGenericMappedType(source) || isGenericMappedType(target)) {
result = mappedTypeRelatedTo(source, target, reportStructuralErrors);
}
else {
result = propertiesRelatedTo(source, target, reportStructuralErrors);
if (result) {
result &= signaturesRelatedTo(source, target, SignatureKind.Call, reportStructuralErrors);
if (result) {
result &= signaturesRelatedTo(source, target, SignatureKind.Construct, reportStructuralErrors);
if (result) {
result &= indexTypesRelatedTo(source, target, IndexKind.String, sourceIsPrimitive, reportStructuralErrors);
if (result) {
result &= indexTypesRelatedTo(source, target, IndexKind.Number, sourceIsPrimitive, reportStructuralErrors);
}
}
}
}
}
if (result) {
errorInfo = saveErrorInfo;
return result;
}
}
}
return Ternary.False;
}
// A type [P in S]: X is related to a type [Q in T]: Y if T is related to S and X' is
// related to Y, where X' is an instantiation of X in which P is replaced with Q. Notice
// that S and T are contra-variant whereas X and Y are co-variant.
function mappedTypeRelatedTo(source: Type, target: Type, reportErrors: boolean): Ternary {
if (isGenericMappedType(target)) {
if (isGenericMappedType(source)) {
const sourceReadonly = !!(<MappedType>source).declaration.readonlyToken;
const sourceOptional = !!(<MappedType>source).declaration.questionToken;
const targetReadonly = !!(<MappedType>target).declaration.readonlyToken;
const targetOptional = !!(<MappedType>target).declaration.questionToken;
const modifiersRelated = relation === identityRelation ?
sourceReadonly === targetReadonly && sourceOptional === targetOptional :
relation === comparableRelation || !sourceOptional || targetOptional;
if (modifiersRelated) {
let result: Ternary;
if (result = isRelatedTo(getConstraintTypeFromMappedType(<MappedType>target), getConstraintTypeFromMappedType(<MappedType>source), reportErrors)) {
const mapper = createTypeMapper([getTypeParameterFromMappedType(<MappedType>source)], [getTypeParameterFromMappedType(<MappedType>target)]);
return result & isRelatedTo(instantiateType(getTemplateTypeFromMappedType(<MappedType>source), mapper), getTemplateTypeFromMappedType(<MappedType>target), reportErrors);
}
}
}
else if ((<MappedType>target).declaration.questionToken && isEmptyObjectType(source)) {
return Ternary.True;
}
}
else if (relation !== identityRelation) {
const resolved = resolveStructuredTypeMembers(<ObjectType>target);
if (isEmptyResolvedType(resolved) || resolved.stringIndexInfo && resolved.stringIndexInfo.type.flags & TypeFlags.Any) {
return Ternary.True;
}
}
return Ternary.False;
}
function propertiesRelatedTo(source: Type, target: Type, reportErrors: boolean): Ternary {
if (relation === identityRelation) {
return propertiesIdenticalTo(source, target);
}
let result = Ternary.True;
const properties = getPropertiesOfObjectType(target);
const requireOptionalProperties = relation === subtypeRelation && !(getObjectFlags(source) & ObjectFlags.ObjectLiteral);
let foundMatchingProperty = !isWeak(target);
for (const targetProp of properties) {
const sourceProp = getPropertyOfType(source, targetProp.name);
if (sourceProp) {
foundMatchingProperty = true;
}
if (sourceProp !== targetProp) {
if (!sourceProp) {
if (!(targetProp.flags & SymbolFlags.Optional) || requireOptionalProperties) {
if (reportErrors) {
reportError(Diagnostics.Property_0_is_missing_in_type_1, symbolToString(targetProp), typeToString(source));
}
return Ternary.False;
}
}
else if (!(targetProp.flags & SymbolFlags.Prototype)) {
const sourcePropFlags = getDeclarationModifierFlagsFromSymbol(sourceProp);
const targetPropFlags = getDeclarationModifierFlagsFromSymbol(targetProp);
if (sourcePropFlags & ModifierFlags.Private || targetPropFlags & ModifierFlags.Private) {
if (getCheckFlags(sourceProp) & CheckFlags.ContainsPrivate) {
if (reportErrors) {
reportError(Diagnostics.Property_0_has_conflicting_declarations_and_is_inaccessible_in_type_1, symbolToString(sourceProp), typeToString(source));
}
return Ternary.False;
}
if (sourceProp.valueDeclaration !== targetProp.valueDeclaration) {
if (reportErrors) {
if (sourcePropFlags & ModifierFlags.Private && targetPropFlags & ModifierFlags.Private) {
reportError(Diagnostics.Types_have_separate_declarations_of_a_private_property_0, symbolToString(targetProp));
}
else {
reportError(Diagnostics.Property_0_is_private_in_type_1_but_not_in_type_2, symbolToString(targetProp),
typeToString(sourcePropFlags & ModifierFlags.Private ? source : target),
typeToString(sourcePropFlags & ModifierFlags.Private ? target : source));
}
}
return Ternary.False;
}
}
else if (targetPropFlags & ModifierFlags.Protected) {
if (!isValidOverrideOf(sourceProp, targetProp)) {
if (reportErrors) {
reportError(Diagnostics.Property_0_is_protected_but_type_1_is_not_a_class_derived_from_2, symbolToString(targetProp),
typeToString(getDeclaringClass(sourceProp) || source), typeToString(getDeclaringClass(targetProp) || target));
}
return Ternary.False;
}
}
else if (sourcePropFlags & ModifierFlags.Protected) {
if (reportErrors) {
reportError(Diagnostics.Property_0_is_protected_in_type_1_but_public_in_type_2,
symbolToString(targetProp), typeToString(source), typeToString(target));
}
return Ternary.False;
}
const saveDisableWeakTypeErrors = disableWeakTypeErrors;
disableWeakTypeErrors = false;
const related = isRelatedTo(getTypeOfSymbol(sourceProp), getTypeOfSymbol(targetProp), reportErrors);
disableWeakTypeErrors = saveDisableWeakTypeErrors;
if (!related) {
if (reportErrors) {
reportError(Diagnostics.Types_of_property_0_are_incompatible, symbolToString(targetProp));
}
return Ternary.False;
}
result &= related;
// When checking for comparability, be more lenient with optional properties.
if (relation !== comparableRelation && sourceProp.flags & SymbolFlags.Optional && !(targetProp.flags & SymbolFlags.Optional)) {
// TypeScript 1.0 spec (April 2014): 3.8.3
// S is a subtype of a type T, and T is a supertype of S if ...
// S' and T are object types and, for each member M in T..
// M is a property and S' contains a property N where
// if M is a required property, N is also a required property
// (M - property in T)
// (N - property in S)
if (reportErrors) {
reportError(Diagnostics.Property_0_is_optional_in_type_1_but_required_in_type_2,
symbolToString(targetProp), typeToString(source), typeToString(target));
}
return Ternary.False;
}
}
}
}
if (!foundMatchingProperty && !disableWeakTypeErrors && source !== globalObjectType && getPropertiesOfType(source).length > 0) {
if (reportErrors) {
reportError(Diagnostics.Weak_type_0_has_no_properties_in_common_with_1, typeToString(target), typeToString(source));
}
return Ternary.False;
}
return result;
}
/**
* A type is 'weak' if it is an object type with at least one optional property
* and no required properties, call/construct signatures or index signatures
*/
function isWeak(type: Type) {
const props = getPropertiesOfType(type);
return type.flags & TypeFlags.Object &&
props.length > 0 &&
every(props, p => !!(p.flags & SymbolFlags.Optional)) &&
!getSignaturesOfType(type, SignatureKind.Call).length &&
!getSignaturesOfType(type, SignatureKind.Construct).length &&
!getIndexTypeOfType(type, IndexKind.String) &&
!getIndexTypeOfType(type, IndexKind.Number);
}
function propertiesIdenticalTo(source: Type, target: Type): Ternary {
if (!(source.flags & TypeFlags.Object && target.flags & TypeFlags.Object)) {
return Ternary.False;
}
const sourceProperties = getPropertiesOfObjectType(source);
const targetProperties = getPropertiesOfObjectType(target);
if (sourceProperties.length !== targetProperties.length) {
return Ternary.False;
}
let result = Ternary.True;
for (const sourceProp of sourceProperties) {
const targetProp = getPropertyOfObjectType(target, sourceProp.name);
if (!targetProp) {
return Ternary.False;
}
const related = compareProperties(sourceProp, targetProp, isRelatedTo);
if (!related) {
return Ternary.False;
}
result &= related;
}
return result;
}
function signaturesRelatedTo(source: Type, target: Type, kind: SignatureKind, reportErrors: boolean): Ternary {
if (relation === identityRelation) {
return signaturesIdenticalTo(source, target, kind);
}
if (target === anyFunctionType || source === anyFunctionType) {
return Ternary.True;
}
const sourceSignatures = getSignaturesOfType(source, kind);
const targetSignatures = getSignaturesOfType(target, kind);
if (kind === SignatureKind.Construct && sourceSignatures.length && targetSignatures.length) {
if (isAbstractConstructorType(source) && !isAbstractConstructorType(target)) {
// An abstract constructor type is not assignable to a non-abstract constructor type
// as it would otherwise be possible to new an abstract class. Note that the assignability
// check we perform for an extends clause excludes construct signatures from the target,
// so this check never proceeds.
if (reportErrors) {
reportError(Diagnostics.Cannot_assign_an_abstract_constructor_type_to_a_non_abstract_constructor_type);
}
return Ternary.False;
}
if (!constructorVisibilitiesAreCompatible(sourceSignatures[0], targetSignatures[0], reportErrors)) {
return Ternary.False;
}
}
let result = Ternary.True;
const saveErrorInfo = errorInfo;
if (getObjectFlags(source) & ObjectFlags.Instantiated && getObjectFlags(target) & ObjectFlags.Instantiated && source.symbol === target.symbol) {
// We instantiations of the same anonymous type (which typically will be the type of a method).
// Simply do a pairwise comparison of the signatures in the two signature lists instead of the
// much more expensive N * M comparison matrix we explore below.
for (let i = 0; i < targetSignatures.length; i++) {
const related = signatureRelatedTo(sourceSignatures[i], targetSignatures[i], reportErrors);
if (!related) {
return Ternary.False;
}
result &= related;
}
}
else {
outer: for (const t of targetSignatures) {
// Only elaborate errors from the first failure
let shouldElaborateErrors = reportErrors;
for (const s of sourceSignatures) {
const related = signatureRelatedTo(s, t, shouldElaborateErrors);
if (related) {
result &= related;
errorInfo = saveErrorInfo;
continue outer;
}
shouldElaborateErrors = false;
}
if (shouldElaborateErrors) {
reportError(Diagnostics.Type_0_provides_no_match_for_the_signature_1,
typeToString(source),
signatureToString(t, /*enclosingDeclaration*/ undefined, /*flags*/ undefined, kind));
}
return Ternary.False;
}
}
return result;
}
/**
* See signatureAssignableTo, compareSignaturesIdentical
*/
function signatureRelatedTo(source: Signature, target: Signature, reportErrors: boolean): Ternary {
return compareSignaturesRelated(source, target, /*checkAsCallback*/ false, /*ignoreReturnTypes*/ false, reportErrors, reportError, isRelatedTo);
}
function signaturesIdenticalTo(source: Type, target: Type, kind: SignatureKind): Ternary {
const sourceSignatures = getSignaturesOfType(source, kind);
const targetSignatures = getSignaturesOfType(target, kind);
if (sourceSignatures.length !== targetSignatures.length) {
return Ternary.False;
}
let result = Ternary.True;
for (let i = 0; i < sourceSignatures.length; i++) {
const related = compareSignaturesIdentical(sourceSignatures[i], targetSignatures[i], /*partialMatch*/ false, /*ignoreThisTypes*/ false, /*ignoreReturnTypes*/ false, isRelatedTo);
if (!related) {
return Ternary.False;
}
result &= related;
}
return result;
}
function eachPropertyRelatedTo(source: Type, target: Type, kind: IndexKind, reportErrors: boolean): Ternary {
let result = Ternary.True;
for (const prop of getPropertiesOfObjectType(source)) {
if (kind === IndexKind.String || isNumericLiteralName(prop.name)) {
const related = isRelatedTo(getTypeOfSymbol(prop), target, reportErrors);
if (!related) {
if (reportErrors) {
reportError(Diagnostics.Property_0_is_incompatible_with_index_signature, symbolToString(prop));
}
return Ternary.False;
}
result &= related;
}
}
return result;
}
function indexInfoRelatedTo(sourceInfo: IndexInfo, targetInfo: IndexInfo, reportErrors: boolean) {
const related = isRelatedTo(sourceInfo.type, targetInfo.type, reportErrors);
if (!related && reportErrors) {
reportError(Diagnostics.Index_signatures_are_incompatible);
}
return related;
}
function indexTypesRelatedTo(source: Type, target: Type, kind: IndexKind, sourceIsPrimitive: boolean, reportErrors: boolean) {
if (relation === identityRelation) {
return indexTypesIdenticalTo(source, target, kind);
}
const targetInfo = getIndexInfoOfType(target, kind);
if (!targetInfo || targetInfo.type.flags & TypeFlags.Any && !sourceIsPrimitive) {
// Index signature of type any permits assignment from everything but primitives
return Ternary.True;
}
const sourceInfo = getIndexInfoOfType(source, kind) ||
kind === IndexKind.Number && getIndexInfoOfType(source, IndexKind.String);
if (sourceInfo) {
return indexInfoRelatedTo(sourceInfo, targetInfo, reportErrors);
}
if (isObjectLiteralType(source)) {
let related = Ternary.True;
if (kind === IndexKind.String) {
const sourceNumberInfo = getIndexInfoOfType(source, IndexKind.Number);
if (sourceNumberInfo) {
related = indexInfoRelatedTo(sourceNumberInfo, targetInfo, reportErrors);
}
}
if (related) {
related &= eachPropertyRelatedTo(source, targetInfo.type, kind, reportErrors);
}
return related;
}
if (reportErrors) {
reportError(Diagnostics.Index_signature_is_missing_in_type_0, typeToString(source));
}
return Ternary.False;
}
function indexTypesIdenticalTo(source: Type, target: Type, indexKind: IndexKind): Ternary {
const targetInfo = getIndexInfoOfType(target, indexKind);
const sourceInfo = getIndexInfoOfType(source, indexKind);
if (!sourceInfo && !targetInfo) {
return Ternary.True;
}
if (sourceInfo && targetInfo && sourceInfo.isReadonly === targetInfo.isReadonly) {
return isRelatedTo(sourceInfo.type, targetInfo.type);
}
return Ternary.False;
}
function constructorVisibilitiesAreCompatible(sourceSignature: Signature, targetSignature: Signature, reportErrors: boolean) {
if (!sourceSignature.declaration || !targetSignature.declaration) {
return true;
}
const sourceAccessibility = getModifierFlags(sourceSignature.declaration) & ModifierFlags.NonPublicAccessibilityModifier;
const targetAccessibility = getModifierFlags(targetSignature.declaration) & ModifierFlags.NonPublicAccessibilityModifier;
// A public, protected and private signature is assignable to a private signature.
if (targetAccessibility === ModifierFlags.Private) {
return true;
}
// A public and protected signature is assignable to a protected signature.
if (targetAccessibility === ModifierFlags.Protected && sourceAccessibility !== ModifierFlags.Private) {
return true;
}
// Only a public signature is assignable to public signature.
if (targetAccessibility !== ModifierFlags.Protected && !sourceAccessibility) {
return true;
}
if (reportErrors) {
reportError(Diagnostics.Cannot_assign_a_0_constructor_type_to_a_1_constructor_type, visibilityToString(sourceAccessibility), visibilityToString(targetAccessibility));
}
return false;
}
}
// Invoke the callback for each underlying property symbol of the given symbol and return the first
// value that isn't undefined.
function forEachProperty<T>(prop: Symbol, callback: (p: Symbol) => T): T {
if (getCheckFlags(prop) & CheckFlags.Synthetic) {
for (const t of (<TransientSymbol>prop).containingType.types) {
const p = getPropertyOfType(t, prop.name);
const result = p && forEachProperty(p, callback);
if (result) {
return result;
}
}
return undefined;
}
return callback(prop);
}
// Return the declaring class type of a property or undefined if property not declared in class
function getDeclaringClass(prop: Symbol) {
return prop.parent && prop.parent.flags & SymbolFlags.Class ? getDeclaredTypeOfSymbol(getParentOfSymbol(prop)) : undefined;
}
// Return true if some underlying source property is declared in a class that derives
// from the given base class.
function isPropertyInClassDerivedFrom(prop: Symbol, baseClass: Type) {
return forEachProperty(prop, sp => {
const sourceClass = getDeclaringClass(sp);
return sourceClass ? hasBaseType(sourceClass, baseClass) : false;
});
}
// Return true if source property is a valid override of protected parts of target property.
function isValidOverrideOf(sourceProp: Symbol, targetProp: Symbol) {
return !forEachProperty(targetProp, tp => getDeclarationModifierFlagsFromSymbol(tp) & ModifierFlags.Protected ?
!isPropertyInClassDerivedFrom(sourceProp, getDeclaringClass(tp)) : false);
}
// Return true if the given class derives from each of the declaring classes of the protected
// constituents of the given property.
function isClassDerivedFromDeclaringClasses(checkClass: Type, prop: Symbol) {
return forEachProperty(prop, p => getDeclarationModifierFlagsFromSymbol(p) & ModifierFlags.Protected ?
!hasBaseType(checkClass, getDeclaringClass(p)) : false) ? undefined : checkClass;
}
// Return true if the given type is the constructor type for an abstract class
function isAbstractConstructorType(type: Type) {
if (getObjectFlags(type) & ObjectFlags.Anonymous) {
const symbol = type.symbol;
if (symbol && symbol.flags & SymbolFlags.Class) {
const declaration = getClassLikeDeclarationOfSymbol(symbol);
if (declaration && getModifierFlags(declaration) & ModifierFlags.Abstract) {
return true;
}
}
}
return false;
}
// Return true if the given type is deeply nested. We consider this to be the case when structural type comparisons
// for 5 or more occurrences or instantiations of the type have been recorded on the given stack. 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 5
// levels, but unequal at some level beyond that.
function isDeeplyNestedType(type: Type, stack: Type[], depth: number): boolean {
// We track all object types that have an associated symbol (representing the origin of the type)
if (depth >= 5 && type.flags & TypeFlags.Object) {
const symbol = type.symbol;
if (symbol) {
let count = 0;
for (let i = 0; i < depth; i++) {
const t = stack[i];
if (t.flags & TypeFlags.Object && t.symbol === symbol) {
count++;
if (count >= 5) return true;
}
}
}
}
return false;
}
function isPropertyIdenticalTo(sourceProp: Symbol, targetProp: Symbol): boolean {
return compareProperties(sourceProp, targetProp, compareTypesIdentical) !== Ternary.False;
}
function compareProperties(sourceProp: Symbol, targetProp: Symbol, compareTypes: (source: Type, target: Type) => Ternary): Ternary {
// Two members are considered identical when
// - they are public properties with identical names, optionality, and types,
// - they are private or protected properties originating in the same declaration and having identical types
if (sourceProp === targetProp) {
return Ternary.True;
}
const sourcePropAccessibility = getDeclarationModifierFlagsFromSymbol(sourceProp) & ModifierFlags.NonPublicAccessibilityModifier;
const targetPropAccessibility = getDeclarationModifierFlagsFromSymbol(targetProp) & ModifierFlags.NonPublicAccessibilityModifier;
if (sourcePropAccessibility !== targetPropAccessibility) {
return Ternary.False;
}
if (sourcePropAccessibility) {
if (getTargetSymbol(sourceProp) !== getTargetSymbol(targetProp)) {
return Ternary.False;
}
}
else {
if ((sourceProp.flags & SymbolFlags.Optional) !== (targetProp.flags & SymbolFlags.Optional)) {
return Ternary.False;
}
}
if (isReadonlySymbol(sourceProp) !== isReadonlySymbol(targetProp)) {
return Ternary.False;
}
return compareTypes(getTypeOfSymbol(sourceProp), getTypeOfSymbol(targetProp));
}
function isMatchingSignature(source: Signature, target: Signature, partialMatch: boolean) {
// A source signature matches a target signature if the two signatures have the same number of required,
// optional, and rest parameters.
if (source.parameters.length === target.parameters.length &&
source.minArgumentCount === target.minArgumentCount &&
source.hasRestParameter === target.hasRestParameter) {
return true;
}
// A source signature partially matches a target signature if the target signature has no fewer required
// parameters and no more overall parameters than the source signature (where a signature with a rest
// parameter is always considered to have more overall parameters than one without).
const sourceRestCount = source.hasRestParameter ? 1 : 0;
const targetRestCount = target.hasRestParameter ? 1 : 0;
if (partialMatch && source.minArgumentCount <= target.minArgumentCount && (
sourceRestCount > targetRestCount ||
sourceRestCount === targetRestCount && source.parameters.length >= target.parameters.length)) {
return true;
}
return false;
}
/**
* See signatureRelatedTo, compareSignaturesIdentical
*/
function compareSignaturesIdentical(source: Signature, target: Signature, partialMatch: boolean, ignoreThisTypes: boolean, ignoreReturnTypes: boolean, compareTypes: (s: Type, t: Type) => Ternary): Ternary {
// TODO (drosen): De-duplicate code between related functions.
if (source === target) {
return Ternary.True;
}
if (!(isMatchingSignature(source, target, partialMatch))) {
return Ternary.False;
}
// Check that the two signatures have the same number of type parameters. We might consider
// also checking that any type parameter constraints match, but that would require instantiating
// the constraints with a common set of type arguments to get relatable entities in places where
// type parameters occur in the constraints. The complexity of doing that doesn't seem worthwhile,
// particularly as we're comparing erased versions of the signatures below.
if (length(source.typeParameters) !== length(target.typeParameters)) {
return Ternary.False;
}
// Spec 1.0 Section 3.8.3 & 3.8.4:
// M and N (the signatures) are instantiated using type Any as the type argument for all type parameters declared by M and N
source = getErasedSignature(source);
target = getErasedSignature(target);
let result = Ternary.True;
if (!ignoreThisTypes) {
const sourceThisType = getThisTypeOfSignature(source);
if (sourceThisType) {
const targetThisType = getThisTypeOfSignature(target);
if (targetThisType) {
const related = compareTypes(sourceThisType, targetThisType);
if (!related) {
return Ternary.False;
}
result &= related;
}
}
}
const targetLen = target.parameters.length;
for (let i = 0; i < targetLen; i++) {
const s = isRestParameterIndex(source, i) ? getRestTypeOfSignature(source) : getTypeOfParameter(source.parameters[i]);
const t = isRestParameterIndex(target, i) ? getRestTypeOfSignature(target) : getTypeOfParameter(target.parameters[i]);
const related = compareTypes(s, t);
if (!related) {
return Ternary.False;
}
result &= related;
}
if (!ignoreReturnTypes) {
result &= compareTypes(getReturnTypeOfSignature(source), getReturnTypeOfSignature(target));
}
return result;
}
function isRestParameterIndex(signature: Signature, parameterIndex: number) {
return signature.hasRestParameter && parameterIndex >= signature.parameters.length - 1;
}
function isSupertypeOfEach(candidate: Type, types: Type[]): boolean {
for (const t of types) {
if (candidate !== t && !isTypeSubtypeOf(t, candidate)) return false;
}
return true;
}
function literalTypesWithSameBaseType(types: Type[]): boolean {
let commonBaseType: Type;
for (const t of types) {
const baseType = getBaseTypeOfLiteralType(t);
if (!commonBaseType) {
commonBaseType = baseType;
}
if (baseType === t || baseType !== commonBaseType) {
return false;
}
}
return true;
}
// When the candidate types are all literal types with the same base type, the common
// supertype is a union of those literal types. Otherwise, the common supertype is the
// first type that is a supertype of each of the other types.
function getSupertypeOrUnion(types: Type[]): Type {
return literalTypesWithSameBaseType(types) ? getUnionType(types) : forEach(types, t => isSupertypeOfEach(t, types) ? t : undefined);
}
function getCommonSupertype(types: Type[]): Type {
if (!strictNullChecks) {
return getSupertypeOrUnion(types);
}
const primaryTypes = filter(types, t => !(t.flags & TypeFlags.Nullable));
if (!primaryTypes.length) {
return getUnionType(types, /*subtypeReduction*/ true);
}
const supertype = getSupertypeOrUnion(primaryTypes);
return supertype && getNullableType(supertype, getFalsyFlagsOfTypes(types) & TypeFlags.Nullable);
}
function reportNoCommonSupertypeError(types: Type[], errorLocation: Node, errorMessageChainHead: DiagnosticMessageChain): void {
// The downfallType/bestSupertypeDownfallType is the first type that caused a particular candidate
// to not be the common supertype. So if it weren't for this one downfallType (and possibly others),
// the type in question could have been the common supertype.
let bestSupertype: Type;
let bestSupertypeDownfallType: Type;
let bestSupertypeScore = 0;
for (let i = 0; i < types.length; i++) {
let score = 0;
let downfallType: Type = undefined;
for (let j = 0; j < types.length; j++) {
if (isTypeSubtypeOf(types[j], types[i])) {
score++;
}
else if (!downfallType) {
downfallType = types[j];
}
}
Debug.assert(!!downfallType, "If there is no common supertype, each type should have a downfallType");
if (score > bestSupertypeScore) {
bestSupertype = types[i];
bestSupertypeDownfallType = downfallType;
bestSupertypeScore = score;
}
// types.length - 1 is the maximum score, given that getCommonSupertype returned false
if (bestSupertypeScore === types.length - 1) {
break;
}
}
// In the following errors, the {1} slot is before the {0} slot because checkTypeSubtypeOf supplies the
// subtype as the first argument to the error
checkTypeSubtypeOf(bestSupertypeDownfallType, bestSupertype, errorLocation,
Diagnostics.Type_argument_candidate_1_is_not_a_valid_type_argument_because_it_is_not_a_supertype_of_candidate_0,
errorMessageChainHead);
}
function isArrayType(type: Type): boolean {
return getObjectFlags(type) & ObjectFlags.Reference && (<TypeReference>type).target === globalArrayType;
}
function isArrayLikeType(type: Type): boolean {
// A type is array-like if it is a reference to the global Array or global ReadonlyArray type,
// or if it is not the undefined or null type and if it is assignable to ReadonlyArray<any>
return getObjectFlags(type) & ObjectFlags.Reference && ((<TypeReference>type).target === globalArrayType || (<TypeReference>type).target === globalReadonlyArrayType) ||
!(type.flags & TypeFlags.Nullable) && isTypeAssignableTo(type, anyReadonlyArrayType);
}
function isTupleLikeType(type: Type): boolean {
return !!getPropertyOfType(type, "0");
}
function isUnitType(type: Type): boolean {
return (type.flags & (TypeFlags.Literal | TypeFlags.Undefined | TypeFlags.Null)) !== 0;
}
function isLiteralType(type: Type): boolean {
return type.flags & TypeFlags.Boolean ? true :
type.flags & TypeFlags.Union ? type.flags & TypeFlags.EnumLiteral ? true : !forEach((<UnionType>type).types, t => !isUnitType(t)) :
isUnitType(type);
}
function getBaseTypeOfLiteralType(type: Type): Type {
return type.flags & TypeFlags.EnumLiteral ? getBaseTypeOfEnumLiteralType(<LiteralType>type) :
type.flags & TypeFlags.StringLiteral ? stringType :
type.flags & TypeFlags.NumberLiteral ? numberType :
type.flags & TypeFlags.BooleanLiteral ? booleanType :
type.flags & TypeFlags.Union ? getUnionType(sameMap((<UnionType>type).types, getBaseTypeOfLiteralType)) :
type;
}
function getWidenedLiteralType(type: Type): Type {
return type.flags & TypeFlags.EnumLiteral ? getBaseTypeOfEnumLiteralType(<LiteralType>type) :
type.flags & TypeFlags.StringLiteral && type.flags & TypeFlags.FreshLiteral ? stringType :
type.flags & TypeFlags.NumberLiteral && type.flags & TypeFlags.FreshLiteral ? numberType :
type.flags & TypeFlags.BooleanLiteral ? booleanType :
type.flags & TypeFlags.Union ? getUnionType(sameMap((<UnionType>type).types, getWidenedLiteralType)) :
type;
}
/**
* Check if a Type was written as a tuple type literal.
* Prefer using isTupleLikeType() unless the use of `elementTypes` is required.
*/
function isTupleType(type: Type): boolean {
return !!(getObjectFlags(type) & ObjectFlags.Reference && (<TypeReference>type).target.objectFlags & ObjectFlags.Tuple);
}
function getFalsyFlagsOfTypes(types: Type[]): TypeFlags {
let result: TypeFlags = 0;
for (const t of types) {
result |= getFalsyFlags(t);
}
return result;
}
// Returns the String, Number, Boolean, StringLiteral, NumberLiteral, BooleanLiteral, Void, Undefined, or Null
// flags for the string, number, boolean, "", 0, false, void, undefined, or null types respectively. Returns
// no flags for all other types (including non-falsy literal types).
function getFalsyFlags(type: Type): TypeFlags {
return type.flags & TypeFlags.Union ? getFalsyFlagsOfTypes((<UnionType>type).types) :
type.flags & TypeFlags.StringLiteral ? (<LiteralType>type).value === "" ? TypeFlags.StringLiteral : 0 :
type.flags & TypeFlags.NumberLiteral ? (<LiteralType>type).value === 0 ? TypeFlags.NumberLiteral : 0 :
type.flags & TypeFlags.BooleanLiteral ? type === falseType ? TypeFlags.BooleanLiteral : 0 :
type.flags & TypeFlags.PossiblyFalsy;
}
function removeDefinitelyFalsyTypes(type: Type): Type {
return getFalsyFlags(type) & TypeFlags.DefinitelyFalsy ?
filterType(type, t => !(getFalsyFlags(t) & TypeFlags.DefinitelyFalsy)) :
type;
}
function extractDefinitelyFalsyTypes(type: Type): Type {
return mapType(type, getDefinitelyFalsyPartOfType);
}
function getDefinitelyFalsyPartOfType(type: Type): Type {
return type.flags & TypeFlags.String ? emptyStringType :
type.flags & TypeFlags.Number ? zeroType :
type.flags & TypeFlags.Boolean || type === falseType ? falseType :
type.flags & (TypeFlags.Void | TypeFlags.Undefined | TypeFlags.Null) ||
type.flags & TypeFlags.StringLiteral && (<LiteralType>type).value === "" ||
type.flags & TypeFlags.NumberLiteral && (<LiteralType>type).value === 0 ? type :
neverType;
}
function getNullableType(type: Type, flags: TypeFlags): Type {
const missing = (flags & ~type.flags) & (TypeFlags.Undefined | TypeFlags.Null);
return missing === 0 ? type :
missing === TypeFlags.Undefined ? getUnionType([type, undefinedType]) :
missing === TypeFlags.Null ? getUnionType([type, nullType]) :
getUnionType([type, undefinedType, nullType]);
}
function getNonNullableType(type: Type): Type {
return strictNullChecks ? getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull) : type;
}
/**
* Return true if type was inferred from an object literal or written as an object type literal
* with no call or construct signatures.
*/
function isObjectLiteralType(type: Type) {
return type.symbol && (type.symbol.flags & (SymbolFlags.ObjectLiteral | SymbolFlags.TypeLiteral)) !== 0 &&
getSignaturesOfType(type, SignatureKind.Call).length === 0 &&
getSignaturesOfType(type, SignatureKind.Construct).length === 0;
}
function createSymbolWithType(source: Symbol, type: Type) {
const symbol = createSymbol(source.flags, source.name);
symbol.declarations = source.declarations;
symbol.parent = source.parent;
symbol.type = type;
symbol.target = source;
if (source.valueDeclaration) {
symbol.valueDeclaration = source.valueDeclaration;
}
return symbol;
}
function transformTypeOfMembers(type: Type, f: (propertyType: Type) => Type) {
const members = createMap<Symbol>();
for (const property of getPropertiesOfObjectType(type)) {
const original = getTypeOfSymbol(property);
const updated = f(original);
members.set(property.name, updated === original ? property : createSymbolWithType(property, updated));
}
return members;
}
/**
* If the the provided object literal is subject to the excess properties check,
* create a new that is exempt. Recursively mark object literal members as exempt.
* Leave signatures alone since they are not subject to the check.
*/
function getRegularTypeOfObjectLiteral(type: Type): Type {
if (!(getObjectFlags(type) & ObjectFlags.ObjectLiteral && type.flags & TypeFlags.FreshLiteral)) {
return type;
}
const regularType = (<FreshObjectLiteralType>type).regularType;
if (regularType) {
return regularType;
}
const resolved = <ResolvedType>type;
const members = transformTypeOfMembers(type, getRegularTypeOfObjectLiteral);
const regularNew = createAnonymousType(resolved.symbol,
members,
resolved.callSignatures,
resolved.constructSignatures,
resolved.stringIndexInfo,
resolved.numberIndexInfo);
regularNew.flags = resolved.flags & ~TypeFlags.FreshLiteral;
regularNew.objectFlags |= ObjectFlags.ObjectLiteral;
(<FreshObjectLiteralType>type).regularType = regularNew;
return regularNew;
}
function getWidenedProperty(prop: Symbol): Symbol {
const original = getTypeOfSymbol(prop);
const widened = getWidenedType(original);
return widened === original ? prop : createSymbolWithType(prop, widened);
}
function getWidenedTypeOfObjectLiteral(type: Type): Type {
const members = createMap<Symbol>();
for (const prop of getPropertiesOfObjectType(type)) {
// Since get accessors already widen their return value there is no need to
// widen accessor based properties here.
members.set(prop.name, prop.flags & SymbolFlags.Property ? getWidenedProperty(prop) : prop);
}
const stringIndexInfo = getIndexInfoOfType(type, IndexKind.String);
const numberIndexInfo = getIndexInfoOfType(type, IndexKind.Number);
return createAnonymousType(type.symbol, members, emptyArray, emptyArray,
stringIndexInfo && createIndexInfo(getWidenedType(stringIndexInfo.type), stringIndexInfo.isReadonly),
numberIndexInfo && createIndexInfo(getWidenedType(numberIndexInfo.type), numberIndexInfo.isReadonly));
}
function getWidenedConstituentType(type: Type): Type {
return type.flags & TypeFlags.Nullable ? type : getWidenedType(type);
}
function getWidenedType(type: Type): Type {
if (type.flags & TypeFlags.RequiresWidening) {
if (type.flags & TypeFlags.Nullable) {
return anyType;
}
if (getObjectFlags(type) & ObjectFlags.ObjectLiteral) {
return getWidenedTypeOfObjectLiteral(type);
}
if (type.flags & TypeFlags.Union) {
return getUnionType(sameMap((<UnionType>type).types, getWidenedConstituentType));
}
if (isArrayType(type) || isTupleType(type)) {
return createTypeReference((<TypeReference>type).target, sameMap((<TypeReference>type).typeArguments, getWidenedType));
}
}
return type;
}
/**
* Reports implicit any errors that occur as a result of widening 'null' and 'undefined'
* to 'any'. A call to reportWideningErrorsInType is normally accompanied by a call to
* getWidenedType. But in some cases getWidenedType is called without reporting errors
* (type argument inference is an example).
*
* The return value indicates whether an error was in fact reported. The particular circumstances
* are on a best effort basis. Currently, if the null or undefined that causes widening is inside
* an object literal property (arbitrarily deeply), this function reports an error. If no error is
* reported, reportImplicitAnyError is a suitable fallback to report a general error.
*/
function reportWideningErrorsInType(type: Type): boolean {
let errorReported = false;
if (type.flags & TypeFlags.Union) {
for (const t of (<UnionType>type).types) {
if (reportWideningErrorsInType(t)) {
errorReported = true;
}
}
}
if (isArrayType(type) || isTupleType(type)) {
for (const t of (<TypeReference>type).typeArguments) {
if (reportWideningErrorsInType(t)) {
errorReported = true;
}
}
}
if (getObjectFlags(type) & ObjectFlags.ObjectLiteral) {
for (const p of getPropertiesOfObjectType(type)) {
const t = getTypeOfSymbol(p);
if (t.flags & TypeFlags.ContainsWideningType) {
if (!reportWideningErrorsInType(t)) {
error(p.valueDeclaration, Diagnostics.Object_literal_s_property_0_implicitly_has_an_1_type, p.name, typeToString(getWidenedType(t)));
}
errorReported = true;
}
}
}
return errorReported;
}
function reportImplicitAnyError(declaration: Declaration, type: Type) {
const typeAsString = typeToString(getWidenedType(type));
let diagnostic: DiagnosticMessage;
switch (declaration.kind) {
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.PropertySignature:
diagnostic = Diagnostics.Member_0_implicitly_has_an_1_type;
break;
case SyntaxKind.Parameter:
diagnostic = (<ParameterDeclaration>declaration).dotDotDotToken ?
Diagnostics.Rest_parameter_0_implicitly_has_an_any_type :
Diagnostics.Parameter_0_implicitly_has_an_1_type;
break;
case SyntaxKind.BindingElement:
diagnostic = Diagnostics.Binding_element_0_implicitly_has_an_1_type;
break;
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
if (!(declaration as NamedDeclaration).name) {
error(declaration, Diagnostics.Function_expression_which_lacks_return_type_annotation_implicitly_has_an_0_return_type, typeAsString);
return;
}
diagnostic = Diagnostics._0_which_lacks_return_type_annotation_implicitly_has_an_1_return_type;
break;
default:
diagnostic = Diagnostics.Variable_0_implicitly_has_an_1_type;
}
error(declaration, diagnostic, declarationNameToString(getNameOfDeclaration(declaration)), typeAsString);
}
function reportErrorsFromWidening(declaration: Declaration, type: Type) {
if (produceDiagnostics && noImplicitAny && type.flags & TypeFlags.ContainsWideningType) {
// Report implicit any error within type if possible, otherwise report error on declaration
if (!reportWideningErrorsInType(type)) {
reportImplicitAnyError(declaration, type);
}
}
}
function forEachMatchingParameterType(source: Signature, target: Signature, callback: (s: Type, t: Type) => void) {
const sourceMax = source.parameters.length;
const targetMax = target.parameters.length;
let count: number;
if (source.hasRestParameter && target.hasRestParameter) {
count = Math.max(sourceMax, targetMax);
}
else if (source.hasRestParameter) {
count = targetMax;
}
else if (target.hasRestParameter) {
count = sourceMax;
}
else {
count = Math.min(sourceMax, targetMax);
}
for (let i = 0; i < count; i++) {
callback(getTypeAtPosition(source, i), getTypeAtPosition(target, i));
}
}
function createInferenceContext(signature: Signature, inferUnionTypes: boolean, useAnyForNoInferences: boolean): InferenceContext {
const inferences = map(signature.typeParameters, createTypeInferencesObject);
return {
signature,
inferUnionTypes,
inferences,
inferredTypes: new Array(signature.typeParameters.length),
useAnyForNoInferences
};
}
function createTypeInferencesObject(): TypeInferences {
return {
primary: undefined,
secondary: undefined,
topLevel: true,
isFixed: false,
};
}
// Return true if the given type could possibly reference a type parameter for which
// we perform type inference (i.e. a type parameter of a generic function). We cache
// results for union and intersection types for performance reasons.
function couldContainTypeVariables(type: Type): boolean {
const objectFlags = getObjectFlags(type);
return !!(type.flags & TypeFlags.TypeVariable ||
objectFlags & ObjectFlags.Reference && forEach((<TypeReference>type).typeArguments, couldContainTypeVariables) ||
objectFlags & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Method | SymbolFlags.TypeLiteral | SymbolFlags.Class) ||
objectFlags & ObjectFlags.Mapped ||
type.flags & TypeFlags.UnionOrIntersection && couldUnionOrIntersectionContainTypeVariables(<UnionOrIntersectionType>type));
}
function couldUnionOrIntersectionContainTypeVariables(type: UnionOrIntersectionType): boolean {
if (type.couldContainTypeVariables === undefined) {
type.couldContainTypeVariables = forEach(type.types, couldContainTypeVariables);
}
return type.couldContainTypeVariables;
}
function isTypeParameterAtTopLevel(type: Type, typeParameter: TypeParameter): boolean {
return type === typeParameter || type.flags & TypeFlags.UnionOrIntersection && forEach((<UnionOrIntersectionType>type).types, t => isTypeParameterAtTopLevel(t, typeParameter));
}
// Infer a suitable input type for a homomorphic mapped type { [P in keyof T]: X }. We construct
// an object type with the same set of properties as the source type, where the type of each
// property is computed by inferring from the source property type to X for the type
// variable T[P] (i.e. we treat the type T[P] as the type variable we're inferring for).
function inferTypeForHomomorphicMappedType(source: Type, target: MappedType): Type {
const properties = getPropertiesOfType(source);
let indexInfo = getIndexInfoOfType(source, IndexKind.String);
if (properties.length === 0 && !indexInfo) {
return undefined;
}
const typeVariable = <TypeVariable>getIndexedAccessType((<IndexType>getConstraintTypeFromMappedType(target)).type, getTypeParameterFromMappedType(target));
const typeVariableArray = [typeVariable];
const typeInferences = createTypeInferencesObject();
const typeInferencesArray = [typeInferences];
const templateType = getTemplateTypeFromMappedType(target);
const readonlyMask = target.declaration.readonlyToken ? false : true;
const optionalMask = target.declaration.questionToken ? 0 : SymbolFlags.Optional;
const members = createMap<Symbol>();
for (const prop of properties) {
const inferredPropType = inferTargetType(getTypeOfSymbol(prop));
if (!inferredPropType) {
return undefined;
}
const inferredProp = createSymbol(SymbolFlags.Property | prop.flags & optionalMask, prop.name);
inferredProp.checkFlags = readonlyMask && isReadonlySymbol(prop) ? CheckFlags.Readonly : 0;
inferredProp.declarations = prop.declarations;
inferredProp.type = inferredPropType;
members.set(prop.name, inferredProp);
}
if (indexInfo) {
const inferredIndexType = inferTargetType(indexInfo.type);
if (!inferredIndexType) {
return undefined;
}
indexInfo = createIndexInfo(inferredIndexType, readonlyMask && indexInfo.isReadonly);
}
return createAnonymousType(undefined, members, emptyArray, emptyArray, indexInfo, undefined);
function inferTargetType(sourceType: Type): Type {
typeInferences.primary = undefined;
typeInferences.secondary = undefined;
inferTypes(typeVariableArray, typeInferencesArray, sourceType, templateType);
const inferences = typeInferences.primary || typeInferences.secondary;
return inferences && getUnionType(inferences, /*subtypeReduction*/ true);
}
}
function inferTypesWithContext(context: InferenceContext, originalSource: Type, originalTarget: Type) {
inferTypes(context.signature.typeParameters, context.inferences, originalSource, originalTarget);
}
function inferTypes(typeVariables: TypeVariable[], typeInferences: TypeInferences[], originalSource: Type, originalTarget: Type) {
let symbolStack: Symbol[];
let visited: Map<boolean>;
let inferiority = 0;
inferFromTypes(originalSource, originalTarget);
function inferFromTypes(source: Type, target: Type) {
if (!couldContainTypeVariables(target)) {
return;
}
if (source.aliasSymbol && source.aliasTypeArguments && source.aliasSymbol === target.aliasSymbol) {
// Source and target are types originating in the same generic type alias declaration.
// Simply infer from source type arguments to target type arguments.
const sourceTypes = source.aliasTypeArguments;
const targetTypes = target.aliasTypeArguments;
for (let i = 0; i < sourceTypes.length; i++) {
inferFromTypes(sourceTypes[i], targetTypes[i]);
}
return;
}
if (source.flags & TypeFlags.Union && target.flags & TypeFlags.Union && !(source.flags & TypeFlags.EnumLiteral && target.flags & TypeFlags.EnumLiteral) ||
source.flags & TypeFlags.Intersection && target.flags & TypeFlags.Intersection) {
// Source and target are both unions or both intersections. If source and target
// are the same type, just relate each constituent type to itself.
if (source === target) {
for (const t of (<UnionOrIntersectionType>source).types) {
inferFromTypes(t, t);
}
return;
}
// Find each source constituent type that has an identically matching target constituent
// type, and for each such type infer from the type to itself. When inferring from a
// type to itself we effectively find all type parameter occurrences within that type
// and infer themselves as their type arguments. We have special handling for numeric
// and string literals because the number and string types are not represented as unions
// of all their possible values.
let matchingTypes: Type[];
for (const t of (<UnionOrIntersectionType>source).types) {
if (typeIdenticalToSomeType(t, (<UnionOrIntersectionType>target).types)) {
(matchingTypes || (matchingTypes = [])).push(t);
inferFromTypes(t, t);
}
else if (t.flags & (TypeFlags.NumberLiteral | TypeFlags.StringLiteral)) {
const b = getBaseTypeOfLiteralType(t);
if (typeIdenticalToSomeType(b, (<UnionOrIntersectionType>target).types)) {
(matchingTypes || (matchingTypes = [])).push(t, b);
}
}
}
// Next, to improve the quality of inferences, reduce the source and target types by
// removing the identically matched constituents. For example, when inferring from
// 'string | string[]' to 'string | T' we reduce the types to 'string[]' and 'T'.
if (matchingTypes) {
source = removeTypesFromUnionOrIntersection(<UnionOrIntersectionType>source, matchingTypes);
target = removeTypesFromUnionOrIntersection(<UnionOrIntersectionType>target, matchingTypes);
}
}
if (target.flags & TypeFlags.TypeVariable) {
// If target is a type parameter, make an inference, unless the source type contains
// the anyFunctionType (the wildcard type that's used to avoid contextually typing functions).
// Because the anyFunctionType is internal, it should not be exposed to the user by adding
// it as an inference candidate. Hopefully, a better candidate will come along that does
// not contain anyFunctionType when we come back to this argument for its second round
// of inference.
if (source.flags & TypeFlags.ContainsAnyFunctionType) {
return;
}
for (let i = 0; i < typeVariables.length; i++) {
if (target === typeVariables[i]) {
const inferences = typeInferences[i];
if (!inferences.isFixed) {
// Any inferences that are made to a type parameter in a union type are inferior
// to inferences made to a flat (non-union) type. This is because if we infer to
// T | string[], we really don't know if we should be inferring to T or not (because
// the correct constituent on the target side could be string[]). Therefore, we put
// such inferior inferences into a secondary bucket, and only use them if the primary
// bucket is empty.
const candidates = inferiority ?
inferences.secondary || (inferences.secondary = []) :
inferences.primary || (inferences.primary = []);
if (!contains(candidates, source)) {
candidates.push(source);
}
if (target.flags & TypeFlags.TypeParameter && !isTypeParameterAtTopLevel(originalTarget, <TypeParameter>target)) {
inferences.topLevel = false;
}
}
return;
}
}
}
else if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (<TypeReference>source).target === (<TypeReference>target).target) {
// If source and target are references to the same generic type, infer from type arguments
const sourceTypes = (<TypeReference>source).typeArguments || emptyArray;
const targetTypes = (<TypeReference>target).typeArguments || emptyArray;
const count = sourceTypes.length < targetTypes.length ? sourceTypes.length : targetTypes.length;
for (let i = 0; i < count; i++) {
inferFromTypes(sourceTypes[i], targetTypes[i]);
}
}
else if (target.flags & TypeFlags.UnionOrIntersection) {
const targetTypes = (<UnionOrIntersectionType>target).types;
let typeVariableCount = 0;
let typeVariable: TypeVariable;
// First infer to each type in union or intersection that isn't a type variable
for (const t of targetTypes) {
if (t.flags & TypeFlags.TypeVariable && contains(typeVariables, t)) {
typeVariable = <TypeVariable>t;
typeVariableCount++;
}
else {
inferFromTypes(source, t);
}
}
// Next, if target containings a single naked type variable, make a secondary inference to that type
// variable. This gives meaningful results for union types in co-variant positions and intersection
// types in contra-variant positions (such as callback parameters).
if (typeVariableCount === 1) {
inferiority++;
inferFromTypes(source, typeVariable);
inferiority--;
}
}
else if (source.flags & TypeFlags.UnionOrIntersection) {
// Source is a union or intersection type, infer from each constituent type
const sourceTypes = (<UnionOrIntersectionType>source).types;
for (const sourceType of sourceTypes) {
inferFromTypes(sourceType, target);
}
}
else {
source = getApparentType(source);
if (source.flags & TypeFlags.Object) {
const key = source.id + "," + target.id;
if (visited && visited.get(key)) {
return;
}
(visited || (visited = createMap<boolean>())).set(key, true);
// If we are already processing another target type with the same associated symbol (such as
// an instantiation of the same generic type), we do not explore this target as it would yield
// no further inferences. We exclude the static side of classes from this check since it shares
// its symbol with the instance side which would lead to false positives.
const isNonConstructorObject = target.flags & TypeFlags.Object &&
!(getObjectFlags(target) & ObjectFlags.Anonymous && target.symbol && target.symbol.flags & SymbolFlags.Class);
const symbol = isNonConstructorObject ? target.symbol : undefined;
if (symbol) {
if (contains(symbolStack, symbol)) {
return;
}
(symbolStack || (symbolStack = [])).push(symbol);
inferFromObjectTypes(source, target);
symbolStack.pop();
}
else {
inferFromObjectTypes(source, target);
}
}
}
}
function inferFromObjectTypes(source: Type, target: Type) {
if (getObjectFlags(target) & ObjectFlags.Mapped) {
const constraintType = getConstraintTypeFromMappedType(<MappedType>target);
if (constraintType.flags & TypeFlags.Index) {
// We're inferring from some source type S to a homomorphic mapped type { [P in keyof T]: X },
// where T is a type variable. Use inferTypeForHomomorphicMappedType to infer a suitable source
// type and then make a secondary inference from that type to T. We make a secondary inference
// such that direct inferences to T get priority over inferences to Partial<T>, for example.
const index = indexOf(typeVariables, (<IndexType>constraintType).type);
if (index >= 0 && !typeInferences[index].isFixed) {
const inferredType = inferTypeForHomomorphicMappedType(source, <MappedType>target);
if (inferredType) {
inferiority++;
inferFromTypes(inferredType, typeVariables[index]);
inferiority--;
}
}
return;
}
if (constraintType.flags & TypeFlags.TypeParameter) {
// We're inferring from some source type S to a mapped type { [P in T]: X }, where T is a type
// parameter. Infer from 'keyof S' to T and infer from a union of each property type in S to X.
inferFromTypes(getIndexType(source), constraintType);
inferFromTypes(getUnionType(map(getPropertiesOfType(source), getTypeOfSymbol)), getTemplateTypeFromMappedType(<MappedType>target));
return;
}
}
inferFromProperties(source, target);
inferFromSignatures(source, target, SignatureKind.Call);
inferFromSignatures(source, target, SignatureKind.Construct);
inferFromIndexTypes(source, target);
}
function inferFromProperties(source: Type, target: Type) {
const properties = getPropertiesOfObjectType(target);
for (const targetProp of properties) {
const sourceProp = getPropertyOfObjectType(source, targetProp.name);
if (sourceProp) {
inferFromTypes(getTypeOfSymbol(sourceProp), getTypeOfSymbol(targetProp));
}
}
}
function inferFromSignatures(source: Type, target: Type, kind: SignatureKind) {
const sourceSignatures = getSignaturesOfType(source, kind);
const targetSignatures = getSignaturesOfType(target, kind);
const sourceLen = sourceSignatures.length;
const targetLen = targetSignatures.length;
const len = sourceLen < targetLen ? sourceLen : targetLen;
for (let i = 0; i < len; i++) {
inferFromSignature(getErasedSignature(sourceSignatures[sourceLen - len + i]), getErasedSignature(targetSignatures[targetLen - len + i]));
}
}
function inferFromParameterTypes(source: Type, target: Type) {
return inferFromTypes(source, target);
}
function inferFromSignature(source: Signature, target: Signature) {
forEachMatchingParameterType(source, target, inferFromParameterTypes);
if (source.typePredicate && target.typePredicate && source.typePredicate.kind === target.typePredicate.kind) {
inferFromTypes(source.typePredicate.type, target.typePredicate.type);
}
else {
inferFromTypes(getReturnTypeOfSignature(source), getReturnTypeOfSignature(target));
}
}
function inferFromIndexTypes(source: Type, target: Type) {
const targetStringIndexType = getIndexTypeOfType(target, IndexKind.String);
if (targetStringIndexType) {
const sourceIndexType = getIndexTypeOfType(source, IndexKind.String) ||
getImplicitIndexTypeOfType(source, IndexKind.String);
if (sourceIndexType) {
inferFromTypes(sourceIndexType, targetStringIndexType);
}
}
const targetNumberIndexType = getIndexTypeOfType(target, IndexKind.Number);
if (targetNumberIndexType) {
const sourceIndexType = getIndexTypeOfType(source, IndexKind.Number) ||
getIndexTypeOfType(source, IndexKind.String) ||
getImplicitIndexTypeOfType(source, IndexKind.Number);
if (sourceIndexType) {
inferFromTypes(sourceIndexType, targetNumberIndexType);
}
}
}
}
function typeIdenticalToSomeType(type: Type, types: Type[]): boolean {
for (const t of types) {
if (isTypeIdenticalTo(t, type)) {
return true;
}
}
return false;
}
/**
* Return a new union or intersection type computed by removing a given set of types
* from a given union or intersection type.
*/
function removeTypesFromUnionOrIntersection(type: UnionOrIntersectionType, typesToRemove: Type[]) {
const reducedTypes: Type[] = [];
for (const t of type.types) {
if (!typeIdenticalToSomeType(t, typesToRemove)) {
reducedTypes.push(t);
}
}
return type.flags & TypeFlags.Union ? getUnionType(reducedTypes) : getIntersectionType(reducedTypes);
}
function getInferenceCandidates(context: InferenceContext, index: number): Type[] {
const inferences = context.inferences[index];
return inferences.primary || inferences.secondary || emptyArray;
}
function hasPrimitiveConstraint(type: TypeParameter): boolean {
const constraint = getConstraintOfTypeParameter(type);
return constraint && maybeTypeOfKind(constraint, TypeFlags.Primitive | TypeFlags.Index);
}
function getInferredType(context: InferenceContext, index: number): Type {
let inferredType = context.inferredTypes[index];
let inferenceSucceeded: boolean;
if (!inferredType) {
const inferences = getInferenceCandidates(context, index);
if (inferences.length) {
// We widen inferred literal types if
// all inferences were made to top-level ocurrences of the type parameter, and
// the type parameter has no constraint or its constraint includes no primitive or literal types, and
// the type parameter was fixed during inference or does not occur at top-level in the return type.
const signature = context.signature;
const widenLiteralTypes = context.inferences[index].topLevel &&
!hasPrimitiveConstraint(signature.typeParameters[index]) &&
(context.inferences[index].isFixed || !isTypeParameterAtTopLevel(getReturnTypeOfSignature(signature), signature.typeParameters[index]));
const baseInferences = widenLiteralTypes ? sameMap(inferences, getWidenedLiteralType) : inferences;
// Infer widened union or supertype, or the unknown type for no common supertype
const unionOrSuperType = context.inferUnionTypes ? getUnionType(baseInferences, /*subtypeReduction*/ true) : getCommonSupertype(baseInferences);
inferredType = unionOrSuperType ? getWidenedType(unionOrSuperType) : unknownType;
inferenceSucceeded = !!unionOrSuperType;
}
else {
// Infer either the default or the empty object type when no inferences were
// made. It is important to remember that in this case, inference still
// succeeds, meaning there is no error for not having inference candidates. An
// inference error only occurs when there are *conflicting* candidates, i.e.
// candidates with no common supertype.
const defaultType = getDefaultFromTypeParameter(context.signature.typeParameters[index]);
if (defaultType) {
// Instantiate the default type. Any forward reference to a type
// parameter should be instantiated to the empty object type.
inferredType = instantiateType(defaultType,
combineTypeMappers(
createBackreferenceMapper(context.signature.typeParameters, index),
getInferenceMapper(context)));
}
else {
inferredType = context.useAnyForNoInferences ? anyType : emptyObjectType;
}
inferenceSucceeded = true;
}
context.inferredTypes[index] = inferredType;
// Only do the constraint check if inference succeeded (to prevent cascading errors)
if (inferenceSucceeded) {
const constraint = getConstraintOfTypeParameter(context.signature.typeParameters[index]);
if (constraint) {
const instantiatedConstraint = instantiateType(constraint, getInferenceMapper(context));
if (!isTypeAssignableTo(inferredType, getTypeWithThisArgument(instantiatedConstraint, inferredType))) {
context.inferredTypes[index] = inferredType = instantiatedConstraint;
}
}
}
else if (context.failedTypeParameterIndex === undefined || context.failedTypeParameterIndex > index) {
// If inference failed, it is necessary to record the index of the failed type parameter (the one we are on).
// It might be that inference has already failed on a later type parameter on a previous call to inferTypeArguments.
// So if this failure is on preceding type parameter, this type parameter is the new failure index.
context.failedTypeParameterIndex = index;
}
}
return inferredType;
}
function getInferredTypes(context: InferenceContext): Type[] {
for (let i = 0; i < context.inferredTypes.length; i++) {
getInferredType(context, i);
}
return context.inferredTypes;
}
// EXPRESSION TYPE CHECKING
function getResolvedSymbol(node: Identifier): Symbol {
const links = getNodeLinks(node);
if (!links.resolvedSymbol) {
links.resolvedSymbol = !nodeIsMissing(node) && resolveName(node, node.text, SymbolFlags.Value | SymbolFlags.ExportValue, Diagnostics.Cannot_find_name_0, node, Diagnostics.Cannot_find_name_0_Did_you_mean_1) || unknownSymbol;
}
return links.resolvedSymbol;
}
function isInTypeQuery(node: Node): boolean {
// TypeScript 1.0 spec (April 2014): 3.6.3
// A type query consists of the keyword typeof followed by an expression.
// The expression is restricted to a single identifier or a sequence of identifiers separated by periods
return !!findAncestor(
node,
n => n.kind === SyntaxKind.TypeQuery ? true : n.kind === SyntaxKind.Identifier || n.kind === SyntaxKind.QualifiedName ? false : "quit");
}
// Return the flow cache key for a "dotted name" (i.e. a sequence of identifiers
// separated by dots). The key consists of the id of the symbol referenced by the
// leftmost identifier followed by zero or more property names separated by dots.
// The result is undefined if the reference isn't a dotted name. We prefix nodes
// occurring in an apparent type position with '@' because the control flow type
// of such nodes may be based on the apparent type instead of the declared type.
function getFlowCacheKey(node: Node): string {
if (node.kind === SyntaxKind.Identifier) {
const symbol = getResolvedSymbol(<Identifier>node);
return symbol !== unknownSymbol ? (isApparentTypePosition(node) ? "@" : "") + getSymbolId(symbol) : undefined;
}
if (node.kind === SyntaxKind.ThisKeyword) {
return "0";
}
if (node.kind === SyntaxKind.PropertyAccessExpression) {
const key = getFlowCacheKey((<PropertyAccessExpression>node).expression);
return key && key + "." + (<PropertyAccessExpression>node).name.text;
}
return undefined;
}
function getLeftmostIdentifierOrThis(node: Node): Node {
switch (node.kind) {
case SyntaxKind.Identifier:
case SyntaxKind.ThisKeyword:
return node;
case SyntaxKind.PropertyAccessExpression:
return getLeftmostIdentifierOrThis((<PropertyAccessExpression>node).expression);
}
return undefined;
}
function isMatchingReference(source: Node, target: Node): boolean {
switch (source.kind) {
case SyntaxKind.Identifier:
return target.kind === SyntaxKind.Identifier && getResolvedSymbol(<Identifier>source) === getResolvedSymbol(<Identifier>target) ||
(target.kind === SyntaxKind.VariableDeclaration || target.kind === SyntaxKind.BindingElement) &&
getExportSymbolOfValueSymbolIfExported(getResolvedSymbol(<Identifier>source)) === getSymbolOfNode(target);
case SyntaxKind.ThisKeyword:
return target.kind === SyntaxKind.ThisKeyword;
case SyntaxKind.SuperKeyword:
return target.kind === SyntaxKind.SuperKeyword;
case SyntaxKind.PropertyAccessExpression:
return target.kind === SyntaxKind.PropertyAccessExpression &&
(<PropertyAccessExpression>source).name.text === (<PropertyAccessExpression>target).name.text &&
isMatchingReference((<PropertyAccessExpression>source).expression, (<PropertyAccessExpression>target).expression);
}
return false;
}
function containsMatchingReference(source: Node, target: Node) {
while (source.kind === SyntaxKind.PropertyAccessExpression) {
source = (<PropertyAccessExpression>source).expression;
if (isMatchingReference(source, target)) {
return true;
}
}
return false;
}
// Return true if target is a property access xxx.yyy, source is a property access xxx.zzz, the declared
// type of xxx is a union type, and yyy is a property that is possibly a discriminant. We consider a property
// a possible discriminant if its type differs in the constituents of containing union type, and if every
// choice is a unit type or a union of unit types.
function containsMatchingReferenceDiscriminant(source: Node, target: Node) {
return target.kind === SyntaxKind.PropertyAccessExpression &&
containsMatchingReference(source, (<PropertyAccessExpression>target).expression) &&
isDiscriminantProperty(getDeclaredTypeOfReference((<PropertyAccessExpression>target).expression), (<PropertyAccessExpression>target).name.text);
}
function getDeclaredTypeOfReference(expr: Node): Type {
if (expr.kind === SyntaxKind.Identifier) {
return getTypeOfSymbol(getResolvedSymbol(<Identifier>expr));
}
if (expr.kind === SyntaxKind.PropertyAccessExpression) {
const type = getDeclaredTypeOfReference((<PropertyAccessExpression>expr).expression);
return type && getTypeOfPropertyOfType(type, (<PropertyAccessExpression>expr).name.text);
}
return undefined;
}
function isDiscriminantProperty(type: Type, name: string) {
if (type && type.flags & TypeFlags.Union) {
const prop = getUnionOrIntersectionProperty(<UnionType>type, name);
if (prop && getCheckFlags(prop) & CheckFlags.SyntheticProperty) {
if ((<TransientSymbol>prop).isDiscriminantProperty === undefined) {
(<TransientSymbol>prop).isDiscriminantProperty = (<TransientSymbol>prop).checkFlags & CheckFlags.HasNonUniformType && isLiteralType(getTypeOfSymbol(prop));
}
return (<TransientSymbol>prop).isDiscriminantProperty;
}
}
return false;
}
function isOrContainsMatchingReference(source: Node, target: Node) {
return isMatchingReference(source, target) || containsMatchingReference(source, target);
}
function hasMatchingArgument(callExpression: CallExpression, reference: Node) {
if (callExpression.arguments) {
for (const argument of callExpression.arguments) {
if (isOrContainsMatchingReference(reference, argument)) {
return true;
}
}
}
if (callExpression.expression.kind === SyntaxKind.PropertyAccessExpression &&
isOrContainsMatchingReference(reference, (<PropertyAccessExpression>callExpression.expression).expression)) {
return true;
}
return false;
}
function getFlowNodeId(flow: FlowNode): number {
if (!flow.id) {
flow.id = nextFlowId;
nextFlowId++;
}
return flow.id;
}
function typeMaybeAssignableTo(source: Type, target: Type) {
if (!(source.flags & TypeFlags.Union)) {
return isTypeAssignableTo(source, target);
}
for (const t of (<UnionType>source).types) {
if (isTypeAssignableTo(t, target)) {
return true;
}
}
return false;
}
// Remove those constituent types of declaredType to which no constituent type of assignedType is assignable.
// For example, when a variable of type number | string | boolean is assigned a value of type number | boolean,
// we remove type string.
function getAssignmentReducedType(declaredType: UnionType, assignedType: Type) {
if (declaredType !== assignedType) {
if (assignedType.flags & TypeFlags.Never) {
return assignedType;
}
const reducedType = filterType(declaredType, t => typeMaybeAssignableTo(assignedType, t));
if (!(reducedType.flags & TypeFlags.Never)) {
return reducedType;
}
}
return declaredType;
}
function getTypeFactsOfTypes(types: Type[]): TypeFacts {
let result: TypeFacts = TypeFacts.None;
for (const t of types) {
result |= getTypeFacts(t);
}
return result;
}
function isFunctionObjectType(type: ObjectType): boolean {
// We do a quick check for a "bind" property before performing the more expensive subtype
// check. This gives us a quicker out in the common case where an object type is not a function.
const resolved = resolveStructuredTypeMembers(type);
return !!(resolved.callSignatures.length || resolved.constructSignatures.length ||
resolved.members.get("bind") && isTypeSubtypeOf(type, globalFunctionType));
}
function getTypeFacts(type: Type): TypeFacts {
const flags = type.flags;
if (flags & TypeFlags.String) {
return strictNullChecks ? TypeFacts.StringStrictFacts : TypeFacts.StringFacts;
}
if (flags & TypeFlags.StringLiteral) {
const isEmpty = (<LiteralType>type).value === "";
return strictNullChecks ?
isEmpty ? TypeFacts.EmptyStringStrictFacts : TypeFacts.NonEmptyStringStrictFacts :
isEmpty ? TypeFacts.EmptyStringFacts : TypeFacts.NonEmptyStringFacts;
}
if (flags & (TypeFlags.Number | TypeFlags.Enum)) {
return strictNullChecks ? TypeFacts.NumberStrictFacts : TypeFacts.NumberFacts;
}
if (flags & TypeFlags.NumberLiteral) {
const isZero = (<LiteralType>type).value === 0;
return strictNullChecks ?
isZero ? TypeFacts.ZeroStrictFacts : TypeFacts.NonZeroStrictFacts :
isZero ? TypeFacts.ZeroFacts : TypeFacts.NonZeroFacts;
}
if (flags & TypeFlags.Boolean) {
return strictNullChecks ? TypeFacts.BooleanStrictFacts : TypeFacts.BooleanFacts;
}
if (flags & TypeFlags.BooleanLike) {
return strictNullChecks ?
type === falseType ? TypeFacts.FalseStrictFacts : TypeFacts.TrueStrictFacts :
type === falseType ? TypeFacts.FalseFacts : TypeFacts.TrueFacts;
}
if (flags & TypeFlags.Object) {
return isFunctionObjectType(<ObjectType>type) ?
strictNullChecks ? TypeFacts.FunctionStrictFacts : TypeFacts.FunctionFacts :
strictNullChecks ? TypeFacts.ObjectStrictFacts : TypeFacts.ObjectFacts;
}
if (flags & (TypeFlags.Void | TypeFlags.Undefined)) {
return TypeFacts.UndefinedFacts;
}
if (flags & TypeFlags.Null) {
return TypeFacts.NullFacts;
}
if (flags & TypeFlags.ESSymbol) {
return strictNullChecks ? TypeFacts.SymbolStrictFacts : TypeFacts.SymbolFacts;
}
if (flags & TypeFlags.NonPrimitive) {
return strictNullChecks ? TypeFacts.ObjectStrictFacts : TypeFacts.ObjectFacts;
}
if (flags & TypeFlags.TypeVariable) {
return getTypeFacts(getBaseConstraintOfType(type) || emptyObjectType);
}
if (flags & TypeFlags.UnionOrIntersection) {
return getTypeFactsOfTypes((<UnionOrIntersectionType>type).types);
}
return TypeFacts.All;
}
function getTypeWithFacts(type: Type, include: TypeFacts) {
return filterType(type, t => (getTypeFacts(t) & include) !== 0);
}
function getTypeWithDefault(type: Type, defaultExpression: Expression) {
if (defaultExpression) {
const defaultType = getTypeOfExpression(defaultExpression);
return getUnionType([getTypeWithFacts(type, TypeFacts.NEUndefined), defaultType]);
}
return type;
}
function getTypeOfDestructuredProperty(type: Type, name: PropertyName) {
const text = getTextOfPropertyName(name);
return getTypeOfPropertyOfType(type, text) ||
isNumericLiteralName(text) && getIndexTypeOfType(type, IndexKind.Number) ||
getIndexTypeOfType(type, IndexKind.String) ||
unknownType;
}
function getTypeOfDestructuredArrayElement(type: Type, index: number) {
return isTupleLikeType(type) && getTypeOfPropertyOfType(type, "" + index) ||
checkIteratedTypeOrElementType(type, /*errorNode*/ undefined, /*allowStringInput*/ false, /*allowAsyncIterable*/ false) ||
unknownType;
}
function getTypeOfDestructuredSpreadExpression(type: Type) {
return createArrayType(checkIteratedTypeOrElementType(type, /*errorNode*/ undefined, /*allowStringInput*/ false, /*allowAsyncIterable*/ false) || unknownType);
}
function getAssignedTypeOfBinaryExpression(node: BinaryExpression): Type {
const isDestructuringDefaultAssignment =
node.parent.kind === SyntaxKind.ArrayLiteralExpression && isDestructuringAssignmentTarget(node.parent) ||
node.parent.kind === SyntaxKind.PropertyAssignment && isDestructuringAssignmentTarget(node.parent.parent);
return isDestructuringDefaultAssignment ?
getTypeWithDefault(getAssignedType(node), node.right) :
getTypeOfExpression(node.right);
}
function isDestructuringAssignmentTarget(parent: Node) {
return parent.parent.kind === SyntaxKind.BinaryExpression && (parent.parent as BinaryExpression).left === parent ||
parent.parent.kind === SyntaxKind.ForOfStatement && (parent.parent as ForOfStatement).initializer === parent;
}
function getAssignedTypeOfArrayLiteralElement(node: ArrayLiteralExpression, element: Expression): Type {
return getTypeOfDestructuredArrayElement(getAssignedType(node), indexOf(node.elements, element));
}
function getAssignedTypeOfSpreadExpression(node: SpreadElement): Type {
return getTypeOfDestructuredSpreadExpression(getAssignedType(<ArrayLiteralExpression>node.parent));
}
function getAssignedTypeOfPropertyAssignment(node: PropertyAssignment | ShorthandPropertyAssignment): Type {
return getTypeOfDestructuredProperty(getAssignedType(<ObjectLiteralExpression>node.parent), node.name);
}
function getAssignedTypeOfShorthandPropertyAssignment(node: ShorthandPropertyAssignment): Type {
return getTypeWithDefault(getAssignedTypeOfPropertyAssignment(node), node.objectAssignmentInitializer);
}
function getAssignedType(node: Expression): Type {
const parent = node.parent;
switch (parent.kind) {
case SyntaxKind.ForInStatement:
return stringType;
case SyntaxKind.ForOfStatement:
return checkRightHandSideOfForOf((<ForOfStatement>parent).expression, (<ForOfStatement>parent).awaitModifier) || unknownType;
case SyntaxKind.BinaryExpression:
return getAssignedTypeOfBinaryExpression(<BinaryExpression>parent);
case SyntaxKind.DeleteExpression:
return undefinedType;
case SyntaxKind.ArrayLiteralExpression:
return getAssignedTypeOfArrayLiteralElement(<ArrayLiteralExpression>parent, node);
case SyntaxKind.SpreadElement:
return getAssignedTypeOfSpreadExpression(<SpreadElement>parent);
case SyntaxKind.PropertyAssignment:
return getAssignedTypeOfPropertyAssignment(<PropertyAssignment>parent);
case SyntaxKind.ShorthandPropertyAssignment:
return getAssignedTypeOfShorthandPropertyAssignment(<ShorthandPropertyAssignment>parent);
}
return unknownType;
}
function getInitialTypeOfBindingElement(node: BindingElement): Type {
const pattern = <BindingPattern>node.parent;
const parentType = getInitialType(<VariableDeclaration | BindingElement>pattern.parent);
const type = pattern.kind === SyntaxKind.ObjectBindingPattern ?
getTypeOfDestructuredProperty(parentType, node.propertyName || <Identifier>node.name) :
!node.dotDotDotToken ?
getTypeOfDestructuredArrayElement(parentType, indexOf(pattern.elements, node)) :
getTypeOfDestructuredSpreadExpression(parentType);
return getTypeWithDefault(type, node.initializer);
}
function getTypeOfInitializer(node: Expression) {
// Return the cached type if one is available. If the type of the variable was inferred
// from its initializer, we'll already have cached the type. Otherwise we compute it now
// without caching such that transient types are reflected.
const links = getNodeLinks(node);
return links.resolvedType || getTypeOfExpression(node);
}
function getInitialTypeOfVariableDeclaration(node: VariableDeclaration) {
if (node.initializer) {
return getTypeOfInitializer(node.initializer);
}
if (node.parent.parent.kind === SyntaxKind.ForInStatement) {
return stringType;
}
if (node.parent.parent.kind === SyntaxKind.ForOfStatement) {
return checkRightHandSideOfForOf((<ForOfStatement>node.parent.parent).expression, (<ForOfStatement>node.parent.parent).awaitModifier) || unknownType;
}
return unknownType;
}
function getInitialType(node: VariableDeclaration | BindingElement) {
return node.kind === SyntaxKind.VariableDeclaration ?
getInitialTypeOfVariableDeclaration(<VariableDeclaration>node) :
getInitialTypeOfBindingElement(<BindingElement>node);
}
function getInitialOrAssignedType(node: VariableDeclaration | BindingElement | Expression) {
return node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.BindingElement ?
getInitialType(<VariableDeclaration | BindingElement>node) :
getAssignedType(<Expression>node);
}
function isEmptyArrayAssignment(node: VariableDeclaration | BindingElement | Expression) {
return node.kind === SyntaxKind.VariableDeclaration && (<VariableDeclaration>node).initializer &&
isEmptyArrayLiteral((<VariableDeclaration>node).initializer) ||
node.kind !== SyntaxKind.BindingElement && node.parent.kind === SyntaxKind.BinaryExpression &&
isEmptyArrayLiteral((<BinaryExpression>node.parent).right);
}
function getReferenceCandidate(node: Expression): Expression {
switch (node.kind) {
case SyntaxKind.ParenthesizedExpression:
return getReferenceCandidate((<ParenthesizedExpression>node).expression);
case SyntaxKind.BinaryExpression:
switch ((<BinaryExpression>node).operatorToken.kind) {
case SyntaxKind.EqualsToken:
return getReferenceCandidate((<BinaryExpression>node).left);
case SyntaxKind.CommaToken:
return getReferenceCandidate((<BinaryExpression>node).right);
}
}
return node;
}
function getReferenceRoot(node: Node): Node {
const parent = node.parent;
return parent.kind === SyntaxKind.ParenthesizedExpression ||
parent.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>parent).operatorToken.kind === SyntaxKind.EqualsToken && (<BinaryExpression>parent).left === node ||
parent.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>parent).operatorToken.kind === SyntaxKind.CommaToken && (<BinaryExpression>parent).right === node ?
getReferenceRoot(parent) : node;
}
function getTypeOfSwitchClause(clause: CaseClause | DefaultClause) {
if (clause.kind === SyntaxKind.CaseClause) {
const caseType = getRegularTypeOfLiteralType(getTypeOfExpression((<CaseClause>clause).expression));
return isUnitType(caseType) ? caseType : undefined;
}
return neverType;
}
function getSwitchClauseTypes(switchStatement: SwitchStatement): Type[] {
const links = getNodeLinks(switchStatement);
if (!links.switchTypes) {
// If all case clauses specify expressions that have unit types, we return an array
// of those unit types. Otherwise we return an empty array.
const types = map(switchStatement.caseBlock.clauses, getTypeOfSwitchClause);
links.switchTypes = !contains(types, undefined) ? types : emptyArray;
}
return links.switchTypes;
}
function eachTypeContainedIn(source: Type, types: Type[]) {
return source.flags & TypeFlags.Union ? !forEach((<UnionType>source).types, t => !contains(types, t)) : contains(types, source);
}
function isTypeSubsetOf(source: Type, target: Type) {
return source === target || target.flags & TypeFlags.Union && isTypeSubsetOfUnion(source, <UnionType>target);
}
function isTypeSubsetOfUnion(source: Type, target: UnionType) {
if (source.flags & TypeFlags.Union) {
for (const t of (<UnionType>source).types) {
if (!containsType(target.types, t)) {
return false;
}
}
return true;
}
if (source.flags & TypeFlags.EnumLiteral && getBaseTypeOfEnumLiteralType(<LiteralType>source) === target) {
return true;
}
return containsType(target.types, source);
}
function forEachType<T>(type: Type, f: (t: Type) => T): T {
return type.flags & TypeFlags.Union ? forEach((<UnionType>type).types, f) : f(type);
}
function filterType(type: Type, f: (t: Type) => boolean): Type {
if (type.flags & TypeFlags.Union) {
const types = (<UnionType>type).types;
const filtered = filter(types, f);
return filtered === types ? type : getUnionTypeFromSortedList(filtered);
}
return f(type) ? type : neverType;
}
// Apply a mapping function to a type and return the resulting type. If the source type
// is a union type, the mapping function is applied to each constituent type and a union
// of the resulting types is returned.
function mapType(type: Type, mapper: (t: Type) => Type): Type {
if (!(type.flags & TypeFlags.Union)) {
return mapper(type);
}
const types = (<UnionType>type).types;
let mappedType: Type;
let mappedTypes: Type[];
for (const current of types) {
const t = mapper(current);
if (t) {
if (!mappedType) {
mappedType = t;
}
else if (!mappedTypes) {
mappedTypes = [mappedType, t];
}
else {
mappedTypes.push(t);
}
}
}
return mappedTypes ? getUnionType(mappedTypes) : mappedType;
}
function extractTypesOfKind(type: Type, kind: TypeFlags) {
return filterType(type, t => (t.flags & kind) !== 0);
}
// Return a new type in which occurrences of the string and number primitive types in
// typeWithPrimitives have been replaced with occurrences of string literals and numeric
// literals in typeWithLiterals, respectively.
function replacePrimitivesWithLiterals(typeWithPrimitives: Type, typeWithLiterals: Type) {
if (isTypeSubsetOf(stringType, typeWithPrimitives) && maybeTypeOfKind(typeWithLiterals, TypeFlags.StringLiteral) ||
isTypeSubsetOf(numberType, typeWithPrimitives) && maybeTypeOfKind(typeWithLiterals, TypeFlags.NumberLiteral)) {
return mapType(typeWithPrimitives, t =>
t.flags & TypeFlags.String ? extractTypesOfKind(typeWithLiterals, TypeFlags.String | TypeFlags.StringLiteral) :
t.flags & TypeFlags.Number ? extractTypesOfKind(typeWithLiterals, TypeFlags.Number | TypeFlags.NumberLiteral) :
t);
}
return typeWithPrimitives;
}
function isIncomplete(flowType: FlowType) {
return flowType.flags === 0;
}
function getTypeFromFlowType(flowType: FlowType) {
return flowType.flags === 0 ? (<IncompleteType>flowType).type : <Type>flowType;
}
function createFlowType(type: Type, incomplete: boolean): FlowType {
return incomplete ? { flags: 0, type } : type;
}
// An evolving array type tracks the element types that have so far been seen in an
// 'x.push(value)' or 'x[n] = value' operation along the control flow graph. Evolving
// array types are ultimately converted into manifest array types (using getFinalArrayType)
// and never escape the getFlowTypeOfReference function.
function createEvolvingArrayType(elementType: Type): EvolvingArrayType {
const result = <EvolvingArrayType>createObjectType(ObjectFlags.EvolvingArray);
result.elementType = elementType;
return result;
}
function getEvolvingArrayType(elementType: Type): EvolvingArrayType {
return evolvingArrayTypes[elementType.id] || (evolvingArrayTypes[elementType.id] = createEvolvingArrayType(elementType));
}
// When adding evolving array element types we do not perform subtype reduction. Instead,
// we defer subtype reduction until the evolving array type is finalized into a manifest
// array type.
function addEvolvingArrayElementType(evolvingArrayType: EvolvingArrayType, node: Expression): EvolvingArrayType {
const elementType = getBaseTypeOfLiteralType(getContextFreeTypeOfExpression(node));
return isTypeSubsetOf(elementType, evolvingArrayType.elementType) ? evolvingArrayType : getEvolvingArrayType(getUnionType([evolvingArrayType.elementType, elementType]));
}
function createFinalArrayType(elementType: Type) {
return elementType.flags & TypeFlags.Never ?
autoArrayType :
createArrayType(elementType.flags & TypeFlags.Union ?
getUnionType((<UnionType>elementType).types, /*subtypeReduction*/ true) :
elementType);
}
// We perform subtype reduction upon obtaining the final array type from an evolving array type.
function getFinalArrayType(evolvingArrayType: EvolvingArrayType): Type {
return evolvingArrayType.finalArrayType || (evolvingArrayType.finalArrayType = createFinalArrayType(evolvingArrayType.elementType));
}
function finalizeEvolvingArrayType(type: Type): Type {
return getObjectFlags(type) & ObjectFlags.EvolvingArray ? getFinalArrayType(<EvolvingArrayType>type) : type;
}
function getElementTypeOfEvolvingArrayType(type: Type) {
return getObjectFlags(type) & ObjectFlags.EvolvingArray ? (<EvolvingArrayType>type).elementType : neverType;
}
function isEvolvingArrayTypeList(types: Type[]) {
let hasEvolvingArrayType = false;
for (const t of types) {
if (!(t.flags & TypeFlags.Never)) {
if (!(getObjectFlags(t) & ObjectFlags.EvolvingArray)) {
return false;
}
hasEvolvingArrayType = true;
}
}
return hasEvolvingArrayType;
}
// At flow control branch or loop junctions, if the type along every antecedent code path
// is an evolving array type, we construct a combined evolving array type. Otherwise we
// finalize all evolving array types.
function getUnionOrEvolvingArrayType(types: Type[], subtypeReduction: boolean) {
return isEvolvingArrayTypeList(types) ?
getEvolvingArrayType(getUnionType(map(types, getElementTypeOfEvolvingArrayType))) :
getUnionType(sameMap(types, finalizeEvolvingArrayType), subtypeReduction);
}
// Return true if the given node is 'x' in an 'x.length', x.push(value)', 'x.unshift(value)' or
// 'x[n] = value' operation, where 'n' is an expression of type any, undefined, or a number-like type.
function isEvolvingArrayOperationTarget(node: Node) {
const root = getReferenceRoot(node);
const parent = root.parent;
const isLengthPushOrUnshift = parent.kind === SyntaxKind.PropertyAccessExpression && (
(<PropertyAccessExpression>parent).name.text === "length" ||
parent.parent.kind === SyntaxKind.CallExpression && isPushOrUnshiftIdentifier((<PropertyAccessExpression>parent).name));
const isElementAssignment = parent.kind === SyntaxKind.ElementAccessExpression &&
(<ElementAccessExpression>parent).expression === root &&
parent.parent.kind === SyntaxKind.BinaryExpression &&
(<BinaryExpression>parent.parent).operatorToken.kind === SyntaxKind.EqualsToken &&
(<BinaryExpression>parent.parent).left === parent &&
!isAssignmentTarget(parent.parent) &&
isTypeAnyOrAllConstituentTypesHaveKind(getTypeOfExpression((<ElementAccessExpression>parent).argumentExpression), TypeFlags.NumberLike | TypeFlags.Undefined);
return isLengthPushOrUnshift || isElementAssignment;
}
function maybeTypePredicateCall(node: CallExpression) {
const links = getNodeLinks(node);
if (links.maybeTypePredicate === undefined) {
links.maybeTypePredicate = getMaybeTypePredicate(node);
}
return links.maybeTypePredicate;
}
function getMaybeTypePredicate(node: CallExpression) {
if (node.expression.kind !== SyntaxKind.SuperKeyword) {
const funcType = checkNonNullExpression(node.expression);
if (funcType !== silentNeverType) {
const apparentType = getApparentType(funcType);
if (apparentType !== unknownType) {
const callSignatures = getSignaturesOfType(apparentType, SignatureKind.Call);
return !!forEach(callSignatures, sig => sig.typePredicate);
}
}
}
return false;
}
function getFlowTypeOfReference(reference: Node, declaredType: Type, initialType = declaredType, flowContainer?: Node, couldBeUninitialized?: boolean) {
let key: string;
if (!reference.flowNode || !couldBeUninitialized && !(declaredType.flags & TypeFlags.Narrowable)) {
return declaredType;
}
const visitedFlowStart = visitedFlowCount;
const evolvedType = getTypeFromFlowType(getTypeAtFlowNode(reference.flowNode));
visitedFlowCount = visitedFlowStart;
// When the reference is 'x' in an 'x.length', 'x.push(value)', 'x.unshift(value)' or x[n] = value' operation,
// we give type 'any[]' to 'x' instead of using the type determined by control flow analysis such that operations
// on empty arrays are possible without implicit any errors and new element types can be inferred without
// type mismatch errors.
const resultType = getObjectFlags(evolvedType) & ObjectFlags.EvolvingArray && isEvolvingArrayOperationTarget(reference) ? anyArrayType : finalizeEvolvingArrayType(evolvedType);
if (reference.parent.kind === SyntaxKind.NonNullExpression && getTypeWithFacts(resultType, TypeFacts.NEUndefinedOrNull).flags & TypeFlags.Never) {
return declaredType;
}
return resultType;
function getTypeAtFlowNode(flow: FlowNode): FlowType {
while (true) {
if (flow.flags & FlowFlags.Shared) {
// We cache results of flow type resolution for shared nodes that were previously visited in
// the same getFlowTypeOfReference invocation. A node is considered shared when it is the
// antecedent of more than one node.
for (let i = visitedFlowStart; i < visitedFlowCount; i++) {
if (visitedFlowNodes[i] === flow) {
return visitedFlowTypes[i];
}
}
}
let type: FlowType;
if (flow.flags & FlowFlags.AfterFinally) {
// block flow edge: finally -> pre-try (for larger explanation check comment in binder.ts - bindTryStatement
(<AfterFinallyFlow>flow).locked = true;
type = getTypeAtFlowNode((<AfterFinallyFlow>flow).antecedent);
(<AfterFinallyFlow>flow).locked = false;
}
else if (flow.flags & FlowFlags.PreFinally) {
// locked pre-finally flows are filtered out in getTypeAtFlowBranchLabel
// so here just redirect to antecedent
flow = (<PreFinallyFlow>flow).antecedent;
continue;
}
else if (flow.flags & FlowFlags.Assignment) {
type = getTypeAtFlowAssignment(<FlowAssignment>flow);
if (!type) {
flow = (<FlowAssignment>flow).antecedent;
continue;
}
}
else if (flow.flags & FlowFlags.Condition) {
type = getTypeAtFlowCondition(<FlowCondition>flow);
}
else if (flow.flags & FlowFlags.SwitchClause) {
type = getTypeAtSwitchClause(<FlowSwitchClause>flow);
}
else if (flow.flags & FlowFlags.Label) {
if ((<FlowLabel>flow).antecedents.length === 1) {
flow = (<FlowLabel>flow).antecedents[0];
continue;
}
type = flow.flags & FlowFlags.BranchLabel ?
getTypeAtFlowBranchLabel(<FlowLabel>flow) :
getTypeAtFlowLoopLabel(<FlowLabel>flow);
}
else if (flow.flags & FlowFlags.ArrayMutation) {
type = getTypeAtFlowArrayMutation(<FlowArrayMutation>flow);
if (!type) {
flow = (<FlowArrayMutation>flow).antecedent;
continue;
}
}
else if (flow.flags & FlowFlags.Start) {
// Check if we should continue with the control flow of the containing function.
const container = (<FlowStart>flow).container;
if (container && container !== flowContainer && reference.kind !== SyntaxKind.PropertyAccessExpression && reference.kind !== SyntaxKind.ThisKeyword) {
flow = container.flowNode;
continue;
}
// At the top of the flow we have the initial type.
type = initialType;
}
else {
// Unreachable code errors are reported in the binding phase. Here we
// simply return the non-auto declared type to reduce follow-on errors.
type = convertAutoToAny(declaredType);
}
if (flow.flags & FlowFlags.Shared) {
// Record visited node and the associated type in the cache.
visitedFlowNodes[visitedFlowCount] = flow;
visitedFlowTypes[visitedFlowCount] = type;
visitedFlowCount++;
}
return type;
}
}
function getTypeAtFlowAssignment(flow: FlowAssignment) {
const node = flow.node;
// Assignments only narrow the computed type if the declared type is a union type. Thus, we
// only need to evaluate the assigned type if the declared type is a union type.
if (isMatchingReference(reference, node)) {
if (getAssignmentTargetKind(node) === AssignmentKind.Compound) {
const flowType = getTypeAtFlowNode(flow.antecedent);
return createFlowType(getBaseTypeOfLiteralType(getTypeFromFlowType(flowType)), isIncomplete(flowType));
}
if (declaredType === autoType || declaredType === autoArrayType) {
if (isEmptyArrayAssignment(node)) {
return getEvolvingArrayType(neverType);
}
const assignedType = getBaseTypeOfLiteralType(getInitialOrAssignedType(node));
return isTypeAssignableTo(assignedType, declaredType) ? assignedType : anyArrayType;
}
if (declaredType.flags & TypeFlags.Union) {
return getAssignmentReducedType(<UnionType>declaredType, getInitialOrAssignedType(node));
}
return declaredType;
}
// We didn't have a direct match. However, if the reference is a dotted name, this
// may be an assignment to a left hand part of the reference. For example, for a
// reference 'x.y.z', we may be at an assignment to 'x.y' or 'x'. In that case,
// return the declared type.
if (containsMatchingReference(reference, node)) {
return declaredType;
}
// Assignment doesn't affect reference
return undefined;
}
function getTypeAtFlowArrayMutation(flow: FlowArrayMutation): FlowType {
const node = flow.node;
const expr = node.kind === SyntaxKind.CallExpression ?
(<PropertyAccessExpression>(<CallExpression>node).expression).expression :
(<ElementAccessExpression>(<BinaryExpression>node).left).expression;
if (isMatchingReference(reference, getReferenceCandidate(expr))) {
const flowType = getTypeAtFlowNode(flow.antecedent);
const type = getTypeFromFlowType(flowType);
if (getObjectFlags(type) & ObjectFlags.EvolvingArray) {
let evolvedType = <EvolvingArrayType>type;
if (node.kind === SyntaxKind.CallExpression) {
for (const arg of (<CallExpression>node).arguments) {
evolvedType = addEvolvingArrayElementType(evolvedType, arg);
}
}
else {
const indexType = getTypeOfExpression((<ElementAccessExpression>(<BinaryExpression>node).left).argumentExpression);
if (isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.NumberLike | TypeFlags.Undefined)) {
evolvedType = addEvolvingArrayElementType(evolvedType, (<BinaryExpression>node).right);
}
}
return evolvedType === type ? flowType : createFlowType(evolvedType, isIncomplete(flowType));
}
return flowType;
}
return undefined;
}
function getTypeAtFlowCondition(flow: FlowCondition): FlowType {
const flowType = getTypeAtFlowNode(flow.antecedent);
const type = getTypeFromFlowType(flowType);
if (type.flags & TypeFlags.Never) {
return flowType;
}
// If we have an antecedent type (meaning we're reachable in some way), we first
// attempt to narrow the antecedent type. If that produces the never type, and if
// the antecedent type is incomplete (i.e. a transient type in a loop), then we
// take the type guard as an indication that control *could* reach here once we
// have the complete type. We proceed by switching to the silent never type which
// doesn't report errors when operators are applied to it. Note that this is the
// *only* place a silent never type is ever generated.
const assumeTrue = (flow.flags & FlowFlags.TrueCondition) !== 0;
const nonEvolvingType = finalizeEvolvingArrayType(type);
const narrowedType = narrowType(nonEvolvingType, flow.expression, assumeTrue);
if (narrowedType === nonEvolvingType) {
return flowType;
}
const incomplete = isIncomplete(flowType);
const resultType = incomplete && narrowedType.flags & TypeFlags.Never ? silentNeverType : narrowedType;
return createFlowType(resultType, incomplete);
}
function getTypeAtSwitchClause(flow: FlowSwitchClause): FlowType {
const flowType = getTypeAtFlowNode(flow.antecedent);
let type = getTypeFromFlowType(flowType);
const expr = flow.switchStatement.expression;
if (isMatchingReference(reference, expr)) {
type = narrowTypeBySwitchOnDiscriminant(type, flow.switchStatement, flow.clauseStart, flow.clauseEnd);
}
else if (isMatchingReferenceDiscriminant(expr)) {
type = narrowTypeByDiscriminant(type, <PropertyAccessExpression>expr, t => narrowTypeBySwitchOnDiscriminant(t, flow.switchStatement, flow.clauseStart, flow.clauseEnd));
}
return createFlowType(type, isIncomplete(flowType));
}
function getTypeAtFlowBranchLabel(flow: FlowLabel): FlowType {
const antecedentTypes: Type[] = [];
let subtypeReduction = false;
let seenIncomplete = false;
for (const antecedent of flow.antecedents) {
if (antecedent.flags & FlowFlags.PreFinally && (<PreFinallyFlow>antecedent).lock.locked) {
// if flow correspond to branch from pre-try to finally and this branch is locked - this means that
// we initially have started following the flow outside the finally block.
// in this case we should ignore this branch.
continue;
}
const flowType = getTypeAtFlowNode(antecedent);
const type = getTypeFromFlowType(flowType);
// If the type at a particular antecedent path is the declared type and the
// reference is known to always be assigned (i.e. when declared and initial types
// are the same), there is no reason to process more antecedents since the only
// possible outcome is subtypes that will be removed in the final union type anyway.
if (type === declaredType && declaredType === initialType) {
return type;
}
if (!contains(antecedentTypes, type)) {
antecedentTypes.push(type);
}
// If an antecedent type is not a subset of the declared type, we need to perform
// subtype reduction. This happens when a "foreign" type is injected into the control
// flow using the instanceof operator or a user defined type predicate.
if (!isTypeSubsetOf(type, declaredType)) {
subtypeReduction = true;
}
if (isIncomplete(flowType)) {
seenIncomplete = true;
}
}
return createFlowType(getUnionOrEvolvingArrayType(antecedentTypes, subtypeReduction), seenIncomplete);
}
function getTypeAtFlowLoopLabel(flow: FlowLabel): FlowType {
// If we have previously computed the control flow type for the reference at
// this flow loop junction, return the cached type.
const id = getFlowNodeId(flow);
const cache = flowLoopCaches[id] || (flowLoopCaches[id] = createMap<Type>());
if (!key) {
key = getFlowCacheKey(reference);
}
const cached = cache.get(key);
if (cached) {
return cached;
}
// If this flow loop junction and reference are already being processed, return
// the union of the types computed for each branch so far, marked as incomplete.
// It is possible to see an empty array in cases where loops are nested and the
// back edge of the outer loop reaches an inner loop that is already being analyzed.
// In such cases we restart the analysis of the inner loop, which will then see
// a non-empty in-process array for the outer loop and eventually terminate because
// the first antecedent of a loop junction is always the non-looping control flow
// path that leads to the top.
for (let i = flowLoopStart; i < flowLoopCount; i++) {
if (flowLoopNodes[i] === flow && flowLoopKeys[i] === key && flowLoopTypes[i].length) {
return createFlowType(getUnionOrEvolvingArrayType(flowLoopTypes[i], /*subtypeReduction*/ false), /*incomplete*/ true);
}
}
// Add the flow loop junction and reference to the in-process stack and analyze
// each antecedent code path.
const antecedentTypes: Type[] = [];
let subtypeReduction = false;
let firstAntecedentType: FlowType;
flowLoopNodes[flowLoopCount] = flow;
flowLoopKeys[flowLoopCount] = key;
flowLoopTypes[flowLoopCount] = antecedentTypes;
for (const antecedent of flow.antecedents) {
flowLoopCount++;
const flowType = getTypeAtFlowNode(antecedent);
flowLoopCount--;
if (!firstAntecedentType) {
firstAntecedentType = flowType;
}
const type = getTypeFromFlowType(flowType);
// If we see a value appear in the cache it is a sign that control flow analysis
// was restarted and completed by checkExpressionCached. We can simply pick up
// the resulting type and bail out.
const cached = cache.get(key);
if (cached) {
return cached;
}
if (!contains(antecedentTypes, type)) {
antecedentTypes.push(type);
}
// If an antecedent type is not a subset of the declared type, we need to perform
// subtype reduction. This happens when a "foreign" type is injected into the control
// flow using the instanceof operator or a user defined type predicate.
if (!isTypeSubsetOf(type, declaredType)) {
subtypeReduction = true;
}
// If the type at a particular antecedent path is the declared type there is no
// reason to process more antecedents since the only possible outcome is subtypes
// that will be removed in the final union type anyway.
if (type === declaredType) {
break;
}
}
// The result is incomplete if the first antecedent (the non-looping control flow path)
// is incomplete.
const result = getUnionOrEvolvingArrayType(antecedentTypes, subtypeReduction);
if (isIncomplete(firstAntecedentType)) {
return createFlowType(result, /*incomplete*/ true);
}
cache.set(key, result);
return result;
}
function isMatchingReferenceDiscriminant(expr: Expression) {
return expr.kind === SyntaxKind.PropertyAccessExpression &&
declaredType.flags & TypeFlags.Union &&
isMatchingReference(reference, (<PropertyAccessExpression>expr).expression) &&
isDiscriminantProperty(declaredType, (<PropertyAccessExpression>expr).name.text);
}
function narrowTypeByDiscriminant(type: Type, propAccess: PropertyAccessExpression, narrowType: (t: Type) => Type): Type {
const propName = propAccess.name.text;
const propType = getTypeOfPropertyOfType(type, propName);
const narrowedPropType = propType && narrowType(propType);
return propType === narrowedPropType ? type : filterType(type, t => isTypeComparableTo(getTypeOfPropertyOfType(t, propName), narrowedPropType));
}
function narrowTypeByTruthiness(type: Type, expr: Expression, assumeTrue: boolean): Type {
if (isMatchingReference(reference, expr)) {
return getTypeWithFacts(type, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy);
}
if (isMatchingReferenceDiscriminant(expr)) {
return narrowTypeByDiscriminant(type, <PropertyAccessExpression>expr, t => getTypeWithFacts(t, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy));
}
if (containsMatchingReferenceDiscriminant(reference, expr)) {
return declaredType;
}
return type;
}
function narrowTypeByBinaryExpression(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type {
switch (expr.operatorToken.kind) {
case SyntaxKind.EqualsToken:
return narrowTypeByTruthiness(type, expr.left, assumeTrue);
case SyntaxKind.EqualsEqualsToken:
case SyntaxKind.ExclamationEqualsToken:
case SyntaxKind.EqualsEqualsEqualsToken:
case SyntaxKind.ExclamationEqualsEqualsToken:
const operator = expr.operatorToken.kind;
const left = getReferenceCandidate(expr.left);
const right = getReferenceCandidate(expr.right);
if (left.kind === SyntaxKind.TypeOfExpression && right.kind === SyntaxKind.StringLiteral) {
return narrowTypeByTypeof(type, <TypeOfExpression>left, operator, <LiteralExpression>right, assumeTrue);
}
if (right.kind === SyntaxKind.TypeOfExpression && left.kind === SyntaxKind.StringLiteral) {
return narrowTypeByTypeof(type, <TypeOfExpression>right, operator, <LiteralExpression>left, assumeTrue);
}
if (isMatchingReference(reference, left)) {
return narrowTypeByEquality(type, operator, right, assumeTrue);
}
if (isMatchingReference(reference, right)) {
return narrowTypeByEquality(type, operator, left, assumeTrue);
}
if (isMatchingReferenceDiscriminant(left)) {
return narrowTypeByDiscriminant(type, <PropertyAccessExpression>left, t => narrowTypeByEquality(t, operator, right, assumeTrue));
}
if (isMatchingReferenceDiscriminant(right)) {
return narrowTypeByDiscriminant(type, <PropertyAccessExpression>right, t => narrowTypeByEquality(t, operator, left, assumeTrue));
}
if (containsMatchingReferenceDiscriminant(reference, left) || containsMatchingReferenceDiscriminant(reference, right)) {
return declaredType;
}
break;
case SyntaxKind.InstanceOfKeyword:
return narrowTypeByInstanceof(type, expr, assumeTrue);
case SyntaxKind.CommaToken:
return narrowType(type, expr.right, assumeTrue);
}
return type;
}
function narrowTypeByEquality(type: Type, operator: SyntaxKind, value: Expression, assumeTrue: boolean): Type {
if (type.flags & TypeFlags.Any) {
return type;
}
if (operator === SyntaxKind.ExclamationEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken) {
assumeTrue = !assumeTrue;
}
const valueType = getTypeOfExpression(value);
if (valueType.flags & TypeFlags.Nullable) {
if (!strictNullChecks) {
return type;
}
const doubleEquals = operator === SyntaxKind.EqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsToken;
const facts = doubleEquals ?
assumeTrue ? TypeFacts.EQUndefinedOrNull : TypeFacts.NEUndefinedOrNull :
value.kind === SyntaxKind.NullKeyword ?
assumeTrue ? TypeFacts.EQNull : TypeFacts.NENull :
assumeTrue ? TypeFacts.EQUndefined : TypeFacts.NEUndefined;
return getTypeWithFacts(type, facts);
}
if (type.flags & TypeFlags.NotUnionOrUnit) {
return type;
}
if (assumeTrue) {
const narrowedType = filterType(type, t => areTypesComparable(t, valueType));
return narrowedType.flags & TypeFlags.Never ? type : replacePrimitivesWithLiterals(narrowedType, valueType);
}
if (isUnitType(valueType)) {
const regularType = getRegularTypeOfLiteralType(valueType);
return filterType(type, t => getRegularTypeOfLiteralType(t) !== regularType);
}
return type;
}
function narrowTypeByTypeof(type: Type, typeOfExpr: TypeOfExpression, operator: SyntaxKind, literal: LiteralExpression, assumeTrue: boolean): Type {
// We have '==', '!=', '====', or !==' operator with 'typeof xxx' and string literal operands
const target = getReferenceCandidate(typeOfExpr.expression);
if (!isMatchingReference(reference, target)) {
// For a reference of the form 'x.y', a 'typeof x === ...' type guard resets the
// narrowed type of 'y' to its declared type.
if (containsMatchingReference(reference, target)) {
return declaredType;
}
return type;
}
if (operator === SyntaxKind.ExclamationEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken) {
assumeTrue = !assumeTrue;
}
if (assumeTrue && !(type.flags & TypeFlags.Union)) {
// We narrow a non-union type to an exact primitive type if the non-union type
// is a supertype of that primitive type. For example, type 'any' can be narrowed
// to one of the primitive types.
const targetType = typeofTypesByName.get(literal.text);
if (targetType) {
if (isTypeSubtypeOf(targetType, type)) {
return targetType;
}
if (type.flags & TypeFlags.TypeVariable) {
const constraint = getBaseConstraintOfType(type) || anyType;
if (isTypeSubtypeOf(targetType, constraint)) {
return getIntersectionType([type, targetType]);
}
}
}
}
const facts = assumeTrue ?
typeofEQFacts.get(literal.text) || TypeFacts.TypeofEQHostObject :
typeofNEFacts.get(literal.text) || TypeFacts.TypeofNEHostObject;
return getTypeWithFacts(type, facts);
}
function narrowTypeBySwitchOnDiscriminant(type: Type, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number) {
// We only narrow if all case expressions specify values with unit types
const switchTypes = getSwitchClauseTypes(switchStatement);
if (!switchTypes.length) {
return type;
}
const clauseTypes = switchTypes.slice(clauseStart, clauseEnd);
const hasDefaultClause = clauseStart === clauseEnd || contains(clauseTypes, neverType);
const discriminantType = getUnionType(clauseTypes);
const caseType =
discriminantType.flags & TypeFlags.Never ? neverType :
replacePrimitivesWithLiterals(filterType(type, t => isTypeComparableTo(discriminantType, t)), discriminantType);
if (!hasDefaultClause) {
return caseType;
}
const defaultType = filterType(type, t => !(isUnitType(t) && contains(switchTypes, getRegularTypeOfLiteralType(t))));
return caseType.flags & TypeFlags.Never ? defaultType : getUnionType([caseType, defaultType]);
}
function narrowTypeByInstanceof(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type {
const left = getReferenceCandidate(expr.left);
if (!isMatchingReference(reference, left)) {
// For a reference of the form 'x.y', an 'x instanceof T' type guard resets the
// narrowed type of 'y' to its declared type.
if (containsMatchingReference(reference, left)) {
return declaredType;
}
return type;
}
// Check that right operand is a function type with a prototype property
const rightType = getTypeOfExpression(expr.right);
if (!isTypeSubtypeOf(rightType, globalFunctionType)) {
return type;
}
let targetType: Type;
const prototypeProperty = getPropertyOfType(rightType, "prototype");
if (prototypeProperty) {
// Target type is type of the prototype property
const prototypePropertyType = getTypeOfSymbol(prototypeProperty);
if (!isTypeAny(prototypePropertyType)) {
targetType = prototypePropertyType;
}
}
// Don't narrow from 'any' if the target type is exactly 'Object' or 'Function'
if (isTypeAny(type) && (targetType === globalObjectType || targetType === globalFunctionType)) {
return type;
}
if (!targetType) {
// Target type is type of construct signature
let constructSignatures: Signature[];
if (getObjectFlags(rightType) & ObjectFlags.Interface) {
constructSignatures = resolveDeclaredMembers(<InterfaceType>rightType).declaredConstructSignatures;
}
else if (getObjectFlags(rightType) & ObjectFlags.Anonymous) {
constructSignatures = getSignaturesOfType(rightType, SignatureKind.Construct);
}
if (constructSignatures && constructSignatures.length) {
targetType = getUnionType(map(constructSignatures, signature => getReturnTypeOfSignature(getErasedSignature(signature))));
}
}
if (targetType) {
return getNarrowedType(type, targetType, assumeTrue, isTypeInstanceOf);
}
return type;
}
function getNarrowedType(type: Type, candidate: Type, assumeTrue: boolean, isRelated: (source: Type, target: Type) => boolean) {
if (!assumeTrue) {
return filterType(type, t => !isRelated(t, candidate));
}
// If the current type is a union type, remove all constituents that couldn't be instances of
// the candidate type. If one or more constituents remain, return a union of those.
if (type.flags & TypeFlags.Union) {
const assignableType = filterType(type, t => isRelated(t, candidate));
if (!(assignableType.flags & TypeFlags.Never)) {
return assignableType;
}
}
// If the candidate type is a subtype of the target type, narrow to the candidate type.
// Otherwise, if the target type is assignable to the candidate type, keep the target type.
// Otherwise, if the candidate type is assignable to the target type, narrow to the candidate
// type. Otherwise, the types are completely unrelated, so narrow to an intersection of the
// two types.
return isTypeSubtypeOf(candidate, type) ? candidate :
isTypeAssignableTo(type, candidate) ? type :
isTypeAssignableTo(candidate, type) ? candidate :
getIntersectionType([type, candidate]);
}
function narrowTypeByTypePredicate(type: Type, callExpression: CallExpression, assumeTrue: boolean): Type {
if (!hasMatchingArgument(callExpression, reference) || !maybeTypePredicateCall(callExpression)) {
return type;
}
const signature = getResolvedSignature(callExpression);
const predicate = signature.typePredicate;
if (!predicate) {
return type;
}
// Don't narrow from 'any' if the predicate type is exactly 'Object' or 'Function'
if (isTypeAny(type) && (predicate.type === globalObjectType || predicate.type === globalFunctionType)) {
return type;
}
if (isIdentifierTypePredicate(predicate)) {
const predicateArgument = callExpression.arguments[predicate.parameterIndex - (signature.thisParameter ? 1 : 0)];
if (predicateArgument) {
if (isMatchingReference(reference, predicateArgument)) {
return getNarrowedType(type, predicate.type, assumeTrue, isTypeSubtypeOf);
}
if (containsMatchingReference(reference, predicateArgument)) {
return declaredType;
}
}
}
else {
const invokedExpression = skipParentheses(callExpression.expression);
if (invokedExpression.kind === SyntaxKind.ElementAccessExpression || invokedExpression.kind === SyntaxKind.PropertyAccessExpression) {
const accessExpression = invokedExpression as ElementAccessExpression | PropertyAccessExpression;
const possibleReference = skipParentheses(accessExpression.expression);
if (isMatchingReference(reference, possibleReference)) {
return getNarrowedType(type, predicate.type, assumeTrue, isTypeSubtypeOf);
}
if (containsMatchingReference(reference, possibleReference)) {
return declaredType;
}
}
}
return type;
}
// Narrow the given type based on the given expression having the assumed boolean value. The returned type
// will be a subtype or the same type as the argument.
function narrowType(type: Type, expr: Expression, assumeTrue: boolean): Type {
switch (expr.kind) {
case SyntaxKind.Identifier:
case SyntaxKind.ThisKeyword:
case SyntaxKind.SuperKeyword:
case SyntaxKind.PropertyAccessExpression:
return narrowTypeByTruthiness(type, expr, assumeTrue);
case SyntaxKind.CallExpression:
return narrowTypeByTypePredicate(type, <CallExpression>expr, assumeTrue);
case SyntaxKind.ParenthesizedExpression:
return narrowType(type, (<ParenthesizedExpression>expr).expression, assumeTrue);
case SyntaxKind.BinaryExpression:
return narrowTypeByBinaryExpression(type, <BinaryExpression>expr, assumeTrue);
case SyntaxKind.PrefixUnaryExpression:
if ((<PrefixUnaryExpression>expr).operator === SyntaxKind.ExclamationToken) {
return narrowType(type, (<PrefixUnaryExpression>expr).operand, !assumeTrue);
}
break;
}
return type;
}
}
function getTypeOfSymbolAtLocation(symbol: Symbol, location: Node) {
// If we have an identifier or a property access at the given location, if the location is
// an dotted name expression, and if the location is not an assignment target, obtain the type
// of the expression (which will reflect control flow analysis). If the expression indeed
// resolved to the given symbol, return the narrowed type.
if (location.kind === SyntaxKind.Identifier) {
if (isRightSideOfQualifiedNameOrPropertyAccess(location)) {
location = location.parent;
}
if (isPartOfExpression(location) && !isAssignmentTarget(location)) {
const type = getTypeOfExpression(<Expression>location);
if (getExportSymbolOfValueSymbolIfExported(getNodeLinks(location).resolvedSymbol) === symbol) {
return type;
}
}
}
// The location isn't a reference to the given symbol, meaning we're being asked
// a hypothetical question of what type the symbol would have if there was a reference
// to it at the given location. Since we have no control flow information for the
// hypothetical reference (control flow information is created and attached by the
// binder), we simply return the declared type of the symbol.
return getTypeOfSymbol(symbol);
}
function getControlFlowContainer(node: Node): Node {
return findAncestor(node.parent, node =>
isFunctionLike(node) && !getImmediatelyInvokedFunctionExpression(node) ||
node.kind === SyntaxKind.ModuleBlock ||
node.kind === SyntaxKind.SourceFile ||
node.kind === SyntaxKind.PropertyDeclaration);
}
// Check if a parameter is assigned anywhere within its declaring function.
function isParameterAssigned(symbol: Symbol) {
const func = <FunctionLikeDeclaration>getRootDeclaration(symbol.valueDeclaration).parent;
const links = getNodeLinks(func);
if (!(links.flags & NodeCheckFlags.AssignmentsMarked)) {
links.flags |= NodeCheckFlags.AssignmentsMarked;
if (!hasParentWithAssignmentsMarked(func)) {
markParameterAssignments(func);
}
}
return symbol.isAssigned || false;
}
function hasParentWithAssignmentsMarked(node: Node) {
return !!findAncestor(node.parent, node => isFunctionLike(node) && !!(getNodeLinks(node).flags & NodeCheckFlags.AssignmentsMarked));
}
function markParameterAssignments(node: Node) {
if (node.kind === SyntaxKind.Identifier) {
if (isAssignmentTarget(node)) {
const symbol = getResolvedSymbol(<Identifier>node);
if (symbol.valueDeclaration && getRootDeclaration(symbol.valueDeclaration).kind === SyntaxKind.Parameter) {
symbol.isAssigned = true;
}
}
}
else {
forEachChild(node, markParameterAssignments);
}
}
function isConstVariable(symbol: Symbol) {
return symbol.flags & SymbolFlags.Variable && (getDeclarationNodeFlagsFromSymbol(symbol) & NodeFlags.Const) !== 0 && getTypeOfSymbol(symbol) !== autoArrayType;
}
/** remove undefined from the annotated type of a parameter when there is an initializer (that doesn't include undefined) */
function removeOptionalityFromDeclaredType(declaredType: Type, declaration: VariableLikeDeclaration): Type {
const annotationIncludesUndefined = strictNullChecks &&
declaration.kind === SyntaxKind.Parameter &&
declaration.initializer &&
getFalsyFlags(declaredType) & TypeFlags.Undefined &&
!(getFalsyFlags(checkExpression(declaration.initializer)) & TypeFlags.Undefined);
return annotationIncludesUndefined ? getTypeWithFacts(declaredType, TypeFacts.NEUndefined) : declaredType;
}
function isApparentTypePosition(node: Node) {
const parent = node.parent;
return parent.kind === SyntaxKind.PropertyAccessExpression ||
parent.kind === SyntaxKind.CallExpression && (<CallExpression>parent).expression === node ||
parent.kind === SyntaxKind.ElementAccessExpression && (<ElementAccessExpression>parent).expression === node;
}
function typeHasNullableConstraint(type: Type) {
return type.flags & TypeFlags.TypeVariable && maybeTypeOfKind(getBaseConstraintOfType(type) || emptyObjectType, TypeFlags.Nullable);
}
function getDeclaredOrApparentType(symbol: Symbol, node: Node) {
// When a node is the left hand expression of a property access, element access, or call expression,
// and the type of the node includes type variables with constraints that are nullable, we fetch the
// apparent type of the node *before* performing control flow analysis such that narrowings apply to
// the constraint type.
const type = getTypeOfSymbol(symbol);
if (isApparentTypePosition(node) && forEachType(type, typeHasNullableConstraint)) {
return mapType(getWidenedType(type), getApparentType);
}
return type;
}
function checkIdentifier(node: Identifier): Type {
const symbol = getResolvedSymbol(node);
if (symbol === unknownSymbol) {
return unknownType;
}
// As noted in ECMAScript 6 language spec, arrow functions never have an arguments objects.
// Although in down-level emit of arrow function, we emit it using function expression which means that
// arguments objects will be bound to the inner object; emitting arrow function natively in ES6, arguments objects
// will be bound to non-arrow function that contain this arrow function. This results in inconsistent behavior.
// To avoid that we will give an error to users if they use arguments objects in arrow function so that they
// can explicitly bound arguments objects
if (symbol === argumentsSymbol) {
const container = getContainingFunction(node);
if (languageVersion < ScriptTarget.ES2015) {
if (container.kind === SyntaxKind.ArrowFunction) {
error(node, Diagnostics.The_arguments_object_cannot_be_referenced_in_an_arrow_function_in_ES3_and_ES5_Consider_using_a_standard_function_expression);
}
else if (hasModifier(container, ModifierFlags.Async)) {
error(node, Diagnostics.The_arguments_object_cannot_be_referenced_in_an_async_function_or_method_in_ES3_and_ES5_Consider_using_a_standard_function_or_method);
}
}
getNodeLinks(container).flags |= NodeCheckFlags.CaptureArguments;
return getTypeOfSymbol(symbol);
}
if (symbol.flags & SymbolFlags.Alias && !isInTypeQuery(node) && !isConstEnumOrConstEnumOnlyModule(resolveAlias(symbol))) {
markAliasSymbolAsReferenced(symbol);
}
const localOrExportSymbol = getExportSymbolOfValueSymbolIfExported(symbol);
if (localOrExportSymbol.flags & SymbolFlags.Class) {
const declaration = localOrExportSymbol.valueDeclaration;
// Due to the emit for class decorators, any reference to the class from inside of the class body
// must instead be rewritten to point to a temporary variable to avoid issues with the double-bind
// behavior of class names in ES6.
if (declaration.kind === SyntaxKind.ClassDeclaration
&& nodeIsDecorated(declaration)) {
let container = getContainingClass(node);
while (container !== undefined) {
if (container === declaration && container.name !== node) {
getNodeLinks(declaration).flags |= NodeCheckFlags.ClassWithConstructorReference;
getNodeLinks(node).flags |= NodeCheckFlags.ConstructorReferenceInClass;
break;
}
container = getContainingClass(container);
}
}
else if (declaration.kind === SyntaxKind.ClassExpression) {
// When we emit a class expression with static members that contain a reference
// to the constructor in the initializer, we will need to substitute that
// binding with an alias as the class name is not in scope.
let container = getThisContainer(node, /*includeArrowFunctions*/ false);
while (container !== undefined) {
if (container.parent === declaration) {
if (container.kind === SyntaxKind.PropertyDeclaration && hasModifier(container, ModifierFlags.Static)) {
getNodeLinks(declaration).flags |= NodeCheckFlags.ClassWithConstructorReference;
getNodeLinks(node).flags |= NodeCheckFlags.ConstructorReferenceInClass;
}
break;
}
container = getThisContainer(container, /*includeArrowFunctions*/ false);
}
}
}
checkCollisionWithCapturedSuperVariable(node, node);
checkCollisionWithCapturedThisVariable(node, node);
checkCollisionWithCapturedNewTargetVariable(node, node);
checkNestedBlockScopedBinding(node, symbol);
const type = getDeclaredOrApparentType(localOrExportSymbol, node);
const declaration = localOrExportSymbol.valueDeclaration;
const assignmentKind = getAssignmentTargetKind(node);
if (assignmentKind) {
if (!(localOrExportSymbol.flags & SymbolFlags.Variable)) {
error(node, Diagnostics.Cannot_assign_to_0_because_it_is_not_a_variable, symbolToString(symbol));
return unknownType;
}
if (isReadonlySymbol(localOrExportSymbol)) {
error(node, Diagnostics.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property, symbolToString(symbol));
return unknownType;
}
}
// We only narrow variables and parameters occurring in a non-assignment position. For all other
// entities we simply return the declared type.
if (!(localOrExportSymbol.flags & SymbolFlags.Variable) || assignmentKind === AssignmentKind.Definite || !declaration) {
return type;
}
// The declaration container is the innermost function that encloses the declaration of the variable
// or parameter. The flow container is the innermost function starting with which we analyze the control
// flow graph to determine the control flow based type.
const isParameter = getRootDeclaration(declaration).kind === SyntaxKind.Parameter;
const declarationContainer = getControlFlowContainer(declaration);
let flowContainer = getControlFlowContainer(node);
const isOuterVariable = flowContainer !== declarationContainer;
// When the control flow originates in a function expression or arrow function and we are referencing
// a const variable or parameter from an outer function, we extend the origin of the control flow
// analysis to include the immediately enclosing function.
while (flowContainer !== declarationContainer && (flowContainer.kind === SyntaxKind.FunctionExpression ||
flowContainer.kind === SyntaxKind.ArrowFunction || isObjectLiteralOrClassExpressionMethod(flowContainer)) &&
(isConstVariable(localOrExportSymbol) || isParameter && !isParameterAssigned(localOrExportSymbol))) {
flowContainer = getControlFlowContainer(flowContainer);
}
// We only look for uninitialized variables in strict null checking mode, and only when we can analyze
// the entire control flow graph from the variable's declaration (i.e. when the flow container and
// declaration container are the same).
const assumeInitialized = isParameter || isOuterVariable ||
type !== autoType && type !== autoArrayType && (!strictNullChecks || (type.flags & TypeFlags.Any) !== 0 || isInTypeQuery(node) || node.parent.kind === SyntaxKind.ExportSpecifier) ||
isInAmbientContext(declaration);
const initialType = assumeInitialized ? (isParameter ? removeOptionalityFromDeclaredType(type, getRootDeclaration(declaration) as VariableLikeDeclaration) : type) :
type === autoType || type === autoArrayType ? undefinedType :
getNullableType(type, TypeFlags.Undefined);
const flowType = getFlowTypeOfReference(node, type, initialType, flowContainer, !assumeInitialized);
// A variable is considered uninitialized when it is possible to analyze the entire control flow graph
// from declaration to use, and when the variable's declared type doesn't include undefined but the
// control flow based type does include undefined.
if (type === autoType || type === autoArrayType) {
if (flowType === autoType || flowType === autoArrayType) {
if (noImplicitAny) {
error(getNameOfDeclaration(declaration), Diagnostics.Variable_0_implicitly_has_type_1_in_some_locations_where_its_type_cannot_be_determined, symbolToString(symbol), typeToString(flowType));
error(node, Diagnostics.Variable_0_implicitly_has_an_1_type, symbolToString(symbol), typeToString(flowType));
}
return convertAutoToAny(flowType);
}
}
else if (!assumeInitialized && !(getFalsyFlags(type) & TypeFlags.Undefined) && getFalsyFlags(flowType) & TypeFlags.Undefined) {
error(node, Diagnostics.Variable_0_is_used_before_being_assigned, symbolToString(symbol));
// Return the declared type to reduce follow-on errors
return type;
}
return assignmentKind ? getBaseTypeOfLiteralType(flowType) : flowType;
}
function isInsideFunction(node: Node, threshold: Node): boolean {
return !!findAncestor(node, n => n === threshold ? "quit" : isFunctionLike(n));
}
function checkNestedBlockScopedBinding(node: Identifier, symbol: Symbol): void {
if (languageVersion >= ScriptTarget.ES2015 ||
(symbol.flags & (SymbolFlags.BlockScopedVariable | SymbolFlags.Class)) === 0 ||
symbol.valueDeclaration.parent.kind === SyntaxKind.CatchClause) {
return;
}
// 1. walk from the use site up to the declaration and check
// if there is anything function like between declaration and use-site (is binding/class is captured in function).
// 2. walk from the declaration up to the boundary of lexical environment and check
// if there is an iteration statement in between declaration and boundary (is binding/class declared inside iteration statement)
const container = getEnclosingBlockScopeContainer(symbol.valueDeclaration);
const usedInFunction = isInsideFunction(node.parent, container);
let current = container;
let containedInIterationStatement = false;
while (current && !nodeStartsNewLexicalEnvironment(current)) {
if (isIterationStatement(current, /*lookInLabeledStatements*/ false)) {
containedInIterationStatement = true;
break;
}
current = current.parent;
}
if (containedInIterationStatement) {
if (usedInFunction) {
// mark iteration statement as containing block-scoped binding captured in some function
getNodeLinks(current).flags |= NodeCheckFlags.LoopWithCapturedBlockScopedBinding;
}
// mark variables that are declared in loop initializer and reassigned inside the body of ForStatement.
// if body of ForStatement will be converted to function then we'll need a extra machinery to propagate reassigned values back.
if (container.kind === SyntaxKind.ForStatement &&
getAncestor(symbol.valueDeclaration, SyntaxKind.VariableDeclarationList).parent === container &&
isAssignedInBodyOfForStatement(node, <ForStatement>container)) {
getNodeLinks(symbol.valueDeclaration).flags |= NodeCheckFlags.NeedsLoopOutParameter;
}
// set 'declared inside loop' bit on the block-scoped binding
getNodeLinks(symbol.valueDeclaration).flags |= NodeCheckFlags.BlockScopedBindingInLoop;
}
if (usedInFunction) {
getNodeLinks(symbol.valueDeclaration).flags |= NodeCheckFlags.CapturedBlockScopedBinding;
}
}
function isAssignedInBodyOfForStatement(node: Identifier, container: ForStatement): boolean {
// skip parenthesized nodes
let current: Node = node;
while (current.parent.kind === SyntaxKind.ParenthesizedExpression) {
current = current.parent;
}
// check if node is used as LHS in some assignment expression
let isAssigned = false;
if (isAssignmentTarget(current)) {
isAssigned = true;
}
else if ((current.parent.kind === SyntaxKind.PrefixUnaryExpression || current.parent.kind === SyntaxKind.PostfixUnaryExpression)) {
const expr = <PrefixUnaryExpression | PostfixUnaryExpression>current.parent;
isAssigned = expr.operator === SyntaxKind.PlusPlusToken || expr.operator === SyntaxKind.MinusMinusToken;
}
if (!isAssigned) {
return false;
}
// at this point we know that node is the target of assignment
// now check that modification happens inside the statement part of the ForStatement
return !!findAncestor(current, n => n === container ? "quit" : n === container.statement);
}
function captureLexicalThis(node: Node, container: Node): void {
getNodeLinks(node).flags |= NodeCheckFlags.LexicalThis;
if (container.kind === SyntaxKind.PropertyDeclaration || container.kind === SyntaxKind.Constructor) {
const classNode = container.parent;
getNodeLinks(classNode).flags |= NodeCheckFlags.CaptureThis;
}
else {
getNodeLinks(container).flags |= NodeCheckFlags.CaptureThis;
}
}
function findFirstSuperCall(n: Node): Node {
if (isSuperCall(n)) {
return n;
}
else if (isFunctionLike(n)) {
return undefined;
}
return forEachChild(n, findFirstSuperCall);
}
/**
* Return a cached result if super-statement is already found.
* Otherwise, find a super statement in a given constructor function and cache the result in the node-links of the constructor
*
* @param constructor constructor-function to look for super statement
*/
function getSuperCallInConstructor(constructor: ConstructorDeclaration): ExpressionStatement {
const links = getNodeLinks(constructor);
// Only trying to find super-call if we haven't yet tried to find one. Once we try, we will record the result
if (links.hasSuperCall === undefined) {
links.superCall = <ExpressionStatement>findFirstSuperCall(constructor.body);
links.hasSuperCall = links.superCall ? true : false;
}
return links.superCall;
}
/**
* Check if the given class-declaration extends null then return true.
* Otherwise, return false
* @param classDecl a class declaration to check if it extends null
*/
function classDeclarationExtendsNull(classDecl: ClassDeclaration): boolean {
const classSymbol = getSymbolOfNode(classDecl);
const classInstanceType = <InterfaceType>getDeclaredTypeOfSymbol(classSymbol);
const baseConstructorType = getBaseConstructorTypeOfClass(classInstanceType);
return baseConstructorType === nullWideningType;
}
function checkThisBeforeSuper(node: Node, container: Node, diagnosticMessage: DiagnosticMessage) {
const containingClassDecl = <ClassDeclaration>container.parent;
const baseTypeNode = getClassExtendsHeritageClauseElement(containingClassDecl);
// If a containing class does not have extends clause or the class extends null
// skip checking whether super statement is called before "this" accessing.
if (baseTypeNode && !classDeclarationExtendsNull(containingClassDecl)) {
const superCall = getSuperCallInConstructor(<ConstructorDeclaration>container);
// We should give an error in the following cases:
// - No super-call
// - "this" is accessing before super-call.
// i.e super(this)
// this.x; super();
// We want to make sure that super-call is done before accessing "this" so that
// "this" is not accessed as a parameter of the super-call.
if (!superCall || superCall.end > node.pos) {
// In ES6, super inside constructor of class-declaration has to precede "this" accessing
error(node, diagnosticMessage);
}
}
}
function checkThisExpression(node: Node): Type {
// Stop at the first arrow function so that we can
// tell whether 'this' needs to be captured.
let container = getThisContainer(node, /* includeArrowFunctions */ true);
let needToCaptureLexicalThis = false;
if (container.kind === SyntaxKind.Constructor) {
checkThisBeforeSuper(node, container, Diagnostics.super_must_be_called_before_accessing_this_in_the_constructor_of_a_derived_class);
}
// Now skip arrow functions to get the "real" owner of 'this'.
if (container.kind === SyntaxKind.ArrowFunction) {
container = getThisContainer(container, /* includeArrowFunctions */ false);
// When targeting es6, arrow function lexically bind "this" so we do not need to do the work of binding "this" in emitted code
needToCaptureLexicalThis = (languageVersion < ScriptTarget.ES2015);
}
switch (container.kind) {
case SyntaxKind.ModuleDeclaration:
error(node, Diagnostics.this_cannot_be_referenced_in_a_module_or_namespace_body);
// do not return here so in case if lexical this is captured - it will be reflected in flags on NodeLinks
break;
case SyntaxKind.EnumDeclaration:
error(node, Diagnostics.this_cannot_be_referenced_in_current_location);
// do not return here so in case if lexical this is captured - it will be reflected in flags on NodeLinks
break;
case SyntaxKind.Constructor:
if (isInConstructorArgumentInitializer(node, container)) {
error(node, Diagnostics.this_cannot_be_referenced_in_constructor_arguments);
// do not return here so in case if lexical this is captured - it will be reflected in flags on NodeLinks
}
break;
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.PropertySignature:
if (getModifierFlags(container) & ModifierFlags.Static) {
error(node, Diagnostics.this_cannot_be_referenced_in_a_static_property_initializer);
// do not return here so in case if lexical this is captured - it will be reflected in flags on NodeLinks
}
break;
case SyntaxKind.ComputedPropertyName:
error(node, Diagnostics.this_cannot_be_referenced_in_a_computed_property_name);
break;
}
if (needToCaptureLexicalThis) {
captureLexicalThis(node, container);
}
if (isFunctionLike(container) &&
(!isInParameterInitializerBeforeContainingFunction(node) || getThisParameter(container))) {
// Note: a parameter initializer should refer to class-this unless function-this is explicitly annotated.
// If this is a function in a JS file, it might be a class method. Check if it's the RHS
// of a x.prototype.y = function [name]() { .... }
if (container.kind === SyntaxKind.FunctionExpression &&
container.parent.kind === SyntaxKind.BinaryExpression &&
getSpecialPropertyAssignmentKind(container.parent as BinaryExpression) === SpecialPropertyAssignmentKind.PrototypeProperty) {
// Get the 'x' of 'x.prototype.y = f' (here, 'f' is 'container')
const className = (((container.parent as BinaryExpression) // x.prototype.y = f
.left as PropertyAccessExpression) // x.prototype.y
.expression as PropertyAccessExpression) // x.prototype
.expression; // x
const classSymbol = checkExpression(className).symbol;
if (classSymbol && classSymbol.members && (classSymbol.flags & SymbolFlags.Function)) {
return getInferredClassType(classSymbol);
}
}
const thisType = getThisTypeOfDeclaration(container) || getContextualThisParameterType(container);
if (thisType) {
return thisType;
}
}
if (isClassLike(container.parent)) {
const symbol = getSymbolOfNode(container.parent);
const type = hasModifier(container, ModifierFlags.Static) ? getTypeOfSymbol(symbol) : (<InterfaceType>getDeclaredTypeOfSymbol(symbol)).thisType;
return getFlowTypeOfReference(node, type);
}
if (isInJavaScriptFile(node)) {
const type = getTypeForThisExpressionFromJSDoc(container);
if (type && type !== unknownType) {
return type;
}
}
if (noImplicitThis) {
// With noImplicitThis, functions may not reference 'this' if it has type 'any'
error(node, Diagnostics.this_implicitly_has_type_any_because_it_does_not_have_a_type_annotation);
}
return anyType;
}
function getTypeForThisExpressionFromJSDoc(node: Node) {
const jsdocType = getJSDocType(node);
if (jsdocType && jsdocType.kind === SyntaxKind.JSDocFunctionType) {
const jsDocFunctionType = <JSDocFunctionType>jsdocType;
if (jsDocFunctionType.parameters.length > 0 && jsDocFunctionType.parameters[0].type.kind === SyntaxKind.JSDocThisType) {
return getTypeFromTypeNode(jsDocFunctionType.parameters[0].type);
}
}
}
function isInConstructorArgumentInitializer(node: Node, constructorDecl: Node): boolean {
return !!findAncestor(node, n => n === constructorDecl ? "quit" : n.kind === SyntaxKind.Parameter);
}
function checkSuperExpression(node: Node): Type {
const isCallExpression = node.parent.kind === SyntaxKind.CallExpression && (<CallExpression>node.parent).expression === node;
let container = getSuperContainer(node, /*stopOnFunctions*/ true);
let needToCaptureLexicalThis = false;
// adjust the container reference in case if super is used inside arrow functions with arbitrarily deep nesting
if (!isCallExpression) {
while (container && container.kind === SyntaxKind.ArrowFunction) {
container = getSuperContainer(container, /*stopOnFunctions*/ true);
needToCaptureLexicalThis = languageVersion < ScriptTarget.ES2015;
}
}
const canUseSuperExpression = isLegalUsageOfSuperExpression(container);
let nodeCheckFlag: NodeCheckFlags = 0;
if (!canUseSuperExpression) {
// issue more specific error if super is used in computed property name
// class A { foo() { return "1" }}
// class B {
// [super.foo()]() {}
// }
const current = findAncestor(node, n => n === container ? "quit" : n.kind === SyntaxKind.ComputedPropertyName);
if (current && current.kind === SyntaxKind.ComputedPropertyName) {
error(node, Diagnostics.super_cannot_be_referenced_in_a_computed_property_name);
}
else if (isCallExpression) {
error(node, Diagnostics.Super_calls_are_not_permitted_outside_constructors_or_in_nested_functions_inside_constructors);
}
else if (!container || !container.parent || !(isClassLike(container.parent) || container.parent.kind === SyntaxKind.ObjectLiteralExpression)) {
error(node, Diagnostics.super_can_only_be_referenced_in_members_of_derived_classes_or_object_literal_expressions);
}
else {
error(node, Diagnostics.super_property_access_is_permitted_only_in_a_constructor_member_function_or_member_accessor_of_a_derived_class);
}
return unknownType;
}
if (!isCallExpression && container.kind === SyntaxKind.Constructor) {
checkThisBeforeSuper(node, container, Diagnostics.super_must_be_called_before_accessing_a_property_of_super_in_the_constructor_of_a_derived_class);
}
if ((getModifierFlags(container) & ModifierFlags.Static) || isCallExpression) {
nodeCheckFlag = NodeCheckFlags.SuperStatic;
}
else {
nodeCheckFlag = NodeCheckFlags.SuperInstance;
}
getNodeLinks(node).flags |= nodeCheckFlag;
// Due to how we emit async functions, we need to specialize the emit for an async method that contains a `super` reference.
// This is due to the fact that we emit the body of an async function inside of a generator function. As generator
// functions cannot reference `super`, we emit a helper inside of the method body, but outside of the generator. This helper
// uses an arrow function, which is permitted to reference `super`.
//
// There are two primary ways we can access `super` from within an async method. The first is getting the value of a property
// or indexed access on super, either as part of a right-hand-side expression or call expression. The second is when setting the value
// of a property or indexed access, either as part of an assignment expression or destructuring assignment.
//
// The simplest case is reading a value, in which case we will emit something like the following:
//
// // ts
// ...
// async asyncMethod() {
// let x = await super.asyncMethod();
// return x;
// }
// ...
//
// // js
// ...
// asyncMethod() {
// const _super = name => super[name];
// return __awaiter(this, arguments, Promise, function *() {
// let x = yield _super("asyncMethod").call(this);
// return x;
// });
// }
// ...
//
// The more complex case is when we wish to assign a value, especially as part of a destructuring assignment. As both cases
// are legal in ES6, but also likely less frequent, we emit the same more complex helper for both scenarios:
//
// // ts
// ...
// async asyncMethod(ar: Promise<any[]>) {
// [super.a, super.b] = await ar;
// }
// ...
//
// // js
// ...
// asyncMethod(ar) {
// const _super = (function (geti, seti) {
// const cache = Object.create(null);
// return name => cache[name] || (cache[name] = { get value() { return geti(name); }, set value(v) { seti(name, v); } });
// })(name => super[name], (name, value) => super[name] = value);
// return __awaiter(this, arguments, Promise, function *() {
// [_super("a").value, _super("b").value] = yield ar;
// });
// }
// ...
//
// This helper creates an object with a "value" property that wraps the `super` property or indexed access for both get and set.
// This is required for destructuring assignments, as a call expression cannot be used as the target of a destructuring assignment
// while a property access can.
if (container.kind === SyntaxKind.MethodDeclaration && getModifierFlags(container) & ModifierFlags.Async) {
if (isSuperProperty(node.parent) && isAssignmentTarget(node.parent)) {
getNodeLinks(container).flags |= NodeCheckFlags.AsyncMethodWithSuperBinding;
}
else {
getNodeLinks(container).flags |= NodeCheckFlags.AsyncMethodWithSuper;
}
}
if (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);
}
if (container.parent.kind === SyntaxKind.ObjectLiteralExpression) {
if (languageVersion < ScriptTarget.ES2015) {
error(node, Diagnostics.super_is_only_allowed_in_members_of_object_literal_expressions_when_option_target_is_ES2015_or_higher);
return unknownType;
}
else {
// for object literal assume that type of 'super' is 'any'
return anyType;
}
}
// at this point the only legal case for parent is ClassLikeDeclaration
const classLikeDeclaration = <ClassLikeDeclaration>container.parent;
const classType = <InterfaceType>getDeclaredTypeOfSymbol(getSymbolOfNode(classLikeDeclaration));
const baseClassType = classType && getBaseTypes(classType)[0];
if (!baseClassType) {
if (!getClassExtendsHeritageClauseElement(classLikeDeclaration)) {
error(node, Diagnostics.super_can_only_be_referenced_in_a_derived_class);
}
return unknownType;
}
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);
return unknownType;
}
return nodeCheckFlag === NodeCheckFlags.SuperStatic
? getBaseConstructorTypeOfClass(classType)
: getTypeWithThisArgument(baseClassType, classType.thisType);
function isLegalUsageOfSuperExpression(container: Node): boolean {
if (!container) {
return false;
}
if (isCallExpression) {
// TS 1.0 SPEC (April 2014): 4.8.1
// Super calls are only permitted in constructors of derived classes
return 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
// topmost container must be something that is directly nested in the class declaration\object literal expression
if (isClassLike(container.parent) || container.parent.kind === SyntaxKind.ObjectLiteralExpression) {
if (getModifierFlags(container) & ModifierFlags.Static) {
return container.kind === SyntaxKind.MethodDeclaration ||
container.kind === SyntaxKind.MethodSignature ||
container.kind === SyntaxKind.GetAccessor ||
container.kind === SyntaxKind.SetAccessor;
}
else {
return container.kind === SyntaxKind.MethodDeclaration ||
container.kind === SyntaxKind.MethodSignature ||
container.kind === SyntaxKind.GetAccessor ||
container.kind === SyntaxKind.SetAccessor ||
container.kind === SyntaxKind.PropertyDeclaration ||
container.kind === SyntaxKind.PropertySignature ||
container.kind === SyntaxKind.Constructor;
}
}
}
return false;
}
}
function getContainingObjectLiteral(func: FunctionLikeDeclaration) {
return (func.kind === SyntaxKind.MethodDeclaration ||
func.kind === SyntaxKind.GetAccessor ||
func.kind === SyntaxKind.SetAccessor) && func.parent.kind === SyntaxKind.ObjectLiteralExpression ? <ObjectLiteralExpression>func.parent :
func.kind === SyntaxKind.FunctionExpression && func.parent.kind === SyntaxKind.PropertyAssignment ? <ObjectLiteralExpression>func.parent.parent :
undefined;
}
function getThisTypeArgument(type: Type): Type {
return getObjectFlags(type) & ObjectFlags.Reference && (<TypeReference>type).target === globalThisType ? (<TypeReference>type).typeArguments[0] : undefined;
}
function getThisTypeFromContextualType(type: Type): Type {
return mapType(type, t => {
return t.flags & TypeFlags.Intersection ? forEach((<IntersectionType>t).types, getThisTypeArgument) : getThisTypeArgument(t);
});
}
function getContextualThisParameterType(func: FunctionLikeDeclaration): Type {
if (func.kind === SyntaxKind.ArrowFunction) {
return undefined;
}
if (isContextSensitiveFunctionOrObjectLiteralMethod(func)) {
const contextualSignature = getContextualSignature(func);
if (contextualSignature) {
const thisParameter = contextualSignature.thisParameter;
if (thisParameter) {
return getTypeOfSymbol(thisParameter);
}
}
}
if (noImplicitThis) {
const containingLiteral = getContainingObjectLiteral(func);
if (containingLiteral) {
// We have an object literal method. Check if the containing object literal has a contextual type
// that includes a ThisType<T>. If so, T is the contextual type for 'this'. We continue looking in
// any directly enclosing object literals.
const contextualType = getApparentTypeOfContextualType(containingLiteral);
let literal = containingLiteral;
let type = contextualType;
while (type) {
const thisType = getThisTypeFromContextualType(type);
if (thisType) {
return instantiateType(thisType, getContextualMapper(containingLiteral));
}
if (literal.parent.kind !== SyntaxKind.PropertyAssignment) {
break;
}
literal = <ObjectLiteralExpression>literal.parent.parent;
type = getApparentTypeOfContextualType(literal);
}
// There was no contextual ThisType<T> for the containing object literal, so the contextual type
// for 'this' is the non-null form of the contextual type for the containing object literal or
// the type of the object literal itself.
return contextualType ? getNonNullableType(contextualType) : checkExpressionCached(containingLiteral);
}
// In an assignment of the form 'obj.xxx = function(...)' or 'obj[xxx] = function(...)', the
// contextual type for 'this' is 'obj'.
if (func.parent.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>func.parent).operatorToken.kind === SyntaxKind.EqualsToken) {
const target = (<BinaryExpression>func.parent).left;
if (target.kind === SyntaxKind.PropertyAccessExpression || target.kind === SyntaxKind.ElementAccessExpression) {
return checkExpressionCached((<PropertyAccessExpression | ElementAccessExpression>target).expression);
}
}
}
return undefined;
}
// Return contextual type of parameter or undefined if no contextual type is available
function getContextuallyTypedParameterType(parameter: ParameterDeclaration): Type {
const func = parameter.parent;
if (isContextSensitiveFunctionOrObjectLiteralMethod(func)) {
const iife = getImmediatelyInvokedFunctionExpression(func);
if (iife && iife.arguments) {
const indexOfParameter = indexOf(func.parameters, parameter);
if (parameter.dotDotDotToken) {
const restTypes: Type[] = [];
for (let i = indexOfParameter; i < iife.arguments.length; i++) {
restTypes.push(getWidenedLiteralType(checkExpression(iife.arguments[i])));
}
return restTypes.length ? createArrayType(getUnionType(restTypes)) : undefined;
}
const links = getNodeLinks(iife);
const cached = links.resolvedSignature;
links.resolvedSignature = anySignature;
const type = indexOfParameter < iife.arguments.length ?
getWidenedLiteralType(checkExpression(iife.arguments[indexOfParameter])) :
parameter.initializer ? undefined : undefinedWideningType;
links.resolvedSignature = cached;
return type;
}
const contextualSignature = getContextualSignature(func);
if (contextualSignature) {
const funcHasRestParameters = hasRestParameter(func);
const len = func.parameters.length - (funcHasRestParameters ? 1 : 0);
const indexOfParameter = indexOf(func.parameters, parameter);
if (indexOfParameter < len) {
return getTypeAtPosition(contextualSignature, indexOfParameter);
}
// If last parameter is contextually rest parameter get its type
if (funcHasRestParameters &&
indexOfParameter === (func.parameters.length - 1) &&
isRestParameterIndex(contextualSignature, func.parameters.length - 1)) {
return getTypeOfSymbol(lastOrUndefined(contextualSignature.parameters));
}
}
}
return undefined;
}
// In a variable, parameter or property declaration with a type annotation,
// the contextual type of an initializer expression is the type of the variable, parameter or property.
// Otherwise, in a parameter declaration of a contextually typed function expression,
// the contextual type of an initializer expression is the contextual type of the parameter.
// Otherwise, in a variable or parameter declaration with a binding pattern name,
// the contextual type of an initializer expression is the type implied by the binding pattern.
// Otherwise, in a binding pattern inside a variable or parameter declaration,
// the contextual type of an initializer expression is the type annotation of the containing declaration, if present.
function getContextualTypeForInitializerExpression(node: Expression): Type {
const declaration = <VariableLikeDeclaration>node.parent;
if (node === declaration.initializer) {
if (declaration.type) {
return getTypeFromTypeNode(declaration.type);
}
if (declaration.kind === SyntaxKind.Parameter) {
const type = getContextuallyTypedParameterType(<ParameterDeclaration>declaration);
if (type) {
return type;
}
}
if (isBindingPattern(declaration.name)) {
return getTypeFromBindingPattern(<BindingPattern>declaration.name, /*includePatternInType*/ true, /*reportErrors*/ false);
}
if (isBindingPattern(declaration.parent)) {
const parentDeclaration = declaration.parent.parent;
const name = declaration.propertyName || declaration.name;
if (parentDeclaration.kind !== SyntaxKind.BindingElement &&
parentDeclaration.type &&
!isBindingPattern(name)) {
const text = getTextOfPropertyName(name);
if (text) {
return getTypeOfPropertyOfType(getTypeFromTypeNode(parentDeclaration.type), text);
}
}
}
}
return undefined;
}
function getContextualTypeForReturnExpression(node: Expression): Type {
const func = getContainingFunction(node);
if (func) {
const functionFlags = getFunctionFlags(func);
if (functionFlags & FunctionFlags.Generator) { // AsyncGenerator function or Generator function
return undefined;
}
const contextualReturnType = getContextualReturnType(func);
return functionFlags & FunctionFlags.Async
? contextualReturnType && getAwaitedTypeOfPromise(contextualReturnType) // Async function
: contextualReturnType; // Regular function
}
return undefined;
}
function getContextualTypeForYieldOperand(node: YieldExpression): Type {
const func = getContainingFunction(node);
if (func) {
const functionFlags = getFunctionFlags(func);
const contextualReturnType = getContextualReturnType(func);
if (contextualReturnType) {
return node.asteriskToken
? contextualReturnType
: getIteratedTypeOfGenerator(contextualReturnType, (functionFlags & FunctionFlags.Async) !== 0);
}
}
return undefined;
}
function isInParameterInitializerBeforeContainingFunction(node: Node) {
while (node.parent && !isFunctionLike(node.parent)) {
if (node.parent.kind === SyntaxKind.Parameter && (<ParameterDeclaration>node.parent).initializer === node) {
return true;
}
node = node.parent;
}
return false;
}
function getContextualReturnType(functionDecl: FunctionLikeDeclaration): Type {
// If the containing function has a return type annotation, is a constructor, or is a get accessor whose
// corresponding set accessor has a type annotation, return statements in the function are contextually typed
if (functionDecl.type ||
functionDecl.kind === SyntaxKind.Constructor ||
functionDecl.kind === SyntaxKind.GetAccessor && getSetAccessorTypeAnnotationNode(getDeclarationOfKind<SetAccessorDeclaration>(functionDecl.symbol, SyntaxKind.SetAccessor))) {
return getReturnTypeOfSignature(getSignatureFromDeclaration(functionDecl));
}
// Otherwise, if the containing function is contextually typed by a function type with exactly one call signature
// and that call signature is non-generic, return statements are contextually typed by the return type of the signature
const signature = getContextualSignatureForFunctionLikeDeclaration(<FunctionExpression>functionDecl);
if (signature) {
return getReturnTypeOfSignature(signature);
}
return undefined;
}
// In a typed function call, an argument or substitution expression is contextually typed by the type of the corresponding parameter.
function getContextualTypeForArgument(callTarget: CallLikeExpression, arg: Expression): Type {
const args = getEffectiveCallArguments(callTarget);
const argIndex = indexOf(args, arg);
if (argIndex >= 0) {
const signature = getResolvedOrAnySignature(callTarget);
return getTypeAtPosition(signature, argIndex);
}
return undefined;
}
function getContextualTypeForSubstitutionExpression(template: TemplateExpression, substitutionExpression: Expression) {
if (template.parent.kind === SyntaxKind.TaggedTemplateExpression) {
return getContextualTypeForArgument(<TaggedTemplateExpression>template.parent, substitutionExpression);
}
return undefined;
}
function getContextualTypeForBinaryOperand(node: Expression): Type {
const binaryExpression = <BinaryExpression>node.parent;
const operator = binaryExpression.operatorToken.kind;
if (isAssignmentOperator(operator)) {
// Don't do this for special property assignments to avoid circularity
if (getSpecialPropertyAssignmentKind(binaryExpression) !== SpecialPropertyAssignmentKind.None) {
return undefined;
}
// In an assignment expression, the right operand is contextually typed by the type of the left operand.
if (node === binaryExpression.right) {
return getTypeOfExpression(binaryExpression.left);
}
}
else if (operator === SyntaxKind.BarBarToken) {
// When an || expression has a contextual type, the operands are contextually typed by that type. When an ||
// expression has no contextual type, the right operand is contextually typed by the type of the left operand.
let type = getContextualType(binaryExpression);
if (!type && node === binaryExpression.right) {
type = getTypeOfExpression(binaryExpression.left);
}
return type;
}
else if (operator === SyntaxKind.AmpersandAmpersandToken || operator === SyntaxKind.CommaToken) {
if (node === binaryExpression.right) {
return getContextualType(binaryExpression);
}
}
return undefined;
}
function getTypeOfPropertyOfContextualType(type: Type, name: string) {
return mapType(type, t => {
const prop = t.flags & TypeFlags.StructuredType ? getPropertyOfType(t, name) : undefined;
return prop ? getTypeOfSymbol(prop) : undefined;
});
}
function getIndexTypeOfContextualType(type: Type, kind: IndexKind) {
return mapType(type, t => getIndexTypeOfStructuredType(t, kind));
}
// Return true if the given contextual type is a tuple-like type
function contextualTypeIsTupleLikeType(type: Type): boolean {
return !!(type.flags & TypeFlags.Union ? forEach((<UnionType>type).types, isTupleLikeType) : isTupleLikeType(type));
}
// In an object literal contextually typed by a type T, the contextual type of a property assignment is the type of
// the matching property in T, if one exists. Otherwise, it is the type of the numeric index signature in T, if one
// exists. Otherwise, it is the type of the string index signature in T, if one exists.
function getContextualTypeForObjectLiteralMethod(node: MethodDeclaration): Type {
Debug.assert(isObjectLiteralMethod(node));
if (isInsideWithStatementBody(node)) {
// We cannot answer semantic questions within a with block, do not proceed any further
return undefined;
}
return getContextualTypeForObjectLiteralElement(node);
}
function getContextualTypeForObjectLiteralElement(element: ObjectLiteralElementLike) {
const objectLiteral = <ObjectLiteralExpression>element.parent;
const type = getApparentTypeOfContextualType(objectLiteral);
if (type) {
if (!hasDynamicName(element)) {
// For a (non-symbol) computed property, there is no reason to look up the name
// in the type. It will just be "__computed", which does not appear in any
// SymbolTable.
const symbolName = getSymbolOfNode(element).name;
const propertyType = getTypeOfPropertyOfContextualType(type, symbolName);
if (propertyType) {
return propertyType;
}
}
return isNumericName(element.name) && getIndexTypeOfContextualType(type, IndexKind.Number) ||
getIndexTypeOfContextualType(type, IndexKind.String);
}
return undefined;
}
// In an array literal contextually typed by a type T, the contextual type of an element expression at index N is
// the type of the property with the numeric name N in T, if one exists. Otherwise, if T has a numeric index signature,
// it is the type of the numeric index signature in T. Otherwise, in ES6 and higher, the contextual type is the iterated
// type of T.
function getContextualTypeForElementExpression(node: Expression): Type {
const arrayLiteral = <ArrayLiteralExpression>node.parent;
const type = getApparentTypeOfContextualType(arrayLiteral);
if (type) {
const index = indexOf(arrayLiteral.elements, node);
return getTypeOfPropertyOfContextualType(type, "" + index)
|| getIndexTypeOfContextualType(type, IndexKind.Number)
|| getIteratedTypeOrElementType(type, /*errorNode*/ undefined, /*allowStringInput*/ false, /*allowAsyncIterable*/ false, /*checkAssignability*/ false);
}
return undefined;
}
// In a contextually typed conditional expression, the true/false expressions are contextually typed by the same type.
function getContextualTypeForConditionalOperand(node: Expression): Type {
const conditional = <ConditionalExpression>node.parent;
return node === conditional.whenTrue || node === conditional.whenFalse ? getContextualType(conditional) : undefined;
}
function getContextualTypeForJsxExpression(node: JsxExpression): Type {
// JSX expression can appear in two position : JSX Element's children or JSX attribute
const jsxAttributes = isJsxAttributeLike(node.parent) ?
node.parent.parent :
node.parent.openingElement.attributes; // node.parent is JsxElement
// When we trying to resolve JsxOpeningLikeElement as a stateless function element, we will already give its attributes a contextual type
// which is a type of the parameter of the signature we are trying out.
// If there is no contextual type (e.g. we are trying to resolve stateful component), get attributes type from resolving element's tagName
const attributesType = getContextualType(jsxAttributes);
if (!attributesType || isTypeAny(attributesType)) {
return undefined;
}
if (isJsxAttribute(node.parent)) {
// JSX expression is in JSX attribute
return getTypeOfPropertyOfType(attributesType, (node.parent as JsxAttribute).name.text);
}
else if (node.parent.kind === SyntaxKind.JsxElement) {
// JSX expression is in children of JSX Element, we will look for an "children" atttribute (we get the name from JSX.ElementAttributesProperty)
const jsxChildrenPropertyName = getJsxElementChildrenPropertyname();
return jsxChildrenPropertyName && jsxChildrenPropertyName !== "" ? getTypeOfPropertyOfType(attributesType, jsxChildrenPropertyName) : anyType;
}
else {
// JSX expression is in JSX spread attribute
return attributesType;
}
}
function getContextualTypeForJsxAttribute(attribute: JsxAttribute | JsxSpreadAttribute) {
// When we trying to resolve JsxOpeningLikeElement as a stateless function element, we will already give its attributes a contextual type
// which is a type of the parameter of the signature we are trying out.
// If there is no contextual type (e.g. we are trying to resolve stateful component), get attributes type from resolving element's tagName
const attributesType = getContextualType(<Expression>attribute.parent);
if (isJsxAttribute(attribute)) {
if (!attributesType || isTypeAny(attributesType)) {
return undefined;
}
return getTypeOfPropertyOfType(attributesType, (attribute as JsxAttribute).name.text);
}
else {
return attributesType;
}
}
// Return the contextual type for a given expression node. During overload resolution, a contextual type may temporarily
// be "pushed" onto a node using the contextualType property.
function getApparentTypeOfContextualType(node: Expression): Type {
const type = getContextualType(node);
return type && getApparentType(type);
}
/**
* Woah! Do you really want to use this function?
*
* Unless you're trying to get the *non-apparent* type for a
* value-literal type or you're authoring relevant portions of this algorithm,
* you probably meant to use 'getApparentTypeOfContextualType'.
* Otherwise this may not be very useful.
*
* In cases where you *are* working on this function, you should understand
* when it is appropriate to use 'getContextualType' and 'getApparentTypeOfContextualType'.
*
* - Use 'getContextualType' when you are simply going to propagate the result to the expression.
* - Use 'getApparentTypeOfContextualType' when you're going to need the members of the type.
*
* @param node the expression whose contextual type will be returned.
* @returns the contextual type of an expression.
*/
function getContextualType(node: Expression): Type | undefined {
if (isInsideWithStatementBody(node)) {
// We cannot answer semantic questions within a with block, do not proceed any further
return undefined;
}
if (node.contextualType) {
return node.contextualType;
}
const parent = node.parent;
switch (parent.kind) {
case SyntaxKind.VariableDeclaration:
case SyntaxKind.Parameter:
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.PropertySignature:
case SyntaxKind.BindingElement:
return getContextualTypeForInitializerExpression(node);
case SyntaxKind.ArrowFunction:
case SyntaxKind.ReturnStatement:
return getContextualTypeForReturnExpression(node);
case SyntaxKind.YieldExpression:
return getContextualTypeForYieldOperand(<YieldExpression>parent);
case SyntaxKind.CallExpression:
case SyntaxKind.NewExpression:
return getContextualTypeForArgument(<CallExpression>parent, node);
case SyntaxKind.TypeAssertionExpression:
case SyntaxKind.AsExpression:
return getTypeFromTypeNode((<AssertionExpression>parent).type);
case SyntaxKind.BinaryExpression:
return getContextualTypeForBinaryOperand(node);
case SyntaxKind.PropertyAssignment:
case SyntaxKind.ShorthandPropertyAssignment:
return getContextualTypeForObjectLiteralElement(<ObjectLiteralElementLike>parent);
case SyntaxKind.SpreadAssignment:
return getApparentTypeOfContextualType(parent.parent as ObjectLiteralExpression);
case SyntaxKind.ArrayLiteralExpression:
return getContextualTypeForElementExpression(node);
case SyntaxKind.ConditionalExpression:
return getContextualTypeForConditionalOperand(node);
case SyntaxKind.TemplateSpan:
Debug.assert(parent.parent.kind === SyntaxKind.TemplateExpression);
return getContextualTypeForSubstitutionExpression(<TemplateExpression>parent.parent, node);
case SyntaxKind.ParenthesizedExpression:
return getContextualType(<ParenthesizedExpression>parent);
case SyntaxKind.JsxExpression:
return getContextualTypeForJsxExpression(<JsxExpression>parent);
case SyntaxKind.JsxAttribute:
case SyntaxKind.JsxSpreadAttribute:
return getContextualTypeForJsxAttribute(<JsxAttribute | JsxSpreadAttribute>parent);
case SyntaxKind.JsxOpeningElement:
case SyntaxKind.JsxSelfClosingElement:
return getAttributesTypeFromJsxOpeningLikeElement(<JsxOpeningLikeElement>parent);
}
return undefined;
}
function getContextualMapper(node: Node) {
node = findAncestor(node, n => !!n.contextualMapper);
return node ? node.contextualMapper : identityMapper;
}
// If the given type is an object or union type, if that type has a single signature, and if
// that signature is non-generic, return the signature. Otherwise return undefined.
function getNonGenericSignature(type: Type, node: FunctionExpression | ArrowFunction | MethodDeclaration): Signature {
const signatures = getSignaturesOfStructuredType(type, SignatureKind.Call);
if (signatures.length === 1) {
const signature = signatures[0];
if (!signature.typeParameters && !isAritySmaller(signature, node)) {
return signature;
}
}
}
/** If the contextual signature has fewer parameters than the function expression, do not use it */
function isAritySmaller(signature: Signature, target: FunctionExpression | ArrowFunction | MethodDeclaration) {
let targetParameterCount = 0;
for (; targetParameterCount < target.parameters.length; targetParameterCount++) {
const param = target.parameters[targetParameterCount];
if (param.initializer || param.questionToken || param.dotDotDotToken || isJSDocOptionalParameter(param)) {
break;
}
}
if (target.parameters.length && parameterIsThisKeyword(target.parameters[0])) {
targetParameterCount--;
}
const sourceLength = signature.hasRestParameter ? Number.MAX_VALUE : signature.parameters.length;
return sourceLength < targetParameterCount;
}
function isFunctionExpressionOrArrowFunction(node: Node): node is FunctionExpression | ArrowFunction {
return node.kind === SyntaxKind.FunctionExpression || node.kind === SyntaxKind.ArrowFunction;
}
function getContextualSignatureForFunctionLikeDeclaration(node: FunctionLikeDeclaration): Signature {
// Only function expressions, arrow functions, and object literal methods are contextually typed.
return isFunctionExpressionOrArrowFunction(node) || isObjectLiteralMethod(node)
? getContextualSignature(<FunctionExpression>node)
: undefined;
}
function getContextualTypeForFunctionLikeDeclaration(node: FunctionExpression | ArrowFunction | MethodDeclaration) {
return isObjectLiteralMethod(node) ?
getContextualTypeForObjectLiteralMethod(node) :
getApparentTypeOfContextualType(node);
}
// Return the contextual signature for a given expression node. A contextual type provides a
// contextual signature if it has a single call signature and if that call signature is non-generic.
// If the contextual type is a union type, get the signature from each type possible and if they are
// all identical ignoring their return type, the result is same signature but with return type as
// union type of return types from these signatures
function getContextualSignature(node: FunctionExpression | ArrowFunction | MethodDeclaration): Signature {
Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isObjectLiteralMethod(node));
const type = getContextualTypeForFunctionLikeDeclaration(node);
if (!type) {
return undefined;
}
if (!(type.flags & TypeFlags.Union)) {
return getNonGenericSignature(type, node);
}
let signatureList: Signature[];
const types = (<UnionType>type).types;
for (const current of types) {
const signature = getNonGenericSignature(current, node);
if (signature) {
if (!signatureList) {
// This signature will contribute to contextual union signature
signatureList = [signature];
}
else if (!compareSignaturesIdentical(signatureList[0], signature, /*partialMatch*/ false, /*ignoreThisTypes*/ true, /*ignoreReturnTypes*/ true, compareTypesIdentical)) {
// Signatures aren't identical, do not use
return undefined;
}
else {
// Use this signature for contextual union signature
signatureList.push(signature);
}
}
}
// Result is union of signatures collected (return type is union of return types of this signature set)
let result: Signature;
if (signatureList) {
result = cloneSignature(signatureList[0]);
// Clear resolved return type we possibly got from cloneSignature
result.resolvedReturnType = undefined;
result.unionSignatures = signatureList;
}
return result;
}
function checkSpreadExpression(node: SpreadElement, checkMode?: CheckMode): Type {
if (languageVersion < ScriptTarget.ES2015 && compilerOptions.downlevelIteration) {
checkExternalEmitHelpers(node, ExternalEmitHelpers.SpreadIncludes);
}
const arrayOrIterableType = checkExpression(node.expression, checkMode);
return checkIteratedTypeOrElementType(arrayOrIterableType, node.expression, /*allowStringInput*/ false, /*allowAsyncIterable*/ false);
}
function hasDefaultValue(node: BindingElement | Expression): boolean {
return (node.kind === SyntaxKind.BindingElement && !!(<BindingElement>node).initializer) ||
(node.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>node).operatorToken.kind === SyntaxKind.EqualsToken);
}
function checkArrayLiteral(node: ArrayLiteralExpression, checkMode?: CheckMode): Type {
const elements = node.elements;
let hasSpreadElement = false;
const elementTypes: Type[] = [];
const inDestructuringPattern = isAssignmentTarget(node);
for (const e of elements) {
if (inDestructuringPattern && e.kind === SyntaxKind.SpreadElement) {
// Given the following situation:
// var c: {};
// [...c] = ["", 0];
//
// c is represented in the tree as a spread element in an array literal.
// But c really functions as a rest element, and its purpose is to provide
// a contextual type for the right hand side of the assignment. Therefore,
// instead of calling checkExpression on "...c", which will give an error
// if c is not iterable/array-like, we need to act as if we are trying to
// get the contextual element type from it. So we do something similar to
// getContextualTypeForElementExpression, which will crucially not error
// if there is no index type / iterated type.
const restArrayType = checkExpression((<SpreadElement>e).expression, checkMode);
const restElementType = getIndexTypeOfType(restArrayType, IndexKind.Number) ||
getIteratedTypeOrElementType(restArrayType, /*errorNode*/ undefined, /*allowStringInput*/ false, /*allowAsyncIterable*/ false, /*checkAssignability*/ false);
if (restElementType) {
elementTypes.push(restElementType);
}
}
else {
const type = checkExpressionForMutableLocation(e, checkMode);
elementTypes.push(type);
}
hasSpreadElement = hasSpreadElement || e.kind === SyntaxKind.SpreadElement;
}
if (!hasSpreadElement) {
// If array literal is actually a destructuring pattern, mark it as an implied type. We do this such
// that we get the same behavior for "var [x, y] = []" and "[x, y] = []".
if (inDestructuringPattern && elementTypes.length) {
const type = cloneTypeReference(createTupleType(elementTypes));
type.pattern = node;
return type;
}
const contextualType = getApparentTypeOfContextualType(node);
if (contextualType && contextualTypeIsTupleLikeType(contextualType)) {
const pattern = contextualType.pattern;
// If array literal is contextually typed by a binding pattern or an assignment pattern, pad the resulting
// tuple type with the corresponding binding or assignment element types to make the lengths equal.
if (pattern && (pattern.kind === SyntaxKind.ArrayBindingPattern || pattern.kind === SyntaxKind.ArrayLiteralExpression)) {
const patternElements = (<BindingPattern | ArrayLiteralExpression>pattern).elements;
for (let i = elementTypes.length; i < patternElements.length; i++) {
const patternElement = patternElements[i];
if (hasDefaultValue(patternElement)) {
elementTypes.push((<TypeReference>contextualType).typeArguments[i]);
}
else {
if (patternElement.kind !== SyntaxKind.OmittedExpression) {
error(patternElement, Diagnostics.Initializer_provides_no_value_for_this_binding_element_and_the_binding_element_has_no_default_value);
}
elementTypes.push(unknownType);
}
}
}
if (elementTypes.length) {
return createTupleType(elementTypes);
}
}
}
return createArrayType(elementTypes.length ?
getUnionType(elementTypes, /*subtypeReduction*/ true) :
strictNullChecks ? neverType : undefinedWideningType);
}
function isNumericName(name: DeclarationName): boolean {
return name.kind === SyntaxKind.ComputedPropertyName ? isNumericComputedName(<ComputedPropertyName>name) : isNumericLiteralName((<Identifier>name).text);
}
function isNumericComputedName(name: ComputedPropertyName): boolean {
// It seems odd to consider an expression of type Any to result in a numeric name,
// but this behavior is consistent with checkIndexedAccess
return isTypeAnyOrAllConstituentTypesHaveKind(checkComputedPropertyName(name), TypeFlags.NumberLike);
}
function isTypeAnyOrAllConstituentTypesHaveKind(type: Type, kind: TypeFlags): boolean {
return isTypeAny(type) || isTypeOfKind(type, kind);
}
function isInfinityOrNaNString(name: string): boolean {
return name === "Infinity" || name === "-Infinity" || name === "NaN";
}
function isNumericLiteralName(name: string) {
// The intent of numeric names is that
// - they are names with text in a numeric form, and that
// - setting properties/indexing with them is always equivalent to doing so with the numeric literal 'numLit',
// acquired by applying the abstract 'ToNumber' operation on the name's text.
//
// The subtlety is in the latter portion, as we cannot reliably say that anything that looks like a numeric literal is a numeric name.
// In fact, it is the case that the text of the name must be equal to 'ToString(numLit)' for this to hold.
//
// Consider the property name '"0xF00D"'. When one indexes with '0xF00D', they are actually indexing with the value of 'ToString(0xF00D)'
// according to the ECMAScript specification, so it is actually as if the user indexed with the string '"61453"'.
// Thus, the text of all numeric literals equivalent to '61543' such as '0xF00D', '0xf00D', '0170015', etc. are not valid numeric names
// because their 'ToString' representation is not equal to their original text.
// This is motivated by ECMA-262 sections 9.3.1, 9.8.1, 11.1.5, and 11.2.1.
//
// Here, we test whether 'ToString(ToNumber(name))' is exactly equal to 'name'.
// The '+' prefix operator is equivalent here to applying the abstract ToNumber operation.
// Applying the 'toString()' method on a number gives us the abstract ToString operation on a number.
//
// Note that this accepts the values 'Infinity', '-Infinity', and 'NaN', and that this is intentional.
// This is desired behavior, because when indexing with them as numeric entities, you are indexing
// with the strings '"Infinity"', '"-Infinity"', and '"NaN"' respectively.
return (+name).toString() === name;
}
function checkComputedPropertyName(node: ComputedPropertyName): Type {
const links = getNodeLinks(node.expression);
if (!links.resolvedType) {
links.resolvedType = checkExpression(node.expression);
// This will allow types number, string, symbol or any. It will also allow enums, the unknown
// type, and any union of these types (like string | number).
if (!isTypeAnyOrAllConstituentTypesHaveKind(links.resolvedType, TypeFlags.NumberLike | TypeFlags.StringLike | TypeFlags.ESSymbol)) {
error(node, Diagnostics.A_computed_property_name_must_be_of_type_string_number_symbol_or_any);
}
else {
checkThatExpressionIsProperSymbolReference(node.expression, links.resolvedType, /*reportError*/ true);
}
}
return links.resolvedType;
}
function getObjectLiteralIndexInfo(propertyNodes: NodeArray<ObjectLiteralElementLike>, offset: number, properties: Symbol[], kind: IndexKind): IndexInfo {
const propTypes: Type[] = [];
for (let i = 0; i < properties.length; i++) {
if (kind === IndexKind.String || isNumericName(propertyNodes[i + offset].name)) {
propTypes.push(getTypeOfSymbol(properties[i]));
}
}
const unionType = propTypes.length ? getUnionType(propTypes, /*subtypeReduction*/ true) : undefinedType;
return createIndexInfo(unionType, /*isReadonly*/ false);
}
function checkObjectLiteral(node: ObjectLiteralExpression, checkMode?: CheckMode): Type {
const inDestructuringPattern = isAssignmentTarget(node);
// Grammar checking
checkGrammarObjectLiteralExpression(node, inDestructuringPattern);
let propertiesTable = createMap<Symbol>();
let propertiesArray: Symbol[] = [];
let spread: Type = emptyObjectType;
let propagatedFlags: TypeFlags = 0;
const contextualType = getApparentTypeOfContextualType(node);
const contextualTypeHasPattern = contextualType && contextualType.pattern &&
(contextualType.pattern.kind === SyntaxKind.ObjectBindingPattern || contextualType.pattern.kind === SyntaxKind.ObjectLiteralExpression);
const isJSObjectLiteral = !contextualType && isInJavaScriptFile(node);
let typeFlags: TypeFlags = 0;
let patternWithComputedProperties = false;
let hasComputedStringProperty = false;
let hasComputedNumberProperty = false;
let offset = 0;
for (let i = 0; i < node.properties.length; i++) {
const memberDecl = node.properties[i];
let member = memberDecl.symbol;
if (memberDecl.kind === SyntaxKind.PropertyAssignment ||
memberDecl.kind === SyntaxKind.ShorthandPropertyAssignment ||
isObjectLiteralMethod(memberDecl)) {
let type: Type;
if (memberDecl.kind === SyntaxKind.PropertyAssignment) {
type = checkPropertyAssignment(<PropertyAssignment>memberDecl, checkMode);
}
else if (memberDecl.kind === SyntaxKind.MethodDeclaration) {
type = checkObjectLiteralMethod(<MethodDeclaration>memberDecl, checkMode);
}
else {
Debug.assert(memberDecl.kind === SyntaxKind.ShorthandPropertyAssignment);
type = checkExpressionForMutableLocation((<ShorthandPropertyAssignment>memberDecl).name, checkMode);
}
typeFlags |= type.flags;
const prop = createSymbol(SymbolFlags.Property | member.flags, member.name);
if (inDestructuringPattern) {
// If object literal is an assignment pattern and if the assignment pattern specifies a default value
// for the property, make the property optional.
const isOptional =
(memberDecl.kind === SyntaxKind.PropertyAssignment && hasDefaultValue((<PropertyAssignment>memberDecl).initializer)) ||
(memberDecl.kind === SyntaxKind.ShorthandPropertyAssignment && (<ShorthandPropertyAssignment>memberDecl).objectAssignmentInitializer);
if (isOptional) {
prop.flags |= SymbolFlags.Optional;
}
if (hasDynamicName(memberDecl)) {
patternWithComputedProperties = true;
}
}
else if (contextualTypeHasPattern && !(getObjectFlags(contextualType) & ObjectFlags.ObjectLiteralPatternWithComputedProperties)) {
// If object literal is contextually typed by the implied type of a binding pattern, and if the
// binding pattern specifies a default value for the property, make the property optional.
const impliedProp = getPropertyOfType(contextualType, member.name);
if (impliedProp) {
prop.flags |= impliedProp.flags & SymbolFlags.Optional;
}
else if (!compilerOptions.suppressExcessPropertyErrors && !getIndexInfoOfType(contextualType, IndexKind.String)) {
error(memberDecl.name, Diagnostics.Object_literal_may_only_specify_known_properties_and_0_does_not_exist_in_type_1,
symbolToString(member), typeToString(contextualType));
}
}
prop.declarations = member.declarations;
prop.parent = member.parent;
if (member.valueDeclaration) {
prop.valueDeclaration = member.valueDeclaration;
}
prop.type = type;
prop.target = member;
member = prop;
}
else if (memberDecl.kind === SyntaxKind.SpreadAssignment) {
if (languageVersion < ScriptTarget.ES2015) {
checkExternalEmitHelpers(memberDecl, ExternalEmitHelpers.Assign);
}
if (propertiesArray.length > 0) {
spread = getSpreadType(spread, createObjectLiteralType());
propertiesArray = [];
propertiesTable = createMap<Symbol>();
hasComputedStringProperty = false;
hasComputedNumberProperty = false;
typeFlags = 0;
}
const type = checkExpression((memberDecl as SpreadAssignment).expression);
if (!isValidSpreadType(type)) {
error(memberDecl, Diagnostics.Spread_types_may_only_be_created_from_object_types);
return unknownType;
}
spread = getSpreadType(spread, type);
offset = i + 1;
continue;
}
else {
// TypeScript 1.0 spec (April 2014)
// A get accessor declaration is processed in the same manner as
// an ordinary function declaration(section 6.1) with no parameters.
// A set accessor declaration is processed in the same manner
// as an ordinary function declaration with a single parameter and a Void return type.
Debug.assert(memberDecl.kind === SyntaxKind.GetAccessor || memberDecl.kind === SyntaxKind.SetAccessor);
checkNodeDeferred(memberDecl);
}
if (hasDynamicName(memberDecl)) {
if (isNumericName(memberDecl.name)) {
hasComputedNumberProperty = true;
}
else {
hasComputedStringProperty = true;
}
}
else {
propertiesTable.set(member.name, member);
}
propertiesArray.push(member);
}
// If object literal is contextually typed by the implied type of a binding pattern, augment the result
// type with those properties for which the binding pattern specifies a default value.
if (contextualTypeHasPattern) {
for (const prop of getPropertiesOfType(contextualType)) {
if (!propertiesTable.get(prop.name)) {
if (!(prop.flags & SymbolFlags.Optional)) {
error(prop.valueDeclaration || (<TransientSymbol>prop).bindingElement,
Diagnostics.Initializer_provides_no_value_for_this_binding_element_and_the_binding_element_has_no_default_value);
}
propertiesTable.set(prop.name, prop);
propertiesArray.push(prop);
}
}
}
if (spread !== emptyObjectType) {
if (propertiesArray.length > 0) {
spread = getSpreadType(spread, createObjectLiteralType());
}
if (spread.flags & TypeFlags.Object) {
// only set the symbol and flags if this is a (fresh) object type
spread.flags |= propagatedFlags;
spread.flags |= TypeFlags.FreshLiteral;
(spread as ObjectType).objectFlags |= ObjectFlags.ObjectLiteral;
spread.symbol = node.symbol;
}
return spread;
}
return createObjectLiteralType();
function createObjectLiteralType() {
const stringIndexInfo = isJSObjectLiteral ? jsObjectLiteralIndexInfo : hasComputedStringProperty ? getObjectLiteralIndexInfo(node.properties, offset, propertiesArray, IndexKind.String) : undefined;
const numberIndexInfo = hasComputedNumberProperty && !isJSObjectLiteral ? getObjectLiteralIndexInfo(node.properties, offset, propertiesArray, IndexKind.Number) : undefined;
const result = createAnonymousType(node.symbol, propertiesTable, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo);
const freshObjectLiteralFlag = compilerOptions.suppressExcessPropertyErrors ? 0 : TypeFlags.FreshLiteral;
result.flags |= TypeFlags.ContainsObjectLiteral | freshObjectLiteralFlag | (typeFlags & TypeFlags.PropagatingFlags);
result.objectFlags |= ObjectFlags.ObjectLiteral;
if (patternWithComputedProperties) {
result.objectFlags |= ObjectFlags.ObjectLiteralPatternWithComputedProperties;
}
if (inDestructuringPattern) {
result.pattern = node;
}
if (!(result.flags & TypeFlags.Nullable)) {
propagatedFlags |= (result.flags & TypeFlags.PropagatingFlags);
}
return result;
}
}
function isValidSpreadType(type: Type): boolean {
return !!(type.flags & (TypeFlags.Any | TypeFlags.Null | TypeFlags.Undefined | TypeFlags.NonPrimitive) ||
type.flags & TypeFlags.Object && !isGenericMappedType(type) ||
type.flags & TypeFlags.UnionOrIntersection && !forEach((<UnionOrIntersectionType>type).types, t => !isValidSpreadType(t)));
}
function checkJsxSelfClosingElement(node: JsxSelfClosingElement): Type {
checkJsxOpeningLikeElement(node);
return getJsxGlobalElementType() || anyType;
}
function checkJsxElement(node: JsxElement): Type {
// Check attributes
checkJsxOpeningLikeElement(node.openingElement);
// Perform resolution on the closing tag so that rename/go to definition/etc work
if (isJsxIntrinsicIdentifier(node.closingElement.tagName)) {
getIntrinsicTagSymbol(node.closingElement);
}
else {
checkExpression(node.closingElement.tagName);
}
return getJsxGlobalElementType() || anyType;
}
/**
* Returns true iff the JSX element name would be a valid JS identifier, ignoring restrictions about keywords not being identifiers
*/
function isUnhyphenatedJsxName(name: string) {
// - is the only character supported in JSX attribute names that isn't valid in JavaScript identifiers
return name.indexOf("-") < 0;
}
/**
* Returns true iff React would emit this tag name as a string rather than an identifier or qualified name
*/
function isJsxIntrinsicIdentifier(tagName: JsxTagNameExpression) {
// TODO (yuisu): comment
if (tagName.kind === SyntaxKind.PropertyAccessExpression || tagName.kind === SyntaxKind.ThisKeyword) {
return false;
}
else {
return isIntrinsicJsxName((<Identifier>tagName).text);
}
}
/**
* Get attributes type of the JSX opening-like element. The result is from resolving "attributes" property of the opening-like element.
*
* @param openingLikeElement a JSX opening-like element
* @param filter a function to remove attributes that will not participate in checking whether attributes are assignable
* @return an anonymous type (similar to the one returned by checkObjectLiteral) in which its properties are attributes property.
* @remarks Because this function calls getSpreadType, it needs to use the same checks as checkObjectLiteral,
* which also calls getSpreadType.
*/
function createJsxAttributesTypeFromAttributesProperty(openingLikeElement: JsxOpeningLikeElement, filter?: (symbol: Symbol) => boolean, checkMode?: CheckMode) {
const attributes = openingLikeElement.attributes;
let attributesTable = createMap<Symbol>();
let spread: Type = emptyObjectType;
let attributesArray: Symbol[] = [];
let hasSpreadAnyType = false;
let typeToIntersect: Type;
let explicitlySpecifyChildrenAttribute = false;
const jsxChildrenPropertyName = getJsxElementChildrenPropertyname();
for (const attributeDecl of attributes.properties) {
const member = attributeDecl.symbol;
if (isJsxAttribute(attributeDecl)) {
const exprType = attributeDecl.initializer ?
checkExpression(attributeDecl.initializer, checkMode) :
trueType; // <Elem attr /> is sugar for <Elem attr={true} />
const attributeSymbol = <TransientSymbol>createSymbol(SymbolFlags.Property | SymbolFlags.Transient | member.flags, member.name);
attributeSymbol.declarations = member.declarations;
attributeSymbol.parent = member.parent;
if (member.valueDeclaration) {
attributeSymbol.valueDeclaration = member.valueDeclaration;
}
attributeSymbol.type = exprType;
attributeSymbol.target = member;
attributesTable.set(attributeSymbol.name, attributeSymbol);
attributesArray.push(attributeSymbol);
if (attributeDecl.name.text === jsxChildrenPropertyName) {
explicitlySpecifyChildrenAttribute = true;
}
}
else {
Debug.assert(attributeDecl.kind === SyntaxKind.JsxSpreadAttribute);
if (attributesArray.length > 0) {
spread = getSpreadType(spread, createJsxAttributesType(attributes.symbol, attributesTable));
attributesArray = [];
attributesTable = createMap<Symbol>();
}
const exprType = checkExpression(attributeDecl.expression);
if (isTypeAny(exprType)) {
hasSpreadAnyType = true;
}
if (isValidSpreadType(exprType)) {
spread = getSpreadType(spread, exprType);
}
else {
typeToIntersect = typeToIntersect ? getIntersectionType([typeToIntersect, exprType]) : exprType;
}
}
}
if (!hasSpreadAnyType) {
if (spread !== emptyObjectType) {
if (attributesArray.length > 0) {
spread = getSpreadType(spread, createJsxAttributesType(attributes.symbol, attributesTable));
}
attributesArray = getPropertiesOfType(spread);
}
attributesTable = createMap<Symbol>();
for (const attr of attributesArray) {
if (!filter || filter(attr)) {
attributesTable.set(attr.name, attr);
}
}
}
// Handle children attribute
const parent = openingLikeElement.parent.kind === SyntaxKind.JsxElement ? openingLikeElement.parent as JsxElement : undefined;
// We have to check that openingElement of the parent is the one we are visiting as this may not be true for selfClosingElement
if (parent && parent.openingElement === openingLikeElement && parent.children.length > 0) {
const childrenTypes: Type[] = [];
for (const child of (parent as JsxElement).children) {
// In React, JSX text that contains only whitespaces will be ignored so we don't want to type-check that
// because then type of children property will have constituent of string type.
if (child.kind === SyntaxKind.JsxText) {
if (!child.containsOnlyWhiteSpaces) {
childrenTypes.push(stringType);
}
}
else {
childrenTypes.push(checkExpression(child, checkMode));
}
}
if (!hasSpreadAnyType && jsxChildrenPropertyName && jsxChildrenPropertyName !== "") {
// Error if there is a attribute named "children" explicitly specified and children element.
// This is because children element will overwrite the value from attributes.
// Note: we will not warn "children" attribute overwritten if "children" attribute is specified in object spread.
if (explicitlySpecifyChildrenAttribute) {
error(attributes, Diagnostics._0_are_specified_twice_The_attribute_named_0_will_be_overwritten, jsxChildrenPropertyName);
}
// If there are children in the body of JSX element, create dummy attribute "children" with anyType so that it will pass the attribute checking process
const childrenPropSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, jsxChildrenPropertyName);
childrenPropSymbol.type = childrenTypes.length === 1 ?
childrenTypes[0] :
createArrayType(getUnionType(childrenTypes, /*subtypeReduction*/ false));
attributesTable.set(jsxChildrenPropertyName, childrenPropSymbol);
}
}
if (hasSpreadAnyType) {
return anyType;
}
const attributeType = createJsxAttributesType(attributes.symbol, attributesTable);
return typeToIntersect && attributesTable.size ? getIntersectionType([typeToIntersect, attributeType]) :
typeToIntersect ? typeToIntersect : attributeType;
/**
* Create anonymous type from given attributes symbol table.
* @param symbol a symbol of JsxAttributes containing attributes corresponding to attributesTable
* @param attributesTable a symbol table of attributes property
*/
function createJsxAttributesType(symbol: Symbol, attributesTable: Map<Symbol>) {
const result = createAnonymousType(symbol, attributesTable, emptyArray, emptyArray, /*stringIndexInfo*/ undefined, /*numberIndexInfo*/ undefined);
result.flags |= TypeFlags.JsxAttributes | TypeFlags.ContainsObjectLiteral;
result.objectFlags |= ObjectFlags.ObjectLiteral;
return result;
}
}
/**
* Check attributes property of opening-like element. This function is called during chooseOverload to get call signature of a JSX opening-like element.
* (See "checkApplicableSignatureForJsxOpeningLikeElement" for how the function is used)
* @param node a JSXAttributes to be resolved of its type
*/
function checkJsxAttributes(node: JsxAttributes, checkMode?: CheckMode) {
return createJsxAttributesTypeFromAttributesProperty(node.parent as JsxOpeningLikeElement, /*filter*/ undefined, checkMode);
}
function getJsxType(name: string) {
let jsxType = jsxTypes.get(name);
if (jsxType === undefined) {
jsxTypes.set(name, jsxType = getExportedTypeFromNamespace(JsxNames.JSX, name) || unknownType);
}
return jsxType;
}
/**
* Looks up an intrinsic tag name and returns a symbol that either points to an intrinsic
* property (in which case nodeLinks.jsxFlags will be IntrinsicNamedElement) or an intrinsic
* string index signature (in which case nodeLinks.jsxFlags will be IntrinsicIndexedElement).
* May also return unknownSymbol if both of these lookups fail.
*/
function getIntrinsicTagSymbol(node: JsxOpeningLikeElement | JsxClosingElement): Symbol {
const links = getNodeLinks(node);
if (!links.resolvedSymbol) {
const intrinsicElementsType = getJsxType(JsxNames.IntrinsicElements);
if (intrinsicElementsType !== unknownType) {
// Property case
const intrinsicProp = getPropertyOfType(intrinsicElementsType, (<Identifier>node.tagName).text);
if (intrinsicProp) {
links.jsxFlags |= JsxFlags.IntrinsicNamedElement;
return links.resolvedSymbol = intrinsicProp;
}
// Intrinsic string indexer case
const indexSignatureType = getIndexTypeOfType(intrinsicElementsType, IndexKind.String);
if (indexSignatureType) {
links.jsxFlags |= JsxFlags.IntrinsicIndexedElement;
return links.resolvedSymbol = intrinsicElementsType.symbol;
}
// Wasn't found
error(node, Diagnostics.Property_0_does_not_exist_on_type_1, (<Identifier>node.tagName).text, "JSX." + JsxNames.IntrinsicElements);
return links.resolvedSymbol = unknownSymbol;
}
else {
if (noImplicitAny) {
error(node, Diagnostics.JSX_element_implicitly_has_type_any_because_no_interface_JSX_0_exists, JsxNames.IntrinsicElements);
}
return links.resolvedSymbol = unknownSymbol;
}
}
return links.resolvedSymbol;
}
/**
* Given a JSX element that is a class element, finds the Element Instance Type. If the
* element is not a class element, or the class element type cannot be determined, returns 'undefined'.
* For example, in the element <MyClass>, the element instance type is `MyClass` (not `typeof MyClass`).
*/
function getJsxElementInstanceType(node: JsxOpeningLikeElement, valueType: Type) {
Debug.assert(!(valueType.flags & TypeFlags.Union));
if (isTypeAny(valueType)) {
// Short-circuit if the class tag is using an element type 'any'
return anyType;
}
// Resolve the signatures, preferring constructor
let signatures = getSignaturesOfType(valueType, SignatureKind.Construct);
if (signatures.length === 0) {
// No construct signatures, try call signatures
signatures = getSignaturesOfType(valueType, SignatureKind.Call);
if (signatures.length === 0) {
// We found no signatures at all, which is an error
error(node.tagName, Diagnostics.JSX_element_type_0_does_not_have_any_construct_or_call_signatures, getTextOfNode(node.tagName));
return unknownType;
}
}
const instantiatedSignatures = [];
for (const signature of signatures) {
if (signature.typeParameters) {
const typeArguments = fillMissingTypeArguments(/*typeArguments*/ undefined, signature.typeParameters, /*minTypeArgumentCount*/ 0);
instantiatedSignatures.push(getSignatureInstantiation(signature, typeArguments));
}
else {
instantiatedSignatures.push(signature);
}
}
return getUnionType(map(instantiatedSignatures, getReturnTypeOfSignature), /*subtypeReduction*/ true);
}
/**
* Look into JSX namespace and then look for container with matching name as nameOfAttribPropContainer.
* Get a single property from that container if existed. Report an error if there are more than one property.
*
* @param nameOfAttribPropContainer a string of value JsxNames.ElementAttributesPropertyNameContainer or JsxNames.ElementChildrenAttributeNameContainer
* if other string is given or the container doesn't exist, return undefined.
*/
function getNameFromJsxElementAttributesContainer(nameOfAttribPropContainer: string): string {
// JSX
const jsxNamespace = getGlobalSymbol(JsxNames.JSX, SymbolFlags.Namespace, /*diagnosticMessage*/ undefined);
// JSX.ElementAttributesProperty | JSX.ElementChildrenAttribute [symbol]
const jsxElementAttribPropInterfaceSym = jsxNamespace && getSymbol(jsxNamespace.exports, nameOfAttribPropContainer, SymbolFlags.Type);
// JSX.ElementAttributesProperty | JSX.ElementChildrenAttribute [type]
const jsxElementAttribPropInterfaceType = jsxElementAttribPropInterfaceSym && getDeclaredTypeOfSymbol(jsxElementAttribPropInterfaceSym);
// The properties of JSX.ElementAttributesProperty | JSX.ElementChildrenAttribute
const propertiesOfJsxElementAttribPropInterface = jsxElementAttribPropInterfaceType && getPropertiesOfType(jsxElementAttribPropInterfaceType);
if (propertiesOfJsxElementAttribPropInterface) {
// Element Attributes has zero properties, so the element attributes type will be the class instance type
if (propertiesOfJsxElementAttribPropInterface.length === 0) {
return "";
}
// Element Attributes has one property, so the element attributes type will be the type of the corresponding
// property of the class instance type
else if (propertiesOfJsxElementAttribPropInterface.length === 1) {
return propertiesOfJsxElementAttribPropInterface[0].name;
}
else if (propertiesOfJsxElementAttribPropInterface.length > 1) {
// More than one property on ElementAttributesProperty is an error
error(jsxElementAttribPropInterfaceSym.declarations[0], Diagnostics.The_global_type_JSX_0_may_not_have_more_than_one_property, nameOfAttribPropContainer);
}
}
return undefined;
}
/// e.g. "props" for React.d.ts,
/// or 'undefined' if ElementAttributesProperty doesn't exist (which means all
/// non-intrinsic elements' attributes type is 'any'),
/// or '' if it has 0 properties (which means every
/// non-intrinsic elements' attributes type is the element instance type)
function getJsxElementPropertiesName() {
if (!_hasComputedJsxElementPropertiesName) {
_hasComputedJsxElementPropertiesName = true;
_jsxElementPropertiesName = getNameFromJsxElementAttributesContainer(JsxNames.ElementAttributesPropertyNameContainer);
}
return _jsxElementPropertiesName;
}
function getJsxElementChildrenPropertyname(): string {
if (!_hasComputedJsxElementChildrenPropertyName) {
_hasComputedJsxElementChildrenPropertyName = true;
_jsxElementChildrenPropertyName = getNameFromJsxElementAttributesContainer(JsxNames.ElementChildrenAttributeNameContainer);
}
return _jsxElementChildrenPropertyName;
}
function getApparentTypeOfJsxPropsType(propsType: Type): Type {
if (!propsType) {
return undefined;
}
if (propsType.flags & TypeFlags.Intersection) {
const propsApparentType: Type[] = [];
for (const t of (<UnionOrIntersectionType>propsType).types) {
propsApparentType.push(getApparentType(t));
}
return getIntersectionType(propsApparentType);
}
return getApparentType(propsType);
}
/**
* Get JSX attributes type by trying to resolve openingLikeElement as a stateless function component.
* Return only attributes type of successfully resolved call signature.
* This function assumes that the caller handled other possible element type of the JSX element (e.g. stateful component)
* Unlike tryGetAllJsxStatelessFunctionAttributesType, this function is a default behavior of type-checkers.
* @param openingLikeElement a JSX opening-like element to find attributes type
* @param elementType a type of the opening-like element. This elementType can't be an union type
* @param elemInstanceType an element instance type (the result of newing or invoking this tag)
* @param elementClassType a JSX-ElementClass type. This is a result of looking up ElementClass interface in the JSX global
*/
function defaultTryGetJsxStatelessFunctionAttributesType(openingLikeElement: JsxOpeningLikeElement, elementType: Type, elemInstanceType: Type, elementClassType?: Type): Type {
Debug.assert(!(elementType.flags & TypeFlags.Union));
if (!elementClassType || !isTypeAssignableTo(elemInstanceType, elementClassType)) {
const jsxStatelessElementType = getJsxGlobalStatelessElementType();
if (jsxStatelessElementType) {
// We don't call getResolvedSignature here because we have already resolve the type of JSX Element.
const callSignature = getResolvedJsxStatelessFunctionSignature(openingLikeElement, elementType, /*candidatesOutArray*/ undefined);
if (callSignature !== unknownSignature) {
const callReturnType = callSignature && getReturnTypeOfSignature(callSignature);
let paramType = callReturnType && (callSignature.parameters.length === 0 ? emptyObjectType : getTypeOfSymbol(callSignature.parameters[0]));
paramType = getApparentTypeOfJsxPropsType(paramType);
if (callReturnType && isTypeAssignableTo(callReturnType, jsxStatelessElementType)) {
// Intersect in JSX.IntrinsicAttributes if it exists
const intrinsicAttributes = getJsxType(JsxNames.IntrinsicAttributes);
if (intrinsicAttributes !== unknownType) {
paramType = intersectTypes(intrinsicAttributes, paramType);
}
return paramType;
}
}
}
}
return undefined;
}
/**
* Get JSX attributes type by trying to resolve openingLikeElement as a stateless function component.
* Return all attributes type of resolved call signature including candidate signatures.
* This function assumes that the caller handled other possible element type of the JSX element.
* This function is a behavior used by language service when looking up completion in JSX element.
* @param openingLikeElement a JSX opening-like element to find attributes type
* @param elementType a type of the opening-like element. This elementType can't be an union type
* @param elemInstanceType an element instance type (the result of newing or invoking this tag)
* @param elementClassType a JSX-ElementClass type. This is a result of looking up ElementClass interface in the JSX global
*/
function tryGetAllJsxStatelessFunctionAttributesType(openingLikeElement: JsxOpeningLikeElement, elementType: Type, elemInstanceType: Type, elementClassType?: Type): Type {
Debug.assert(!(elementType.flags & TypeFlags.Union));
if (!elementClassType || !isTypeAssignableTo(elemInstanceType, elementClassType)) {
// Is this is a stateless function component? See if its single signature's return type is assignable to the JSX Element Type
const jsxStatelessElementType = getJsxGlobalStatelessElementType();
if (jsxStatelessElementType) {
// We don't call getResolvedSignature because here we have already resolve the type of JSX Element.
const candidatesOutArray: Signature[] = [];
getResolvedJsxStatelessFunctionSignature(openingLikeElement, elementType, candidatesOutArray);
let result: Type;
let allMatchingAttributesType: Type;
for (const candidate of candidatesOutArray) {
const callReturnType = getReturnTypeOfSignature(candidate);
let paramType = callReturnType && (candidate.parameters.length === 0 ? emptyObjectType : getTypeOfSymbol(candidate.parameters[0]));
paramType = getApparentTypeOfJsxPropsType(paramType);
if (callReturnType && isTypeAssignableTo(callReturnType, jsxStatelessElementType)) {
let shouldBeCandidate = true;
for (const attribute of openingLikeElement.attributes.properties) {
if (isJsxAttribute(attribute) &&
isUnhyphenatedJsxName(attribute.name.text) &&
!getPropertyOfType(paramType, attribute.name.text)) {
shouldBeCandidate = false;
break;
}
}
if (shouldBeCandidate) {
result = intersectTypes(result, paramType);
}
allMatchingAttributesType = intersectTypes(allMatchingAttributesType, paramType);
}
}
// If we can't find any matching, just return everything.
if (!result) {
result = allMatchingAttributesType;
}
// Intersect in JSX.IntrinsicAttributes if it exists
const intrinsicAttributes = getJsxType(JsxNames.IntrinsicAttributes);
if (intrinsicAttributes !== unknownType) {
result = intersectTypes(intrinsicAttributes, result);
}
return result;
}
}
return undefined;
}
/**
* Resolve attributes type of the given opening-like element. The attributes type is a type of attributes associated with the given elementType.
* For instance:
* declare function Foo(attr: { p1: string}): JSX.Element;
* <Foo p1={10} />; // This function will try resolve "Foo" and return an attributes type of "Foo" which is "{ p1: string }"
*
* The function is intended to initially be called from getAttributesTypeFromJsxOpeningLikeElement which already handle JSX-intrinsic-element..
* This function will try to resolve custom JSX attributes type in following order: string literal, stateless function, and stateful component
*
* @param openingLikeElement a non-intrinsic JSXOPeningLikeElement
* @param shouldIncludeAllStatelessAttributesType a boolean indicating whether to include all attributes types from all stateless function signature
* @param elementType an instance type of the given opening-like element. If undefined, the function will check type openinglikeElement's tagname.
* @param elementClassType a JSX-ElementClass type. This is a result of looking up ElementClass interface in the JSX global (imported from react.d.ts)
* @return attributes type if able to resolve the type of node
* anyType if there is no type ElementAttributesProperty or there is an error
* emptyObjectType if there is no "prop" in the element instance type
*/
function resolveCustomJsxElementAttributesType(openingLikeElement: JsxOpeningLikeElement,
shouldIncludeAllStatelessAttributesType: boolean,
elementType?: Type,
elementClassType?: Type): Type {
if (!elementType) {
elementType = checkExpression(openingLikeElement.tagName);
}
if (elementType.flags & TypeFlags.Union) {
const types = (elementType as UnionType).types;
return getUnionType(types.map(type => {
return resolveCustomJsxElementAttributesType(openingLikeElement, shouldIncludeAllStatelessAttributesType, type, elementClassType);
}), /*subtypeReduction*/ true);
}
// If the elemType is a string type, we have to return anyType to prevent an error downstream as we will try to find construct or call signature of the type
if (elementType.flags & TypeFlags.String) {
return anyType;
}
else if (elementType.flags & TypeFlags.StringLiteral) {
// If the elemType is a stringLiteral type, we can then provide a check to make sure that the string literal type is one of the Jsx intrinsic element type
// For example:
// var CustomTag: "h1" = "h1";
// <CustomTag> Hello World </CustomTag>
const intrinsicElementsType = getJsxType(JsxNames.IntrinsicElements);
if (intrinsicElementsType !== unknownType) {
const stringLiteralTypeName = (<StringLiteralType>elementType).value;
const intrinsicProp = getPropertyOfType(intrinsicElementsType, stringLiteralTypeName);
if (intrinsicProp) {
return getTypeOfSymbol(intrinsicProp);
}
const indexSignatureType = getIndexTypeOfType(intrinsicElementsType, IndexKind.String);
if (indexSignatureType) {
return indexSignatureType;
}
error(openingLikeElement, Diagnostics.Property_0_does_not_exist_on_type_1, stringLiteralTypeName, "JSX." + JsxNames.IntrinsicElements);
}
// If we need to report an error, we already done so here. So just return any to prevent any more error downstream
return anyType;
}
// Get the element instance type (the result of newing or invoking this tag)
const elemInstanceType = getJsxElementInstanceType(openingLikeElement, elementType);
// If we should include all stateless attributes type, then get all attributes type from all stateless function signature.
// Otherwise get only attributes type from the signature picked by choose-overload logic.
const statelessAttributesType = shouldIncludeAllStatelessAttributesType ?
tryGetAllJsxStatelessFunctionAttributesType(openingLikeElement, elementType, elemInstanceType, elementClassType) :
defaultTryGetJsxStatelessFunctionAttributesType(openingLikeElement, elementType, elemInstanceType, elementClassType);
if (statelessAttributesType) {
return statelessAttributesType;
}
// Issue an error if this return type isn't assignable to JSX.ElementClass
if (elementClassType) {
checkTypeRelatedTo(elemInstanceType, elementClassType, assignableRelation, openingLikeElement, Diagnostics.JSX_element_type_0_is_not_a_constructor_function_for_JSX_elements);
}
if (isTypeAny(elemInstanceType)) {
return elemInstanceType;
}
const propsName = getJsxElementPropertiesName();
if (propsName === undefined) {
// There is no type ElementAttributesProperty, return 'any'
return anyType;
}
else if (propsName === "") {
// If there is no e.g. 'props' member in ElementAttributesProperty, use the element class type instead
return elemInstanceType;
}
else {
const attributesType = getTypeOfPropertyOfType(elemInstanceType, propsName);
if (!attributesType) {
// There is no property named 'props' on this instance type
return emptyObjectType;
}
else if (isTypeAny(attributesType) || (attributesType === unknownType)) {
// Props is of type 'any' or unknown
return attributesType;
}
else {
// Normal case -- add in IntrinsicClassElements<T> and IntrinsicElements
let apparentAttributesType = attributesType;
const intrinsicClassAttribs = getJsxType(JsxNames.IntrinsicClassAttributes);
if (intrinsicClassAttribs !== unknownType) {
const typeParams = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(intrinsicClassAttribs.symbol);
if (typeParams) {
if (typeParams.length === 1) {
apparentAttributesType = intersectTypes(createTypeReference(<GenericType>intrinsicClassAttribs, [elemInstanceType]), apparentAttributesType);
}
}
else {
apparentAttributesType = intersectTypes(attributesType, intrinsicClassAttribs);
}
}
const intrinsicAttribs = getJsxType(JsxNames.IntrinsicAttributes);
if (intrinsicAttribs !== unknownType) {
apparentAttributesType = intersectTypes(intrinsicAttribs, apparentAttributesType);
}
return apparentAttributesType;
}
}
}
/**
* Get attributes type of the given intrinsic opening-like Jsx element by resolving the tag name.
* The function is intended to be called from a function which has checked that the opening element is an intrinsic element.
* @param node an intrinsic JSX opening-like element
*/
function getIntrinsicAttributesTypeFromJsxOpeningLikeElement(node: JsxOpeningLikeElement): Type {
Debug.assert(isJsxIntrinsicIdentifier(node.tagName));
const links = getNodeLinks(node);
if (!links.resolvedJsxElementAttributesType) {
const symbol = getIntrinsicTagSymbol(node);
if (links.jsxFlags & JsxFlags.IntrinsicNamedElement) {
return links.resolvedJsxElementAttributesType = getTypeOfSymbol(symbol);
}
else if (links.jsxFlags & JsxFlags.IntrinsicIndexedElement) {
return links.resolvedJsxElementAttributesType = getIndexInfoOfSymbol(symbol, IndexKind.String).type;
}
else {
return links.resolvedJsxElementAttributesType = unknownType;
}
}
return links.resolvedJsxElementAttributesType;
}
/**
* Get attributes type of the given custom opening-like JSX element.
* This function is intended to be called from a caller that handles intrinsic JSX element already.
* @param node a custom JSX opening-like element
* @param shouldIncludeAllStatelessAttributesType a boolean value used by language service to get all possible attributes type from an overload stateless function component
*/
function getCustomJsxElementAttributesType(node: JsxOpeningLikeElement, shouldIncludeAllStatelessAttributesType: boolean): Type {
const links = getNodeLinks(node);
if (!links.resolvedJsxElementAttributesType) {
const elemClassType = getJsxGlobalElementClassType();
return links.resolvedJsxElementAttributesType = resolveCustomJsxElementAttributesType(node, shouldIncludeAllStatelessAttributesType, /*elementType*/ undefined, elemClassType);
}
return links.resolvedJsxElementAttributesType;
}
/**
* Get all possible attributes type, especially from an overload stateless function component, of the given JSX opening-like element.
* This function is called by language service (see: completions-tryGetGlobalSymbols).
* @param node a JSX opening-like element to get attributes type for
*/
function getAllAttributesTypeFromJsxOpeningLikeElement(node: JsxOpeningLikeElement): Type {
if (isJsxIntrinsicIdentifier(node.tagName)) {
return getIntrinsicAttributesTypeFromJsxOpeningLikeElement(node);
}
else {
// Because in language service, the given JSX opening-like element may be incomplete and therefore,
// we can't resolve to exact signature if the element is a stateless function component so the best thing to do is return all attributes type from all overloads.
return getCustomJsxElementAttributesType(node, /*shouldIncludeAllStatelessAttributesType*/ true);
}
}
/**
* Get the attributes type, which indicates the attributes that are valid on the given JSXOpeningLikeElement.
* @param node a JSXOpeningLikeElement node
* @return an attributes type of the given node
*/
function getAttributesTypeFromJsxOpeningLikeElement(node: JsxOpeningLikeElement): Type {
if (isJsxIntrinsicIdentifier(node.tagName)) {
return getIntrinsicAttributesTypeFromJsxOpeningLikeElement(node);
}
else {
return getCustomJsxElementAttributesType(node, /*shouldIncludeAllStatelessAttributesType*/ false);
}
}
/**
* Given a JSX attribute, returns the symbol for the corresponds property
* of the element attributes type. Will return unknownSymbol for attributes
* that have no matching element attributes type property.
*/
function getJsxAttributePropertySymbol(attrib: JsxAttribute): Symbol {
const attributesType = getAttributesTypeFromJsxOpeningLikeElement(attrib.parent.parent as JsxOpeningElement);
const prop = getPropertyOfType(attributesType, attrib.name.text);
return prop || unknownSymbol;
}
function getJsxGlobalElementClassType(): Type {
if (!deferredJsxElementClassType) {
deferredJsxElementClassType = getExportedTypeFromNamespace(JsxNames.JSX, JsxNames.ElementClass);
}
return deferredJsxElementClassType;
}
function getJsxGlobalElementType(): Type {
if (!deferredJsxElementType) {
deferredJsxElementType = getExportedTypeFromNamespace(JsxNames.JSX, JsxNames.Element);
}
return deferredJsxElementType;
}
function getJsxGlobalStatelessElementType(): Type {
if (!deferredJsxStatelessElementType) {
const jsxElementType = getJsxGlobalElementType();
if (jsxElementType) {
deferredJsxStatelessElementType = getUnionType([jsxElementType, nullType]);
}
}
return deferredJsxStatelessElementType;
}
/**
* Returns all the properties of the Jsx.IntrinsicElements interface
*/
function getJsxIntrinsicTagNames(): Symbol[] {
const intrinsics = getJsxType(JsxNames.IntrinsicElements);
return intrinsics ? getPropertiesOfType(intrinsics) : emptyArray;
}
function checkJsxPreconditions(errorNode: Node) {
// Preconditions for using JSX
if ((compilerOptions.jsx || JsxEmit.None) === JsxEmit.None) {
error(errorNode, Diagnostics.Cannot_use_JSX_unless_the_jsx_flag_is_provided);
}
if (getJsxGlobalElementType() === undefined) {
if (noImplicitAny) {
error(errorNode, Diagnostics.JSX_element_implicitly_has_type_any_because_the_global_type_JSX_Element_does_not_exist);
}
}
}
function checkJsxOpeningLikeElement(node: JsxOpeningLikeElement) {
checkGrammarJsxElement(node);
checkJsxPreconditions(node);
// The reactNamespace/jsxFactory's root symbol should be marked as 'used' so we don't incorrectly elide its import.
// And if there is no reactNamespace/jsxFactory's symbol in scope when targeting React emit, we should issue an error.
const reactRefErr = compilerOptions.jsx === JsxEmit.React ? Diagnostics.Cannot_find_name_0 : undefined;
const reactNamespace = getJsxNamespace();
const reactSym = resolveName(node.tagName, reactNamespace, SymbolFlags.Value, reactRefErr, reactNamespace);
if (reactSym) {
// Mark local symbol as referenced here because it might not have been marked
// if jsx emit was not react as there wont be error being emitted
reactSym.isReferenced = true;
// If react symbol is alias, mark it as refereced
if (reactSym.flags & SymbolFlags.Alias && !isConstEnumOrConstEnumOnlyModule(resolveAlias(reactSym))) {
markAliasSymbolAsReferenced(reactSym);
}
}
checkJsxAttributesAssignableToTagNameAttributes(node);
}
/**
* Check if a property with the given name is known anywhere in the given type. In an object type, a property
* is considered known if the object type is empty and the check is for assignability, if the object type has
* index signatures, or if the property is actually declared in the object type. In a union or intersection
* type, a property is considered known if it is known in any constituent type.
* @param targetType a type to search a given name in
* @param name a property name to search
* @param isComparingJsxAttributes a boolean flag indicating whether we are searching in JsxAttributesType
*/
function isKnownProperty(targetType: Type, name: string, isComparingJsxAttributes: boolean): boolean {
if (targetType.flags & TypeFlags.Object) {
const resolved = resolveStructuredTypeMembers(<ObjectType>targetType);
if (resolved.stringIndexInfo ||
resolved.numberIndexInfo && isNumericLiteralName(name) ||
getPropertyOfType(targetType, name) ||
isComparingJsxAttributes && !isUnhyphenatedJsxName(name)) {
// For JSXAttributes, if the attribute has a hyphenated name, consider that the attribute to be known.
return true;
}
}
else if (targetType.flags & TypeFlags.UnionOrIntersection) {
for (const t of (<UnionOrIntersectionType>targetType).types) {
if (isKnownProperty(t, name, isComparingJsxAttributes)) {
return true;
}
}
}
return false;
}
/**
* Check whether the given attributes of JSX opening-like element is assignable to the tagName attributes.
* Get the attributes type of the opening-like element through resolving the tagName, "target attributes"
* Check assignablity between given attributes property, "source attributes", and the "target attributes"
* @param openingLikeElement an opening-like JSX element to check its JSXAttributes
*/
function checkJsxAttributesAssignableToTagNameAttributes(openingLikeElement: JsxOpeningLikeElement) {
// The function involves following steps:
// 1. Figure out expected attributes type by resolving tagName of the JSX opening-like element, targetAttributesType.
// During these steps, we will try to resolve the tagName as intrinsic name, stateless function, stateful component (in the order)
// 2. Solved JSX attributes type given by users, sourceAttributesType, which is by resolving "attributes" property of the JSX opening-like element.
// 3. Check if the two are assignable to each other
// targetAttributesType is a type of an attributes from resolving tagName of an opening-like JSX element.
const targetAttributesType = isJsxIntrinsicIdentifier(openingLikeElement.tagName) ?
getIntrinsicAttributesTypeFromJsxOpeningLikeElement(openingLikeElement) :
getCustomJsxElementAttributesType(openingLikeElement, /*shouldIncludeAllStatelessAttributesType*/ false);
// sourceAttributesType is a type of an attributes properties.
// i.e <div attr1={10} attr2="string" />
// attr1 and attr2 are treated as JSXAttributes attached in the JsxOpeningLikeElement as "attributes".
const sourceAttributesType = createJsxAttributesTypeFromAttributesProperty(openingLikeElement,
attribute => {
return isUnhyphenatedJsxName(attribute.name) || !!(getPropertyOfType(targetAttributesType, attribute.name));
});
// If the targetAttributesType is an emptyObjectType, indicating that there is no property named 'props' on this instance type.
// but there exists a sourceAttributesType, we need to explicitly give an error as normal assignability check allow excess properties and will pass.
if (targetAttributesType === emptyObjectType && (isTypeAny(sourceAttributesType) || (<ResolvedType>sourceAttributesType).properties.length > 0)) {
error(openingLikeElement, Diagnostics.JSX_element_class_does_not_support_attributes_because_it_does_not_have_a_0_property, getJsxElementPropertiesName());
}
else {
// Check if sourceAttributesType assignable to targetAttributesType though this check will allow excess properties
const isSourceAttributeTypeAssignableToTarget = checkTypeAssignableTo(sourceAttributesType, targetAttributesType, openingLikeElement.attributes.properties.length > 0 ? openingLikeElement.attributes : openingLikeElement);
// After we check for assignability, we will do another pass to check that all explicitly specified attributes have correct name corresponding in targetAttributeType.
// This will allow excess properties in spread type as it is very common pattern to spread outter attributes into React component in its render method.
if (isSourceAttributeTypeAssignableToTarget && !isTypeAny(sourceAttributesType) && !isTypeAny(targetAttributesType)) {
for (const attribute of openingLikeElement.attributes.properties) {
if (isJsxAttribute(attribute) && !isKnownProperty(targetAttributesType, attribute.name.text, /*isComparingJsxAttributes*/ true)) {
error(attribute, Diagnostics.Property_0_does_not_exist_on_type_1, attribute.name.text, typeToString(targetAttributesType));
// We break here so that errors won't be cascading
break;
}
}
}
}
}
function checkJsxExpression(node: JsxExpression, checkMode?: CheckMode) {
if (node.expression) {
const type = checkExpression(node.expression, checkMode);
if (node.dotDotDotToken && type !== anyType && !isArrayType(type)) {
error(node, Diagnostics.JSX_spread_child_must_be_an_array_type, node.toString(), typeToString(type));
}
return type;
}
else {
return unknownType;
}
}
// If a symbol is a synthesized symbol with no value declaration, we assume it is a property. Example of this are the synthesized
// '.prototype' property as well as synthesized tuple index properties.
function getDeclarationKindFromSymbol(s: Symbol) {
return s.valueDeclaration ? s.valueDeclaration.kind : SyntaxKind.PropertyDeclaration;
}
function getDeclarationNodeFlagsFromSymbol(s: Symbol): NodeFlags {
return s.valueDeclaration ? getCombinedNodeFlags(s.valueDeclaration) : 0;
}
function isMethodLike(symbol: Symbol) {
return !!(symbol.flags & SymbolFlags.Method || getCheckFlags(symbol) & CheckFlags.SyntheticMethod);
}
/**
* Check whether the requested property access is valid.
* Returns true if node is a valid property access, and false otherwise.
* @param node The node to be checked.
* @param left The left hand side of the property access (e.g.: the super in `super.foo`).
* @param type The type of left.
* @param prop The symbol for the right hand side of the property access.
*/
function checkPropertyAccessibility(node: PropertyAccessExpression | QualifiedName | VariableLikeDeclaration, left: Expression | QualifiedName, type: Type, prop: Symbol): boolean {
const flags = getDeclarationModifierFlagsFromSymbol(prop);
const errorNode = node.kind === SyntaxKind.PropertyAccessExpression || node.kind === SyntaxKind.VariableDeclaration ?
(<PropertyAccessExpression | VariableDeclaration>node).name :
(<QualifiedName>node).right;
if (getCheckFlags(prop) & CheckFlags.ContainsPrivate) {
// Synthetic property with private constituent property
error(errorNode, Diagnostics.Property_0_has_conflicting_declarations_and_is_inaccessible_in_type_1, symbolToString(prop), typeToString(type));
return false;
}
if (left.kind === SyntaxKind.SuperKeyword) {
// 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 (languageVersion < ScriptTarget.ES2015) {
const hasNonMethodDeclaration = forEachProperty(prop, p => {
const propKind = getDeclarationKindFromSymbol(p);
return propKind !== SyntaxKind.MethodDeclaration && propKind !== SyntaxKind.MethodSignature;
});
if (hasNonMethodDeclaration) {
error(errorNode, Diagnostics.Only_public_and_protected_methods_of_the_base_class_are_accessible_via_the_super_keyword);
return false;
}
}
if (flags & ModifierFlags.Abstract) {
// A method cannot be accessed in a super property access if the method is abstract.
// This error could mask a private property access error. But, a member
// cannot simultaneously be private and abstract, so this will trigger an
// additional error elsewhere.
error(errorNode, Diagnostics.Abstract_method_0_in_class_1_cannot_be_accessed_via_super_expression, symbolToString(prop), typeToString(getDeclaringClass(prop)));
return false;
}
}
// Public properties are otherwise accessible.
if (!(flags & ModifierFlags.NonPublicAccessibilityModifier)) {
return true;
}
// Property is known to be private or protected at this point
// Private property is accessible if the property is within the declaring class
if (flags & ModifierFlags.Private) {
const declaringClassDeclaration = <ClassLikeDeclaration>getClassLikeDeclarationOfSymbol(getParentOfSymbol(prop));
if (!isNodeWithinClass(node, declaringClassDeclaration)) {
error(errorNode, Diagnostics.Property_0_is_private_and_only_accessible_within_class_1, symbolToString(prop), typeToString(getDeclaringClass(prop)));
return false;
}
return true;
}
// Property is known to be protected at this point
// All protected properties of a supertype are accessible in a super access
if (left.kind === SyntaxKind.SuperKeyword) {
return true;
}
// Find the first enclosing class that has the declaring classes of the protected constituents
// of the property as base classes
const enclosingClass = forEachEnclosingClass(node, enclosingDeclaration => {
const enclosingClass = <InterfaceType>getDeclaredTypeOfSymbol(getSymbolOfNode(enclosingDeclaration));
return isClassDerivedFromDeclaringClasses(enclosingClass, prop) ? enclosingClass : undefined;
});
// A protected property is accessible if the property is within the declaring class or classes derived from it
if (!enclosingClass) {
error(errorNode, Diagnostics.Property_0_is_protected_and_only_accessible_within_class_1_and_its_subclasses, symbolToString(prop), typeToString(getDeclaringClass(prop) || type));
return false;
}
// No further restrictions for static properties
if (flags & ModifierFlags.Static) {
return true;
}
// An instance property must be accessed through an instance of the enclosing class
if (type.flags & TypeFlags.TypeParameter && (type as TypeParameter).isThisType) {
// get the original type -- represented as the type constraint of the 'this' type
type = getConstraintOfTypeParameter(<TypeParameter>type);
}
if (!(getObjectFlags(getTargetType(type)) & ObjectFlags.ClassOrInterface && hasBaseType(type, enclosingClass))) {
error(errorNode, Diagnostics.Property_0_is_protected_and_only_accessible_through_an_instance_of_class_1, symbolToString(prop), typeToString(enclosingClass));
return false;
}
return true;
}
function checkNonNullExpression(node: Expression | QualifiedName) {
return checkNonNullType(checkExpression(node), node);
}
function checkNonNullType(type: Type, errorNode: Node): Type {
const kind = (strictNullChecks ? getFalsyFlags(type) : type.flags) & TypeFlags.Nullable;
if (kind) {
error(errorNode, kind & TypeFlags.Undefined ? kind & TypeFlags.Null ?
Diagnostics.Object_is_possibly_null_or_undefined :
Diagnostics.Object_is_possibly_undefined :
Diagnostics.Object_is_possibly_null);
const t = getNonNullableType(type);
return t.flags & (TypeFlags.Nullable | TypeFlags.Never) ? unknownType : t;
}
return type;
}
function checkPropertyAccessExpression(node: PropertyAccessExpression) {
return checkPropertyAccessExpressionOrQualifiedName(node, node.expression, node.name);
}
function checkQualifiedName(node: QualifiedName) {
return checkPropertyAccessExpressionOrQualifiedName(node, node.left, node.right);
}
function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, right: Identifier) {
const type = checkNonNullExpression(left);
if (isTypeAny(type) || type === silentNeverType) {
return type;
}
const apparentType = getApparentType(getWidenedType(type));
if (apparentType === unknownType || (type.flags & TypeFlags.TypeParameter && isTypeAny(apparentType))) {
// handle cases when type is Type parameter with invalid or any constraint
return apparentType;
}
const prop = getPropertyOfType(apparentType, right.text);
if (!prop) {
const stringIndexType = getIndexTypeOfType(apparentType, IndexKind.String);
if (stringIndexType) {
return stringIndexType;
}
if (right.text && !checkAndReportErrorForExtendingInterface(node)) {
reportNonexistentProperty(right, type.flags & TypeFlags.TypeParameter && (type as TypeParameter).isThisType ? apparentType : type);
}
return unknownType;
}
if (prop.valueDeclaration) {
if (isInPropertyInitializer(node) &&
!isBlockScopedNameDeclaredBeforeUse(prop.valueDeclaration, right)) {
error(right, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, right.text);
}
if (prop.valueDeclaration.kind === SyntaxKind.ClassDeclaration &&
node.parent && node.parent.kind !== SyntaxKind.TypeReference &&
!isInAmbientContext(prop.valueDeclaration) &&
!isBlockScopedNameDeclaredBeforeUse(prop.valueDeclaration, right)) {
error(right, Diagnostics.Class_0_used_before_its_declaration, right.text);
}
}
markPropertyAsReferenced(prop);
getNodeLinks(node).resolvedSymbol = prop;
checkPropertyAccessibility(node, left, apparentType, prop);
const propType = getDeclaredOrApparentType(prop, node);
const assignmentKind = getAssignmentTargetKind(node);
if (assignmentKind) {
if (isReferenceToReadonlyEntity(<Expression>node, prop) || isReferenceThroughNamespaceImport(<Expression>node)) {
error(right, Diagnostics.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property, right.text);
return unknownType;
}
}
// Only compute control flow type if this is a property access expression that isn't an
// assignment target, and the referenced property was declared as a variable, property,
// accessor, or optional method.
if (node.kind !== SyntaxKind.PropertyAccessExpression || assignmentKind === AssignmentKind.Definite ||
!(prop.flags & (SymbolFlags.Variable | SymbolFlags.Property | SymbolFlags.Accessor)) &&
!(prop.flags & SymbolFlags.Method && propType.flags & TypeFlags.Union)) {
return propType;
}
const flowType = getFlowTypeOfReference(node, propType);
return assignmentKind ? getBaseTypeOfLiteralType(flowType) : flowType;
}
function reportNonexistentProperty(propNode: Identifier, containingType: Type) {
let errorInfo: DiagnosticMessageChain;
if (containingType.flags & TypeFlags.Union && !(containingType.flags & TypeFlags.Primitive)) {
for (const subtype of (containingType as UnionType).types) {
if (!getPropertyOfType(subtype, propNode.text)) {
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1, declarationNameToString(propNode), typeToString(subtype));
break;
}
}
}
const suggestion = getSuggestionForNonexistentProperty(propNode, containingType);
if (suggestion) {
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2, declarationNameToString(propNode), typeToString(containingType), suggestion);
}
else {
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1, declarationNameToString(propNode), typeToString(containingType));
}
diagnostics.add(createDiagnosticForNodeFromMessageChain(propNode, errorInfo));
}
function getSuggestionForNonexistentProperty(node: Identifier, containingType: Type): string | undefined {
const suggestion = getSpellingSuggestionForName(node.text, getPropertiesOfObjectType(containingType), SymbolFlags.Value);
return suggestion && suggestion.name;
}
function getSuggestionForNonexistentSymbol(location: Node, name: string, meaning: SymbolFlags): string {
const result = resolveNameHelper(location, name, meaning, /*nameNotFoundMessage*/ undefined, name, (symbols, name, meaning) => {
const symbol = getSymbol(symbols, name, meaning);
if (symbol) {
// Sometimes the symbol is found when location is a return type of a function: `typeof x` and `x` is declared in the body of the function
// So the table *contains* `x` but `x` isn't actually in scope.
// However, resolveNameHelper will continue and call this callback again, so we'll eventually get a correct suggestion.
return symbol;
}
return getSpellingSuggestionForName(name, arrayFrom(symbols.values()), meaning);
});
if (result) {
return result.name;
}
}
/**
* Given a name and a list of symbols whose names are *not* equal to the name, return a spelling suggestion if there is one that is close enough.
* Names less than length 3 only check for case-insensitive equality, not levenshtein distance.
*
* If there is a candidate that's the same except for case, return that.
* If there is a candidate that's within one edit of the name, return that.
* Otherwise, return the candidate with the smallest Levenshtein distance,
* except for candidates:
* * With no name
* * Whose meaning doesn't match the `meaning` parameter.
* * Whose length differs from the target name by more than 0.3 of the length of the name.
* * Whose levenshtein distance is more than 0.4 of the length of the name
* (0.4 allows 1 substitution/transposition for every 5 characters,
* and 1 insertion/deletion at 3 characters)
* Names longer than 30 characters don't get suggestions because Levenshtein distance is an n**2 algorithm.
*/
function getSpellingSuggestionForName(name: string, symbols: Symbol[], meaning: SymbolFlags): Symbol | undefined {
const worstDistance = name.length * 0.4;
const maximumLengthDifference = Math.min(3, name.length * 0.34);
let bestDistance = Number.MAX_VALUE;
let bestCandidate = undefined;
if (name.length > 30) {
return undefined;
}
name = name.toLowerCase();
for (const candidate of symbols) {
if (candidate.flags & meaning &&
candidate.name &&
Math.abs(candidate.name.length - name.length) < maximumLengthDifference) {
const candidateName = candidate.name.toLowerCase();
if (candidateName === name) {
return candidate;
}
if (candidateName.length < 3 ||
name.length < 3 ||
candidateName === "eval" ||
candidateName === "intl" ||
candidateName === "undefined" ||
candidateName === "map" ||
candidateName === "nan" ||
candidateName === "set") {
continue;
}
const distance = levenshtein(name, candidateName);
if (distance > worstDistance) {
continue;
}
if (distance < 3) {
return candidate;
}
else if (distance < bestDistance) {
bestDistance = distance;
bestCandidate = candidate;
}
}
}
return bestCandidate;
}
function markPropertyAsReferenced(prop: Symbol) {
if (prop &&
noUnusedIdentifiers &&
(prop.flags & SymbolFlags.ClassMember) &&
prop.valueDeclaration && (getModifierFlags(prop.valueDeclaration) & ModifierFlags.Private)) {
if (getCheckFlags(prop) & CheckFlags.Instantiated) {
getSymbolLinks(prop).target.isReferenced = true;
}
else {
prop.isReferenced = true;
}
}
}
function isInPropertyInitializer(node: Node): boolean {
while (node) {
if (node.parent && node.parent.kind === SyntaxKind.PropertyDeclaration && (node.parent as PropertyDeclaration).initializer === node) {
return true;
}
node = node.parent;
}
return false;
}
function isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean {
const left = node.kind === SyntaxKind.PropertyAccessExpression
? (<PropertyAccessExpression>node).expression
: (<QualifiedName>node).left;
const type = checkExpression(left);
if (type !== unknownType && !isTypeAny(type)) {
const prop = getPropertyOfType(getWidenedType(type), propertyName);
if (prop) {
return checkPropertyAccessibility(node, left, type, prop);
}
}
return true;
}
/**
* Return the symbol of the for-in variable declared or referenced by the given for-in statement.
*/
function getForInVariableSymbol(node: ForInStatement): Symbol {
const initializer = node.initializer;
if (initializer.kind === SyntaxKind.VariableDeclarationList) {
const variable = (<VariableDeclarationList>initializer).declarations[0];
if (variable && !isBindingPattern(variable.name)) {
return getSymbolOfNode(variable);
}
}
else if (initializer.kind === SyntaxKind.Identifier) {
return getResolvedSymbol(<Identifier>initializer);
}
return undefined;
}
/**
* Return true if the given type is considered to have numeric property names.
*/
function hasNumericPropertyNames(type: Type) {
return getIndexTypeOfType(type, IndexKind.Number) && !getIndexTypeOfType(type, IndexKind.String);
}
/**
* Return true if given node is an expression consisting of an identifier (possibly parenthesized)
* that references a for-in variable for an object with numeric property names.
*/
function isForInVariableForNumericPropertyNames(expr: Expression) {
const e = skipParentheses(expr);
if (e.kind === SyntaxKind.Identifier) {
const symbol = getResolvedSymbol(<Identifier>e);
if (symbol.flags & SymbolFlags.Variable) {
let child: Node = expr;
let node = expr.parent;
while (node) {
if (node.kind === SyntaxKind.ForInStatement &&
child === (<ForInStatement>node).statement &&
getForInVariableSymbol(<ForInStatement>node) === symbol &&
hasNumericPropertyNames(getTypeOfExpression((<ForInStatement>node).expression))) {
return true;
}
child = node;
node = node.parent;
}
}
}
return false;
}
function checkIndexedAccess(node: ElementAccessExpression): Type {
const objectType = checkNonNullExpression(node.expression);
const indexExpression = node.argumentExpression;
if (!indexExpression) {
const sourceFile = getSourceFileOfNode(node);
if (node.parent.kind === SyntaxKind.NewExpression && (<NewExpression>node.parent).expression === node) {
const start = skipTrivia(sourceFile.text, node.expression.end);
const end = node.end;
grammarErrorAtPos(sourceFile, start, end - start, Diagnostics.new_T_cannot_be_used_to_create_an_array_Use_new_Array_T_instead);
}
else {
const start = node.end - "]".length;
const end = node.end;
grammarErrorAtPos(sourceFile, start, end - start, Diagnostics.Expression_expected);
}
return unknownType;
}
const indexType = isForInVariableForNumericPropertyNames(indexExpression) ? numberType : checkExpression(indexExpression);
if (objectType === unknownType || objectType === silentNeverType) {
return objectType;
}
if (isConstEnumObjectType(objectType) && indexExpression.kind !== SyntaxKind.StringLiteral) {
error(indexExpression, Diagnostics.A_const_enum_member_can_only_be_accessed_using_a_string_literal);
return unknownType;
}
return checkIndexedAccessIndexType(getIndexedAccessType(objectType, indexType, node), node);
}
function checkThatExpressionIsProperSymbolReference(expression: Expression, expressionType: Type, reportError: boolean): boolean {
if (expressionType === unknownType) {
// There is already an error, so no need to report one.
return false;
}
if (!isWellKnownSymbolSyntactically(expression)) {
return false;
}
// Make sure the property type is the primitive symbol type
if ((expressionType.flags & TypeFlags.ESSymbol) === 0) {
if (reportError) {
error(expression, Diagnostics.A_computed_property_name_of_the_form_0_must_be_of_type_symbol, getTextOfNode(expression));
}
return false;
}
// The name is Symbol.<someName>, so make sure Symbol actually resolves to the
// global Symbol object
const leftHandSide = <Identifier>(<PropertyAccessExpression>expression).expression;
const leftHandSideSymbol = getResolvedSymbol(leftHandSide);
if (!leftHandSideSymbol) {
return false;
}
const globalESSymbol = getGlobalESSymbolConstructorSymbol(/*reportErrors*/ true);
if (!globalESSymbol) {
// Already errored when we tried to look up the symbol
return false;
}
if (leftHandSideSymbol !== globalESSymbol) {
if (reportError) {
error(leftHandSide, Diagnostics.Symbol_reference_does_not_refer_to_the_global_Symbol_constructor_object);
}
return false;
}
return true;
}
function callLikeExpressionMayHaveTypeArguments(node: CallLikeExpression): node is CallExpression | NewExpression {
// TODO: Also include tagged templates (https://github.com/Microsoft/TypeScript/issues/11947)
return isCallOrNewExpression(node);
}
function resolveUntypedCall(node: CallLikeExpression): Signature {
if (callLikeExpressionMayHaveTypeArguments(node)) {
// Check type arguments even though we will give an error that untyped calls may not accept type arguments.
// This gets us diagnostics for the type arguments and marks them as referenced.
forEach(node.typeArguments, checkSourceElement);
}
if (node.kind === SyntaxKind.TaggedTemplateExpression) {
checkExpression((<TaggedTemplateExpression>node).template);
}
else if (node.kind !== SyntaxKind.Decorator) {
forEach((<CallExpression>node).arguments, argument => {
checkExpression(argument);
});
}
return anySignature;
}
function resolveErrorCall(node: CallLikeExpression): Signature {
resolveUntypedCall(node);
return unknownSignature;
}
// Re-order candidate signatures into the result array. Assumes the result array to be empty.
// The candidate list orders groups in reverse, but within a group signatures are kept in declaration order
// A nit here is that we reorder only signatures that belong to the same symbol,
// so order how inherited signatures are processed is still preserved.
// interface A { (x: string): void }
// interface B extends A { (x: 'foo'): string }
// const b: B;
// b('foo') // <- here overloads should be processed as [(x:'foo'): string, (x: string): void]
function reorderCandidates(signatures: Signature[], result: Signature[]): void {
let lastParent: Node;
let lastSymbol: Symbol;
let cutoffIndex = 0;
let index: number;
let specializedIndex = -1;
let spliceIndex: number;
Debug.assert(!result.length);
for (const signature of signatures) {
const symbol = signature.declaration && getSymbolOfNode(signature.declaration);
const parent = signature.declaration && signature.declaration.parent;
if (!lastSymbol || symbol === lastSymbol) {
if (lastParent && parent === lastParent) {
index++;
}
else {
lastParent = parent;
index = cutoffIndex;
}
}
else {
// current declaration belongs to a different symbol
// set cutoffIndex so re-orderings in the future won't change result set from 0 to cutoffIndex
index = cutoffIndex = result.length;
lastParent = parent;
}
lastSymbol = symbol;
// specialized signatures always need to be placed before non-specialized signatures regardless
// of the cutoff position; see GH#1133
if (signature.hasLiteralTypes) {
specializedIndex++;
spliceIndex = specializedIndex;
// The cutoff index always needs to be greater than or equal to the specialized signature index
// in order to prevent non-specialized signatures from being added before a specialized
// signature.
cutoffIndex++;
}
else {
spliceIndex = index;
}
result.splice(spliceIndex, 0, signature);
}
}
function getSpreadArgumentIndex(args: Expression[]): number {
for (let i = 0; i < args.length; i++) {
const arg = args[i];
if (arg && arg.kind === SyntaxKind.SpreadElement) {
return i;
}
}
return -1;
}
function hasCorrectArity(node: CallLikeExpression, args: Expression[], signature: Signature, signatureHelpTrailingComma = false) {
let argCount: number; // Apparent number of arguments we will have in this call
let typeArguments: NodeArray<TypeNode>; // Type arguments (undefined if none)
let callIsIncomplete: boolean; // In incomplete call we want to be lenient when we have too few arguments
let isDecorator: boolean;
let spreadArgIndex = -1;
if (isJsxOpeningLikeElement(node)) {
// The arity check will be done in "checkApplicableSignatureForJsxOpeningLikeElement".
return true;
}
if (node.kind === SyntaxKind.TaggedTemplateExpression) {
const tagExpression = <TaggedTemplateExpression>node;
// Even if the call is incomplete, we'll have a missing expression as our last argument,
// so we can say the count is just the arg list length
argCount = args.length;
typeArguments = undefined;
if (tagExpression.template.kind === SyntaxKind.TemplateExpression) {
// If a tagged template expression lacks a tail literal, the call is incomplete.
// Specifically, a template only can end in a TemplateTail or a Missing literal.
const templateExpression = <TemplateExpression>tagExpression.template;
const lastSpan = lastOrUndefined(templateExpression.templateSpans);
Debug.assert(lastSpan !== undefined); // we should always have at least one span.
callIsIncomplete = nodeIsMissing(lastSpan.literal) || !!lastSpan.literal.isUnterminated;
}
else {
// If the template didn't end in a backtick, or its beginning occurred right prior to EOF,
// then this might actually turn out to be a TemplateHead in the future;
// so we consider the call to be incomplete.
const templateLiteral = <LiteralExpression>tagExpression.template;
Debug.assert(templateLiteral.kind === SyntaxKind.NoSubstitutionTemplateLiteral);
callIsIncomplete = !!templateLiteral.isUnterminated;
}
}
else if (node.kind === SyntaxKind.Decorator) {
isDecorator = true;
typeArguments = undefined;
argCount = getEffectiveArgumentCount(node, /*args*/ undefined, signature);
}
else {
const callExpression = <CallExpression | NewExpression>node;
if (!callExpression.arguments) {
// This only happens when we have something of the form: 'new C'
Debug.assert(callExpression.kind === SyntaxKind.NewExpression);
return signature.minArgumentCount === 0;
}
argCount = signatureHelpTrailingComma ? args.length + 1 : args.length;
// If we are missing the close parenthesis, the call is incomplete.
callIsIncomplete = callExpression.arguments.end === callExpression.end;
typeArguments = callExpression.typeArguments;
spreadArgIndex = getSpreadArgumentIndex(args);
}
// If the user supplied type arguments, but the number of type arguments does not match
// the declared number of type parameters, the call has an incorrect arity.
const numTypeParameters = length(signature.typeParameters);
const minTypeArgumentCount = getMinTypeArgumentCount(signature.typeParameters);
const hasRightNumberOfTypeArgs = !typeArguments ||
(typeArguments.length >= minTypeArgumentCount && typeArguments.length <= numTypeParameters);
if (!hasRightNumberOfTypeArgs) {
return false;
}
// If spread arguments are present, check that they correspond to a rest parameter. If so, no
// further checking is necessary.
if (spreadArgIndex >= 0) {
return isRestParameterIndex(signature, spreadArgIndex) || spreadArgIndex >= signature.minArgumentCount;
}
// Too many arguments implies incorrect arity.
if (!signature.hasRestParameter && argCount > signature.parameters.length) {
return false;
}
// If the call is incomplete, we should skip the lower bound check.
const hasEnoughArguments = argCount >= signature.minArgumentCount;
return callIsIncomplete || hasEnoughArguments;
}
// If type has a single call signature and no other members, return that signature. Otherwise, return undefined.
function getSingleCallSignature(type: Type): Signature {
if (type.flags & TypeFlags.Object) {
const resolved = resolveStructuredTypeMembers(<ObjectType>type);
if (resolved.callSignatures.length === 1 && resolved.constructSignatures.length === 0 &&
resolved.properties.length === 0 && !resolved.stringIndexInfo && !resolved.numberIndexInfo) {
return resolved.callSignatures[0];
}
}
return undefined;
}
// Instantiate a generic signature in the context of a non-generic signature (section 3.8.5 in TypeScript spec)
function instantiateSignatureInContextOf(signature: Signature, contextualSignature: Signature, contextualMapper: TypeMapper): Signature {
const context = createInferenceContext(signature, /*inferUnionTypes*/ true, /*useAnyForNoInferences*/ false);
forEachMatchingParameterType(contextualSignature, signature, (source, target) => {
// Type parameters from outer context referenced by source type are fixed by instantiation of the source type
inferTypesWithContext(context, instantiateType(source, contextualMapper), target);
});
return getSignatureInstantiation(signature, getInferredTypes(context));
}
function inferTypeArguments(node: CallLikeExpression, signature: Signature, args: Expression[], excludeArgument: boolean[], context: InferenceContext): void {
const typeParameters = signature.typeParameters;
const inferenceMapper = getInferenceMapper(context);
// Clear out all the inference results from the last time inferTypeArguments was called on this context
for (let i = 0; i < typeParameters.length; i++) {
// As an optimization, we don't have to clear (and later recompute) inferred types
// for type parameters that have already been fixed on the previous call to inferTypeArguments.
// It would be just as correct to reset all of them. But then we'd be repeating the same work
// for the type parameters that were fixed, namely the work done by getInferredType.
if (!context.inferences[i].isFixed) {
context.inferredTypes[i] = undefined;
}
}
// On this call to inferTypeArguments, we may get more inferences for certain type parameters that were not
// fixed last time. This means that a type parameter that failed inference last time may succeed this time,
// or vice versa. Therefore, the failedTypeParameterIndex is useless if it points to an unfixed type parameter,
// because it may change. So here we reset it. However, getInferredType will not revisit any type parameters
// that were previously fixed. So if a fixed type parameter failed previously, it will fail again because
// it will contain the exact same set of inferences. So if we reset the index from a fixed type parameter,
// we will lose information that we won't recover this time around.
if (context.failedTypeParameterIndex !== undefined && !context.inferences[context.failedTypeParameterIndex].isFixed) {
context.failedTypeParameterIndex = undefined;
}
const thisType = getThisTypeOfSignature(signature);
if (thisType) {
const thisArgumentNode = getThisArgumentOfCall(node);
const thisArgumentType = thisArgumentNode ? checkExpression(thisArgumentNode) : voidType;
inferTypesWithContext(context, thisArgumentType, thisType);
}
// We perform two passes over the arguments. In the first pass we infer from all arguments, but use
// wildcards for all context sensitive function expressions.
const argCount = getEffectiveArgumentCount(node, args, signature);
for (let i = 0; i < argCount; i++) {
const arg = getEffectiveArgument(node, args, i);
// If the effective argument is 'undefined', then it is an argument that is present but is synthetic.
if (arg === undefined || arg.kind !== SyntaxKind.OmittedExpression) {
const paramType = getTypeAtPosition(signature, i);
let argType = getEffectiveArgumentType(node, i);
// If the effective argument type is 'undefined', there is no synthetic type
// for the argument. In that case, we should check the argument.
if (argType === undefined) {
// For context sensitive arguments we pass the identityMapper, which is a signal to treat all
// context sensitive function expressions as wildcards
const mapper = excludeArgument && excludeArgument[i] !== undefined ? identityMapper : inferenceMapper;
argType = checkExpressionWithContextualType(arg, paramType, mapper);
}
inferTypesWithContext(context, argType, paramType);
}
}
// In the second pass we visit only context sensitive arguments, and only those that aren't excluded, this
// time treating function expressions normally (which may cause previously inferred type arguments to be fixed
// as we construct types for contextually typed parameters)
// Decorators will not have `excludeArgument`, as their arguments cannot be contextually typed.
// Tagged template expressions will always have `undefined` for `excludeArgument[0]`.
if (excludeArgument) {
for (let i = 0; i < argCount; i++) {
// No need to check for omitted args and template expressions, their exclusion value is always undefined
if (excludeArgument[i] === false) {
const arg = args[i];
const paramType = getTypeAtPosition(signature, i);
inferTypesWithContext(context, checkExpressionWithContextualType(arg, paramType, inferenceMapper), paramType);
}
}
}
getInferredTypes(context);
}
function checkTypeArguments(signature: Signature, typeArgumentNodes: TypeNode[], typeArgumentTypes: Type[], reportErrors: boolean, headMessage?: DiagnosticMessage): boolean {
const typeParameters = signature.typeParameters;
let typeArgumentsAreAssignable = true;
let mapper: TypeMapper;
for (let i = 0; i < typeArgumentNodes.length; i++) {
if (typeArgumentsAreAssignable /* so far */) {
const constraint = getConstraintOfTypeParameter(typeParameters[i]);
if (constraint) {
let errorInfo: DiagnosticMessageChain;
let typeArgumentHeadMessage = Diagnostics.Type_0_does_not_satisfy_the_constraint_1;
if (reportErrors && headMessage) {
errorInfo = chainDiagnosticMessages(errorInfo, typeArgumentHeadMessage);
typeArgumentHeadMessage = headMessage;
}
if (!mapper) {
mapper = createTypeMapper(typeParameters, typeArgumentTypes);
}
const typeArgument = typeArgumentTypes[i];
typeArgumentsAreAssignable = checkTypeAssignableTo(
typeArgument,
getTypeWithThisArgument(instantiateType(constraint, mapper), typeArgument),
reportErrors ? typeArgumentNodes[i] : undefined,
typeArgumentHeadMessage,
errorInfo);
}
}
}
return typeArgumentsAreAssignable;
}
/**
* Check if the given signature can possibly be a signature called by the JSX opening-like element.
* @param node a JSX opening-like element we are trying to figure its call signature
* @param signature a candidate signature we are trying whether it is a call signature
* @param relation a relationship to check parameter and argument type
* @param excludeArgument
*/
function checkApplicableSignatureForJsxOpeningLikeElement(node: JsxOpeningLikeElement, signature: Signature, relation: Map<RelationComparisonResult>) {
// JSX opening-like element has correct arity for stateless-function component if the one of the following condition is true:
// 1. callIsIncomplete
// 2. attributes property has same number of properties as the parameter object type.
// We can figure that out by resolving attributes property and check number of properties in the resolved type
// If the call has correct arity, we will then check if the argument type and parameter type is assignable
const callIsIncomplete = node.attributes.end === node.end; // If we are missing the close "/>", the call is incomplete
if (callIsIncomplete) {
return true;
}
const headMessage = Diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1;
// Stateless function components can have maximum of three arguments: "props", "context", and "updater".
// However "context" and "updater" are implicit and can't be specify by users. Only the first parameter, props,
// can be specified by users through attributes property.
const paramType = getTypeAtPosition(signature, 0);
const attributesType = checkExpressionWithContextualType(node.attributes, paramType, /*contextualMapper*/ undefined);
const argProperties = getPropertiesOfType(attributesType);
for (const arg of argProperties) {
if (!getPropertyOfType(paramType, arg.name) && isUnhyphenatedJsxName(arg.name)) {
return false;
}
}
return checkTypeRelatedTo(attributesType, paramType, relation, /*errorNode*/ undefined, headMessage);
}
function checkApplicableSignature(node: CallLikeExpression, args: Expression[], signature: Signature, relation: Map<RelationComparisonResult>, excludeArgument: boolean[], reportErrors: boolean) {
if (isJsxOpeningLikeElement(node)) {
return checkApplicableSignatureForJsxOpeningLikeElement(<JsxOpeningLikeElement>node, signature, relation);
}
const thisType = getThisTypeOfSignature(signature);
if (thisType && thisType !== voidType && node.kind !== SyntaxKind.NewExpression) {
// If the called expression is not of the form `x.f` or `x["f"]`, then sourceType = voidType
// If the signature's 'this' type is voidType, then the check is skipped -- anything is compatible.
// If the expression is a new expression, then the check is skipped.
const thisArgumentNode = getThisArgumentOfCall(node);
const thisArgumentType = thisArgumentNode ? checkExpression(thisArgumentNode) : voidType;
const errorNode = reportErrors ? (thisArgumentNode || node) : undefined;
const headMessage = Diagnostics.The_this_context_of_type_0_is_not_assignable_to_method_s_this_of_type_1;
if (!checkTypeRelatedTo(thisArgumentType, getThisTypeOfSignature(signature), relation, errorNode, headMessage)) {
return false;
}
}
const headMessage = Diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1;
const argCount = getEffectiveArgumentCount(node, args, signature);
for (let i = 0; i < argCount; i++) {
const arg = getEffectiveArgument(node, args, i);
// If the effective argument is 'undefined', then it is an argument that is present but is synthetic.
if (arg === undefined || arg.kind !== SyntaxKind.OmittedExpression) {
// Check spread elements against rest type (from arity check we know spread argument corresponds to a rest parameter)
const paramType = getTypeAtPosition(signature, i);
let argType = getEffectiveArgumentType(node, i);
// If the effective argument type is 'undefined', there is no synthetic type
// for the argument. In that case, we should check the argument.
if (argType === undefined) {
argType = checkExpressionWithContextualType(arg, paramType, excludeArgument && excludeArgument[i] ? identityMapper : undefined);
}
// Use argument expression as error location when reporting errors
const errorNode = reportErrors ? getEffectiveArgumentErrorNode(node, i, arg) : undefined;
if (!checkTypeRelatedTo(argType, paramType, relation, errorNode, headMessage)) {
return false;
}
}
}
return true;
}
/**
* Returns the this argument in calls like x.f(...) and x[f](...). Undefined otherwise.
*/
function getThisArgumentOfCall(node: CallLikeExpression): LeftHandSideExpression {
if (node.kind === SyntaxKind.CallExpression) {
const callee = (<CallExpression>node).expression;
if (callee.kind === SyntaxKind.PropertyAccessExpression) {
return (callee as PropertyAccessExpression).expression;
}
else if (callee.kind === SyntaxKind.ElementAccessExpression) {
return (callee as ElementAccessExpression).expression;
}
}
}
/**
* Returns the effective arguments for an expression that works like a function invocation.
*
* If 'node' is a CallExpression or a NewExpression, then its argument list is returned.
* If 'node' is a TaggedTemplateExpression, a new argument list is constructed from the substitution
* expressions, where the first element of the list is `undefined`.
* If 'node' is a Decorator, the argument list will be `undefined`, and its arguments and types
* will be supplied from calls to `getEffectiveArgumentCount` and `getEffectiveArgumentType`.
*/
function getEffectiveCallArguments(node: CallLikeExpression): Expression[] {
let args: Expression[];
if (node.kind === SyntaxKind.TaggedTemplateExpression) {
const template = (<TaggedTemplateExpression>node).template;
args = [undefined];
if (template.kind === SyntaxKind.TemplateExpression) {
forEach((<TemplateExpression>template).templateSpans, span => {
args.push(span.expression);
});
}
}
else if (node.kind === SyntaxKind.Decorator) {
// For a decorator, we return undefined as we will determine
// the number and types of arguments for a decorator using
// `getEffectiveArgumentCount` and `getEffectiveArgumentType` below.
return undefined;
}
else if (isJsxOpeningLikeElement(node)) {
args = node.attributes.properties.length > 0 ? [node.attributes] : emptyArray;
}
else {
args = node.arguments || emptyArray;
}
return args;
}
/**
* Returns the effective argument count for a node that works like a function invocation.
* If 'node' is a Decorator, the number of arguments is derived from the decoration
* target and the signature:
* If 'node.target' is a class declaration or class expression, the effective argument
* count is 1.
* If 'node.target' is a parameter declaration, the effective argument count is 3.
* If 'node.target' is a property declaration, the effective argument count is 2.
* If 'node.target' is a method or accessor declaration, the effective argument count
* is 3, although it can be 2 if the signature only accepts two arguments, allowing
* us to match a property decorator.
* Otherwise, the argument count is the length of the 'args' array.
*/
function getEffectiveArgumentCount(node: CallLikeExpression, args: Expression[], signature: Signature) {
if (node.kind === SyntaxKind.Decorator) {
switch (node.parent.kind) {
case SyntaxKind.ClassDeclaration:
case SyntaxKind.ClassExpression:
// A class decorator will have one argument (see `ClassDecorator` in core.d.ts)
return 1;
case SyntaxKind.PropertyDeclaration:
// A property declaration decorator will have two arguments (see
// `PropertyDecorator` in core.d.ts)
return 2;
case SyntaxKind.MethodDeclaration:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
// A method or accessor declaration decorator will have two or three arguments (see
// `PropertyDecorator` and `MethodDecorator` in core.d.ts)
// If we are emitting decorators for ES3, we will only pass two arguments.
if (languageVersion === ScriptTarget.ES3) {
return 2;
}
// If the method decorator signature only accepts a target and a key, we will only
// type check those arguments.
return signature.parameters.length >= 3 ? 3 : 2;
case SyntaxKind.Parameter:
// A parameter declaration decorator will have three arguments (see
// `ParameterDecorator` in core.d.ts)
return 3;
}
}
else {
return args.length;
}
}
/**
* Returns the effective type of the first argument to a decorator.
* If 'node' is a class declaration or class expression, the effective argument type
* is the type of the static side of the class.
* If 'node' is a parameter declaration, the effective argument type is either the type
* of the static or instance side of the class for the parameter's parent method,
* depending on whether the method is declared static.
* For a constructor, the type is always the type of the static side of the class.
* If 'node' is a property, method, or accessor declaration, the effective argument
* type is the type of the static or instance side of the parent class for class
* element, depending on whether the element is declared static.
*/
function getEffectiveDecoratorFirstArgumentType(node: Node): Type {
// The first argument to a decorator is its `target`.
if (node.kind === SyntaxKind.ClassDeclaration) {
// For a class decorator, the `target` is the type of the class (e.g. the
// "static" or "constructor" side of the class)
const classSymbol = getSymbolOfNode(node);
return getTypeOfSymbol(classSymbol);
}
if (node.kind === SyntaxKind.Parameter) {
// For a parameter decorator, the `target` is the parent type of the
// parameter's containing method.
node = node.parent;
if (node.kind === SyntaxKind.Constructor) {
const classSymbol = getSymbolOfNode(node);
return getTypeOfSymbol(classSymbol);
}
}
if (node.kind === SyntaxKind.PropertyDeclaration ||
node.kind === SyntaxKind.MethodDeclaration ||
node.kind === SyntaxKind.GetAccessor ||
node.kind === SyntaxKind.SetAccessor) {
// For a property or method decorator, the `target` is the
// "static"-side type of the parent of the member if the member is
// declared "static"; otherwise, it is the "instance"-side type of the
// parent of the member.
return getParentTypeOfClassElement(<ClassElement>node);
}
Debug.fail("Unsupported decorator target.");
return unknownType;
}
/**
* Returns the effective type for the second argument to a decorator.
* If 'node' is a parameter, its effective argument type is one of the following:
* If 'node.parent' is a constructor, the effective argument type is 'any', as we
* will emit `undefined`.
* If 'node.parent' is a member with an identifier, numeric, or string literal name,
* the effective argument type will be a string literal type for the member name.
* If 'node.parent' is a computed property name, the effective argument type will
* either be a symbol type or the string type.
* If 'node' is a member with an identifier, numeric, or string literal name, the
* effective argument type will be a string literal type for the member name.
* If 'node' is a computed property name, the effective argument type will either
* be a symbol type or the string type.
* A class decorator does not have a second argument type.
*/
function getEffectiveDecoratorSecondArgumentType(node: Node) {
// The second argument to a decorator is its `propertyKey`
if (node.kind === SyntaxKind.ClassDeclaration) {
Debug.fail("Class decorators should not have a second synthetic argument.");
return unknownType;
}
if (node.kind === SyntaxKind.Parameter) {
node = node.parent;
if (node.kind === SyntaxKind.Constructor) {
// For a constructor parameter decorator, the `propertyKey` will be `undefined`.
return anyType;
}
// For a non-constructor parameter decorator, the `propertyKey` will be either
// a string or a symbol, based on the name of the parameter's containing method.
}
if (node.kind === SyntaxKind.PropertyDeclaration ||
node.kind === SyntaxKind.MethodDeclaration ||
node.kind === SyntaxKind.GetAccessor ||
node.kind === SyntaxKind.SetAccessor) {
// The `propertyKey` for a property or method decorator will be a
// string literal type if the member name is an identifier, number, or string;
// otherwise, if the member name is a computed property name it will
// be either string or symbol.
const element = <ClassElement>node;
switch (element.name.kind) {
case SyntaxKind.Identifier:
case SyntaxKind.NumericLiteral:
case SyntaxKind.StringLiteral:
return getLiteralType((<Identifier | LiteralExpression>element.name).text);
case SyntaxKind.ComputedPropertyName:
const nameType = checkComputedPropertyName(<ComputedPropertyName>element.name);
if (isTypeOfKind(nameType, TypeFlags.ESSymbol)) {
return nameType;
}
else {
return stringType;
}
default:
Debug.fail("Unsupported property name.");
return unknownType;
}
}
Debug.fail("Unsupported decorator target.");
return unknownType;
}
/**
* Returns the effective argument type for the third argument to a decorator.
* If 'node' is a parameter, the effective argument type is the number type.
* If 'node' is a method or accessor, the effective argument type is a
* `TypedPropertyDescriptor<T>` instantiated with the type of the member.
* Class and property decorators do not have a third effective argument.
*/
function getEffectiveDecoratorThirdArgumentType(node: Node) {
// The third argument to a decorator is either its `descriptor` for a method decorator
// or its `parameterIndex` for a parameter decorator
if (node.kind === SyntaxKind.ClassDeclaration) {
Debug.fail("Class decorators should not have a third synthetic argument.");
return unknownType;
}
if (node.kind === SyntaxKind.Parameter) {
// The `parameterIndex` for a parameter decorator is always a number
return numberType;
}
if (node.kind === SyntaxKind.PropertyDeclaration) {
Debug.fail("Property decorators should not have a third synthetic argument.");
return unknownType;
}
if (node.kind === SyntaxKind.MethodDeclaration ||
node.kind === SyntaxKind.GetAccessor ||
node.kind === SyntaxKind.SetAccessor) {
// The `descriptor` for a method decorator will be a `TypedPropertyDescriptor<T>`
// for the type of the member.
const propertyType = getTypeOfNode(node);
return createTypedPropertyDescriptorType(propertyType);
}
Debug.fail("Unsupported decorator target.");
return unknownType;
}
/**
* Returns the effective argument type for the provided argument to a decorator.
*/
function getEffectiveDecoratorArgumentType(node: Decorator, argIndex: number): Type {
if (argIndex === 0) {
return getEffectiveDecoratorFirstArgumentType(node.parent);
}
else if (argIndex === 1) {
return getEffectiveDecoratorSecondArgumentType(node.parent);
}
else if (argIndex === 2) {
return getEffectiveDecoratorThirdArgumentType(node.parent);
}
Debug.fail("Decorators should not have a fourth synthetic argument.");
return unknownType;
}
/**
* Gets the effective argument type for an argument in a call expression.
*/
function getEffectiveArgumentType(node: CallLikeExpression, argIndex: number): Type {
// Decorators provide special arguments, a tagged template expression provides
// a special first argument, and string literals get string literal types
// unless we're reporting errors
if (node.kind === SyntaxKind.Decorator) {
return getEffectiveDecoratorArgumentType(<Decorator>node, argIndex);
}
else if (argIndex === 0 && node.kind === SyntaxKind.TaggedTemplateExpression) {
return getGlobalTemplateStringsArrayType();
}
// This is not a synthetic argument, so we return 'undefined'
// to signal that the caller needs to check the argument.
return undefined;
}
/**
* Gets the effective argument expression for an argument in a call expression.
*/
function getEffectiveArgument(node: CallLikeExpression, args: Expression[], argIndex: number) {
// For a decorator or the first argument of a tagged template expression we return undefined.
if (node.kind === SyntaxKind.Decorator ||
(argIndex === 0 && node.kind === SyntaxKind.TaggedTemplateExpression)) {
return undefined;
}
return args[argIndex];
}
/**
* Gets the error node to use when reporting errors for an effective argument.
*/
function getEffectiveArgumentErrorNode(node: CallLikeExpression, argIndex: number, arg: Expression) {
if (node.kind === SyntaxKind.Decorator) {
// For a decorator, we use the expression of the decorator for error reporting.
return (<Decorator>node).expression;
}
else if (argIndex === 0 && node.kind === SyntaxKind.TaggedTemplateExpression) {
// For a the first argument of a tagged template expression, we use the template of the tag for error reporting.
return (<TaggedTemplateExpression>node).template;
}
else {
return arg;
}
}
function resolveCall(node: CallLikeExpression, signatures: Signature[], candidatesOutArray: Signature[], fallbackError?: DiagnosticMessage): Signature {
const isTaggedTemplate = node.kind === SyntaxKind.TaggedTemplateExpression;
const isDecorator = node.kind === SyntaxKind.Decorator;
const isJsxOpeningOrSelfClosingElement = isJsxOpeningLikeElement(node);
let typeArguments: TypeNode[];
if (!isTaggedTemplate && !isDecorator && !isJsxOpeningOrSelfClosingElement) {
typeArguments = (<CallExpression>node).typeArguments;
// We already perform checking on the type arguments on the class declaration itself.
if ((<CallExpression>node).expression.kind !== SyntaxKind.SuperKeyword) {
forEach(typeArguments, checkSourceElement);
}
}
if (signatures.length === 1) {
const declaration = signatures[0].declaration;
if (declaration && isInJavaScriptFile(declaration) && !hasJSDocParameterTags(declaration)) {
if (containsArgumentsReference(<FunctionLikeDeclaration>declaration)) {
const signatureWithRest = cloneSignature(signatures[0]);
const syntheticArgsSymbol = createSymbol(SymbolFlags.Variable, "args");
syntheticArgsSymbol.type = anyArrayType;
syntheticArgsSymbol.isRestParameter = true;
signatureWithRest.parameters = concatenate(signatureWithRest.parameters, [syntheticArgsSymbol]);
signatureWithRest.hasRestParameter = true;
signatures = [signatureWithRest];
}
}
}
const candidates = candidatesOutArray || [];
// reorderCandidates fills up the candidates array directly
reorderCandidates(signatures, candidates);
if (!candidates.length) {
diagnostics.add(createDiagnosticForNode(node, Diagnostics.Call_target_does_not_contain_any_signatures));
return resolveErrorCall(node);
}
const args = getEffectiveCallArguments(node);
// The following applies to any value of 'excludeArgument[i]':
// - true: the argument at 'i' is susceptible to a one-time permanent contextual typing.
// - undefined: the argument at 'i' is *not* susceptible to permanent contextual typing.
// - false: the argument at 'i' *was* and *has been* permanently contextually typed.
//
// The idea is that we will perform type argument inference & assignability checking once
// without using the susceptible parameters that are functions, and once more for each of those
// parameters, contextually typing each as we go along.
//
// For a tagged template, then the first argument be 'undefined' if necessary
// because it represents a TemplateStringsArray.
//
// For a decorator, no arguments are susceptible to contextual typing due to the fact
// decorators are applied to a declaration by the emitter, and not to an expression.
let excludeArgument: boolean[];
if (!isDecorator) {
// We do not need to call `getEffectiveArgumentCount` here as it only
// applies when calculating the number of arguments for a decorator.
for (let i = isTaggedTemplate ? 1 : 0; i < args.length; i++) {
if (isContextSensitive(args[i])) {
if (!excludeArgument) {
excludeArgument = new Array(args.length);
}
excludeArgument[i] = true;
}
}
}
// The following variables are captured and modified by calls to chooseOverload.
// If overload resolution or type argument inference fails, we want to report the
// best error possible. The best error is one which says that an argument was not
// assignable to a parameter. This implies that everything else about the overload
// was fine. So if there is any overload that is only incorrect because of an
// argument, we will report an error on that one.
//
// function foo(s: string) {}
// function foo(n: number) {} // Report argument error on this overload
// function foo() {}
// foo(true);
//
// If none of the overloads even made it that far, there are two possibilities.
// There was a problem with type arguments for some overload, in which case
// report an error on that. Or none of the overloads even had correct arity,
// in which case give an arity error.
//
// function foo<T>(x: T, y: T) {} // Report type argument inference error
// function foo() {}
// foo(0, true);
//
let candidateForArgumentError: Signature;
let candidateForTypeArgumentError: Signature;
let resultOfFailedInference: InferenceContext;
let result: Signature;
// If we are in signature help, a trailing comma indicates that we intend to provide another argument,
// so we will only accept overloads with arity at least 1 higher than the current number of provided arguments.
const signatureHelpTrailingComma =
candidatesOutArray && node.kind === SyntaxKind.CallExpression && (<CallExpression>node).arguments.hasTrailingComma;
// Section 4.12.1:
// if the candidate list contains one or more signatures for which the type of each argument
// expression is a subtype of each corresponding parameter type, the return type of the first
// of those signatures becomes the return type of the function call.
// Otherwise, the return type of the first signature in the candidate list becomes the return
// type of the function call.
//
// Whether the call is an error is determined by assignability of the arguments. The subtype pass
// is just important for choosing the best signature. So in the case where there is only one
// signature, the subtype pass is useless. So skipping it is an optimization.
if (candidates.length > 1) {
result = chooseOverload(candidates, subtypeRelation, signatureHelpTrailingComma);
}
if (!result) {
// Reinitialize these pointers for round two
candidateForArgumentError = undefined;
candidateForTypeArgumentError = undefined;
resultOfFailedInference = undefined;
result = chooseOverload(candidates, assignableRelation, signatureHelpTrailingComma);
}
if (result) {
return result;
}
// No signatures were applicable. Now report errors based on the last applicable signature with
// no arguments excluded from assignability checks.
// If candidate is undefined, it means that no candidates had a suitable arity. In that case,
// skip the checkApplicableSignature check.
if (candidateForArgumentError) {
if (isJsxOpeningOrSelfClosingElement) {
// We do not report any error here because any error will be handled in "resolveCustomJsxElementAttributesType".
return candidateForArgumentError;
}
// excludeArgument is undefined, in this case also equivalent to [undefined, undefined, ...]
// The importance of excludeArgument is to prevent us from typing function expression parameters
// in arguments too early. If possible, we'd like to only type them once we know the correct
// overload. However, this matters for the case where the call is correct. When the call is
// an error, we don't need to exclude any arguments, although it would cause no harm to do so.
checkApplicableSignature(node, args, candidateForArgumentError, assignableRelation, /*excludeArgument*/ undefined, /*reportErrors*/ true);
}
else if (candidateForTypeArgumentError) {
if (!isTaggedTemplate && !isDecorator && typeArguments) {
const typeArguments = (<CallExpression>node).typeArguments;
checkTypeArguments(candidateForTypeArgumentError, typeArguments, map(typeArguments, getTypeFromTypeNode), /*reportErrors*/ true, fallbackError);
}
else {
Debug.assert(resultOfFailedInference.failedTypeParameterIndex >= 0);
const failedTypeParameter = candidateForTypeArgumentError.typeParameters[resultOfFailedInference.failedTypeParameterIndex];
const inferenceCandidates = getInferenceCandidates(resultOfFailedInference, resultOfFailedInference.failedTypeParameterIndex);
let diagnosticChainHead = chainDiagnosticMessages(/*details*/ undefined, // details will be provided by call to reportNoCommonSupertypeError
Diagnostics.The_type_argument_for_type_parameter_0_cannot_be_inferred_from_the_usage_Consider_specifying_the_type_arguments_explicitly,
typeToString(failedTypeParameter));
if (fallbackError) {
diagnosticChainHead = chainDiagnosticMessages(diagnosticChainHead, fallbackError);
}
reportNoCommonSupertypeError(inferenceCandidates, (<JsxOpeningLikeElement>node).tagName || (<CallExpression>node).expression || (<TaggedTemplateExpression>node).tag, diagnosticChainHead);
}
}
else if (typeArguments && every(signatures, sig => length(sig.typeParameters) !== typeArguments.length)) {
let min = Number.POSITIVE_INFINITY;
let max = Number.NEGATIVE_INFINITY;
for (const sig of signatures) {
min = Math.min(min, getMinTypeArgumentCount(sig.typeParameters));
max = Math.max(max, length(sig.typeParameters));
}
const paramCount = min < max ? `${min}-${max}` : min;
diagnostics.add(createDiagnosticForNode(node, Diagnostics.Expected_0_type_arguments_but_got_1, paramCount, typeArguments.length));
}
else if (args) {
let min = Number.POSITIVE_INFINITY;
let max = Number.NEGATIVE_INFINITY;
for (const sig of signatures) {
min = Math.min(min, sig.minArgumentCount);
max = Math.max(max, sig.parameters.length);
}
const hasRestParameter = some(signatures, sig => sig.hasRestParameter);
const hasSpreadArgument = getSpreadArgumentIndex(args) > -1;
const paramCount = hasRestParameter ? min :
min < max ? `${min}-${max}` :
min;
const argCount = args.length - (hasSpreadArgument ? 1 : 0);
const error = hasRestParameter && hasSpreadArgument ? Diagnostics.Expected_at_least_0_arguments_but_got_a_minimum_of_1 :
hasRestParameter ? Diagnostics.Expected_at_least_0_arguments_but_got_1 :
hasSpreadArgument ? Diagnostics.Expected_0_arguments_but_got_a_minimum_of_1 :
Diagnostics.Expected_0_arguments_but_got_1;
diagnostics.add(createDiagnosticForNode(node, error, paramCount, argCount));
}
else if (fallbackError) {
diagnostics.add(createDiagnosticForNode(node, fallbackError));
}
// No signature was applicable. We have already reported the errors for the invalid signature.
// If this is a type resolution session, e.g. Language Service, try to get better information that anySignature.
// Pick the first candidate that matches the arity. This way we can get a contextual type for cases like:
// declare function f(a: { xa: number; xb: number; });
// f({ |
if (!produceDiagnostics) {
for (let candidate of candidates) {
if (hasCorrectArity(node, args, candidate)) {
if (candidate.typeParameters && typeArguments) {
candidate = getSignatureInstantiation(candidate, map(typeArguments, getTypeFromTypeNode));
}
return candidate;
}
}
}
return resolveErrorCall(node);
function chooseOverload(candidates: Signature[], relation: Map<RelationComparisonResult>, signatureHelpTrailingComma = false) {
for (const originalCandidate of candidates) {
if (!hasCorrectArity(node, args, originalCandidate, signatureHelpTrailingComma)) {
continue;
}
let candidate: Signature;
let typeArgumentsAreValid: boolean;
const inferenceContext = originalCandidate.typeParameters
? createInferenceContext(originalCandidate, /*inferUnionTypes*/ false, /*useAnyForNoInferences*/ isInJavaScriptFile(node))
: undefined;
while (true) {
candidate = originalCandidate;
if (candidate.typeParameters) {
let typeArgumentTypes: Type[] | undefined;
if (typeArguments) {
typeArgumentTypes = fillMissingTypeArguments(map(typeArguments, getTypeFromTypeNode), candidate.typeParameters, getMinTypeArgumentCount(candidate.typeParameters));
typeArgumentsAreValid = checkTypeArguments(candidate, typeArguments, typeArgumentTypes, /*reportErrors*/ false);
}
else {
inferTypeArguments(node, candidate, args, excludeArgument, inferenceContext);
typeArgumentTypes = inferenceContext.inferredTypes;
typeArgumentsAreValid = inferenceContext.failedTypeParameterIndex === undefined;
}
if (!typeArgumentsAreValid) {
break;
}
candidate = getSignatureInstantiation(candidate, typeArgumentTypes);
}
if (!checkApplicableSignature(node, args, candidate, relation, excludeArgument, /*reportErrors*/ false)) {
break;
}
const index = excludeArgument ? indexOf(excludeArgument, /*value*/ true) : -1;
if (index < 0) {
return candidate;
}
excludeArgument[index] = false;
}
// A post-mortem of this iteration of the loop. The signature was not applicable,
// so we want to track it as a candidate for reporting an error. If the candidate
// had no type parameters, or had no issues related to type arguments, we can
// report an error based on the arguments. If there was an issue with type
// arguments, then we can only report an error based on the type arguments.
if (originalCandidate.typeParameters) {
const instantiatedCandidate = candidate;
if (typeArgumentsAreValid) {
candidateForArgumentError = instantiatedCandidate;
}
else {
candidateForTypeArgumentError = originalCandidate;
if (!typeArguments) {
resultOfFailedInference = inferenceContext;
}
}
}
else {
Debug.assert(originalCandidate === candidate);
candidateForArgumentError = originalCandidate;
}
}
return undefined;
}
}
function resolveCallExpression(node: CallExpression, candidatesOutArray: Signature[]): Signature {
if (node.expression.kind === SyntaxKind.SuperKeyword) {
const superType = checkSuperExpression(node.expression);
if (superType !== unknownType) {
// In super call, the candidate signatures are the matching arity signatures of the base constructor function instantiated
// with the type arguments specified in the extends clause.
const baseTypeNode = getClassExtendsHeritageClauseElement(getContainingClass(node));
if (baseTypeNode) {
const baseConstructors = getInstantiatedConstructorsForTypeArguments(superType, baseTypeNode.typeArguments, baseTypeNode);
return resolveCall(node, baseConstructors, candidatesOutArray);
}
}
return resolveUntypedCall(node);
}
const funcType = checkNonNullExpression(node.expression);
if (funcType === silentNeverType) {
return silentNeverSignature;
}
const apparentType = getApparentType(funcType);
if (apparentType === unknownType) {
// Another error has already been reported
return resolveErrorCall(node);
}
// Technically, this signatures list may be incomplete. We are taking the apparent type,
// but we are not including call signatures that may have been added to the Object or
// Function interface, since they have none by default. This is a bit of a leap of faith
// that the user will not add any.
const callSignatures = getSignaturesOfType(apparentType, SignatureKind.Call);
const constructSignatures = getSignaturesOfType(apparentType, SignatureKind.Construct);
// TS 1.0 Spec: 4.12
// 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.
if (isUntypedFunctionCall(funcType, apparentType, callSignatures.length, constructSignatures.length)) {
// The unknownType indicates that an error already occurred (and was reported). No
// need to report another error in this case.
if (funcType !== unknownType && node.typeArguments) {
error(node, Diagnostics.Untyped_function_calls_may_not_accept_type_arguments);
}
return resolveUntypedCall(node);
}
// If FuncExpr's apparent type(section 3.8.1) is a function type, the call is a typed function call.
// TypeScript employs overload resolution in typed function calls in order to support functions
// with multiple call signatures.
if (!callSignatures.length) {
if (constructSignatures.length) {
error(node, Diagnostics.Value_of_type_0_is_not_callable_Did_you_mean_to_include_new, typeToString(funcType));
}
else {
error(node, Diagnostics.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures, typeToString(apparentType));
}
return resolveErrorCall(node);
}
return resolveCall(node, callSignatures, candidatesOutArray);
}
/**
* TS 1.0 spec: 4.12
* If FuncExpr is of type Any, or of an object type that has no call or construct signatures
* but is a subtype of the Function interface, the call is an untyped function call.
*/
function isUntypedFunctionCall(funcType: Type, apparentFuncType: Type, numCallSignatures: number, numConstructSignatures: number) {
if (isTypeAny(funcType)) {
return true;
}
if (isTypeAny(apparentFuncType) && funcType.flags & TypeFlags.TypeParameter) {
return true;
}
if (!numCallSignatures && !numConstructSignatures) {
// We exclude union types because we may have a union of function types that happen to have
// no common signatures.
if (funcType.flags & TypeFlags.Union) {
return false;
}
return isTypeAssignableTo(funcType, globalFunctionType);
}
return false;
}
function resolveNewExpression(node: NewExpression, candidatesOutArray: Signature[]): Signature {
if (node.arguments && languageVersion < ScriptTarget.ES5) {
const spreadIndex = getSpreadArgumentIndex(node.arguments);
if (spreadIndex >= 0) {
error(node.arguments[spreadIndex], Diagnostics.Spread_operator_in_new_expressions_is_only_available_when_targeting_ECMAScript_5_and_higher);
}
}
let expressionType = checkNonNullExpression(node.expression);
if (expressionType === silentNeverType) {
return silentNeverSignature;
}
// If expressionType's apparent type(section 3.8.1) is an object type with one or
// more construct signatures, the expression is processed in the same manner as a
// function call, but using the construct signatures as the initial set of candidate
// signatures for overload resolution. The result type of the function call becomes
// the result type of the operation.
expressionType = getApparentType(expressionType);
if (expressionType === unknownType) {
// Another error has already been reported
return resolveErrorCall(node);
}
// If the expression is a class of abstract type, then it cannot be instantiated.
// Note, only class declarations can be declared abstract.
// In the case of a merged class-module or class-interface declaration,
// only the class declaration node will have the Abstract flag set.
const valueDecl = expressionType.symbol && getClassLikeDeclarationOfSymbol(expressionType.symbol);
if (valueDecl && getModifierFlags(valueDecl) & ModifierFlags.Abstract) {
error(node, Diagnostics.Cannot_create_an_instance_of_the_abstract_class_0, declarationNameToString(getNameOfDeclaration(valueDecl)));
return resolveErrorCall(node);
}
// TS 1.0 spec: 4.11
// If expressionType is of type Any, Args can be any argument
// list and the result of the operation is of type Any.
if (isTypeAny(expressionType)) {
if (node.typeArguments) {
error(node, Diagnostics.Untyped_function_calls_may_not_accept_type_arguments);
}
return resolveUntypedCall(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.
const constructSignatures = getSignaturesOfType(expressionType, SignatureKind.Construct);
if (constructSignatures.length) {
if (!isConstructorAccessible(node, constructSignatures[0])) {
return resolveErrorCall(node);
}
return resolveCall(node, constructSignatures, candidatesOutArray);
}
// If expressionType'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. It is an error to have a Void this type.
const callSignatures = getSignaturesOfType(expressionType, SignatureKind.Call);
if (callSignatures.length) {
const signature = resolveCall(node, callSignatures, candidatesOutArray);
if (getReturnTypeOfSignature(signature) !== voidType) {
error(node, Diagnostics.Only_a_void_function_can_be_called_with_the_new_keyword);
}
if (getThisTypeOfSignature(signature) === voidType) {
error(node, Diagnostics.A_function_that_is_called_with_the_new_keyword_cannot_have_a_this_type_that_is_void);
}
return signature;
}
error(node, Diagnostics.Cannot_use_new_with_an_expression_whose_type_lacks_a_call_or_construct_signature);
return resolveErrorCall(node);
}
function isConstructorAccessible(node: NewExpression, signature: Signature) {
if (!signature || !signature.declaration) {
return true;
}
const declaration = signature.declaration;
const modifiers = getModifierFlags(declaration);
// Public constructor is accessible.
if (!(modifiers & ModifierFlags.NonPublicAccessibilityModifier)) {
return true;
}
const declaringClassDeclaration = <ClassLikeDeclaration>getClassLikeDeclarationOfSymbol(declaration.parent.symbol);
const declaringClass = <InterfaceType>getDeclaredTypeOfSymbol(declaration.parent.symbol);
// A private or protected constructor can only be instantiated within its own class (or a subclass, for protected)
if (!isNodeWithinClass(node, declaringClassDeclaration)) {
const containingClass = getContainingClass(node);
if (containingClass) {
const containingType = getTypeOfNode(containingClass);
let baseTypes = getBaseTypes(containingType as InterfaceType);
while (baseTypes.length) {
const baseType = baseTypes[0];
if (modifiers & ModifierFlags.Protected &&
baseType.symbol === declaration.parent.symbol) {
return true;
}
baseTypes = getBaseTypes(baseType as InterfaceType);
}
}
if (modifiers & ModifierFlags.Private) {
error(node, Diagnostics.Constructor_of_class_0_is_private_and_only_accessible_within_the_class_declaration, typeToString(declaringClass));
}
if (modifiers & ModifierFlags.Protected) {
error(node, Diagnostics.Constructor_of_class_0_is_protected_and_only_accessible_within_the_class_declaration, typeToString(declaringClass));
}
return false;
}
return true;
}
function resolveTaggedTemplateExpression(node: TaggedTemplateExpression, candidatesOutArray: Signature[]): Signature {
const tagType = checkExpression(node.tag);
const apparentType = getApparentType(tagType);
if (apparentType === unknownType) {
// Another error has already been reported
return resolveErrorCall(node);
}
const callSignatures = getSignaturesOfType(apparentType, SignatureKind.Call);
const constructSignatures = getSignaturesOfType(apparentType, SignatureKind.Construct);
if (isUntypedFunctionCall(tagType, apparentType, callSignatures.length, constructSignatures.length)) {
return resolveUntypedCall(node);
}
if (!callSignatures.length) {
error(node, Diagnostics.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures, typeToString(apparentType));
return resolveErrorCall(node);
}
return resolveCall(node, callSignatures, candidatesOutArray);
}
/**
* Gets the localized diagnostic head message to use for errors when resolving a decorator as a call expression.
*/
function getDiagnosticHeadMessageForDecoratorResolution(node: Decorator) {
switch (node.parent.kind) {
case SyntaxKind.ClassDeclaration:
case SyntaxKind.ClassExpression:
return Diagnostics.Unable_to_resolve_signature_of_class_decorator_when_called_as_an_expression;
case SyntaxKind.Parameter:
return Diagnostics.Unable_to_resolve_signature_of_parameter_decorator_when_called_as_an_expression;
case SyntaxKind.PropertyDeclaration:
return Diagnostics.Unable_to_resolve_signature_of_property_decorator_when_called_as_an_expression;
case SyntaxKind.MethodDeclaration:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
return Diagnostics.Unable_to_resolve_signature_of_method_decorator_when_called_as_an_expression;
}
}
/**
* Resolves a decorator as if it were a call expression.
*/
function resolveDecorator(node: Decorator, candidatesOutArray: Signature[]): Signature {
const funcType = checkExpression(node.expression);
const apparentType = getApparentType(funcType);
if (apparentType === unknownType) {
return resolveErrorCall(node);
}
const callSignatures = getSignaturesOfType(apparentType, SignatureKind.Call);
const constructSignatures = getSignaturesOfType(apparentType, SignatureKind.Construct);
if (isUntypedFunctionCall(funcType, apparentType, callSignatures.length, constructSignatures.length)) {
return resolveUntypedCall(node);
}
const headMessage = getDiagnosticHeadMessageForDecoratorResolution(node);
if (!callSignatures.length) {
let errorInfo: DiagnosticMessageChain;
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures, typeToString(apparentType));
errorInfo = chainDiagnosticMessages(errorInfo, headMessage);
diagnostics.add(createDiagnosticForNodeFromMessageChain(node, errorInfo));
return resolveErrorCall(node);
}
return resolveCall(node, callSignatures, candidatesOutArray, headMessage);
}
/**
* This function is similar to getResolvedSignature but is exclusively for trying to resolve JSX stateless-function component.
* The main reason we have to use this function instead of getResolvedSignature because, the caller of this function will already check the type of openingLikeElement's tagName
* and pass the type as elementType. The elementType can not be a union (as such case should be handled by the caller of this function)
* Note: at this point, we are still not sure whether the opening-like element is a stateless function component or not.
* @param openingLikeElement an opening-like JSX element to try to resolve as JSX stateless function
* @param elementType an element type of the opneing-like element by checking opening-like element's tagname.
* @param candidatesOutArray an array of signature to be filled in by the function. It is passed by signature help in the language service;
* the function will fill it up with appropriate candidate signatures
*/
function getResolvedJsxStatelessFunctionSignature(openingLikeElement: JsxOpeningLikeElement, elementType: Type, candidatesOutArray: Signature[]): Signature {
Debug.assert(!(elementType.flags & TypeFlags.Union));
const callSignature = resolveStatelessJsxOpeningLikeElement(openingLikeElement, elementType, candidatesOutArray);
return callSignature;
}
/**
* Try treating a given opening-like element as stateless function component and resolve a tagName to a function signature.
* @param openingLikeElement an JSX opening-like element we want to try resolve its stateless function if possible
* @param elementType a type of the opening-like JSX element, a result of resolving tagName in opening-like element.
* @param candidatesOutArray an array of signature to be filled in by the function. It is passed by signature help in the language service;
* the function will fill it up with appropriate candidate signatures
* @return a resolved signature if we can find function matching function signature through resolve call or a first signature in the list of functions.
* otherwise return undefined if tag-name of the opening-like element doesn't have call signatures
*/
function resolveStatelessJsxOpeningLikeElement(openingLikeElement: JsxOpeningLikeElement, elementType: Type, candidatesOutArray: Signature[]): Signature {
// If this function is called from language service, elementType can be a union type. This is not possible if the function is called from compiler (see: resolveCustomJsxElementAttributesType)
if (elementType.flags & TypeFlags.Union) {
const types = (elementType as UnionType).types;
let result: Signature;
for (const type of types) {
result = result || resolveStatelessJsxOpeningLikeElement(openingLikeElement, type, candidatesOutArray);
}
return result;
}
const callSignatures = elementType && getSignaturesOfType(elementType, SignatureKind.Call);
if (callSignatures && callSignatures.length > 0) {
let callSignature: Signature;
callSignature = resolveCall(openingLikeElement, callSignatures, candidatesOutArray);
return callSignature;
}
return undefined;
}
function resolveSignature(node: CallLikeExpression, candidatesOutArray?: Signature[]): Signature {
switch (node.kind) {
case SyntaxKind.CallExpression:
return resolveCallExpression(<CallExpression>node, candidatesOutArray);
case SyntaxKind.NewExpression:
return resolveNewExpression(<NewExpression>node, candidatesOutArray);
case SyntaxKind.TaggedTemplateExpression:
return resolveTaggedTemplateExpression(<TaggedTemplateExpression>node, candidatesOutArray);
case SyntaxKind.Decorator:
return resolveDecorator(<Decorator>node, candidatesOutArray);
case SyntaxKind.JsxOpeningElement:
case SyntaxKind.JsxSelfClosingElement:
// This code-path is called by language service
return resolveStatelessJsxOpeningLikeElement(<JsxOpeningLikeElement>node, checkExpression((<JsxOpeningLikeElement>node).tagName), candidatesOutArray);
}
Debug.fail("Branch in 'resolveSignature' should be unreachable.");
}
/**
* Resolve a signature of a given call-like expression.
* @param node a call-like expression to try resolve a signature for
* @param candidatesOutArray an array of signature to be filled in by the function. It is passed by signature help in the language service;
* the function will fill it up with appropriate candidate signatures
* @return a signature of the call-like expression or undefined if one can't be found
*/
function getResolvedSignature(node: CallLikeExpression, candidatesOutArray?: Signature[]): Signature {
const links = getNodeLinks(node);
// If getResolvedSignature has already been called, we will have cached the resolvedSignature.
// However, it is possible that either candidatesOutArray was not passed in the first time,
// or that a different candidatesOutArray was passed in. Therefore, we need to redo the work
// to correctly fill the candidatesOutArray.
const cached = links.resolvedSignature;
if (cached && cached !== resolvingSignature && !candidatesOutArray) {
return cached;
}
links.resolvedSignature = resolvingSignature;
const result = resolveSignature(node, candidatesOutArray);
// If signature resolution originated in control flow type analysis (for example to compute the
// assigned type in a flow assignment) we don't cache the result as it may be based on temporary
// types from the control flow analysis.
links.resolvedSignature = flowLoopStart === flowLoopCount ? result : cached;
return result;
}
function getResolvedOrAnySignature(node: CallLikeExpression) {
// If we're already in the process of resolving the given signature, don't resolve again as
// that could cause infinite recursion. Instead, return anySignature.
return getNodeLinks(node).resolvedSignature === resolvingSignature ? resolvingSignature : getResolvedSignature(node);
}
function getInferredClassType(symbol: Symbol) {
const links = getSymbolLinks(symbol);
if (!links.inferredClassType) {
links.inferredClassType = createAnonymousType(symbol, symbol.members, emptyArray, emptyArray, /*stringIndexType*/ undefined, /*numberIndexType*/ undefined);
}
return links.inferredClassType;
}
/**
* Syntactically and semantically checks a call or new expression.
* @param node The call/new expression to be checked.
* @returns On success, the expression's signature's return type. On failure, anyType.
*/
function checkCallExpression(node: CallExpression | NewExpression): Type {
// Grammar checking; stop grammar-checking if checkGrammarTypeArguments return true
checkGrammarTypeArguments(node, node.typeArguments) || checkGrammarArguments(node, node.arguments);
const signature = getResolvedSignature(node);
if (node.expression.kind === SyntaxKind.SuperKeyword) {
return voidType;
}
if (node.kind === SyntaxKind.NewExpression) {
const declaration = signature.declaration;
if (declaration &&
declaration.kind !== SyntaxKind.Constructor &&
declaration.kind !== SyntaxKind.ConstructSignature &&
declaration.kind !== SyntaxKind.ConstructorType &&
!isJSDocConstructSignature(declaration)) {
// When resolved signature is a call signature (and not a construct signature) the result type is any, unless
// the declaring function had members created through 'x.prototype.y = expr' or 'this.y = expr' psuedodeclarations
// in a JS file
// Note:JS inferred classes might come from a variable declaration instead of a function declaration.
// In this case, using getResolvedSymbol directly is required to avoid losing the members from the declaration.
let funcSymbol = node.expression.kind === SyntaxKind.Identifier ?
getResolvedSymbol(node.expression as Identifier) :
checkExpression(node.expression).symbol;
if (funcSymbol && isDeclarationOfFunctionOrClassExpression(funcSymbol)) {
funcSymbol = getSymbolOfNode((<VariableDeclaration>funcSymbol.valueDeclaration).initializer);
}
if (funcSymbol && funcSymbol.members && funcSymbol.flags & SymbolFlags.Function) {
return getInferredClassType(funcSymbol);
}
else if (noImplicitAny) {
error(node, Diagnostics.new_expression_whose_target_lacks_a_construct_signature_implicitly_has_an_any_type);
}
return anyType;
}
}
// In JavaScript files, calls to any identifier 'require' are treated as external module imports
if (isInJavaScriptFile(node) && isCommonJsRequire(node)) {
return resolveExternalModuleTypeByLiteral(<StringLiteral>node.arguments[0]);
}
return getReturnTypeOfSignature(signature);
}
function isCommonJsRequire(node: Node) {
if (!isRequireCall(node, /*checkArgumentIsStringLiteral*/ true)) {
return false;
}
// Make sure require is not a local function
const resolvedRequire = resolveName(node.expression, (<Identifier>node.expression).text, SymbolFlags.Value, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined);
if (!resolvedRequire) {
// project does not contain symbol named 'require' - assume commonjs require
return true;
}
// project includes symbol named 'require' - make sure that it it ambient and local non-alias
if (resolvedRequire.flags & SymbolFlags.Alias) {
return false;
}
const targetDeclarationKind = resolvedRequire.flags & SymbolFlags.Function
? SyntaxKind.FunctionDeclaration
: resolvedRequire.flags & SymbolFlags.Variable
? SyntaxKind.VariableDeclaration
: SyntaxKind.Unknown;
if (targetDeclarationKind !== SyntaxKind.Unknown) {
const decl = getDeclarationOfKind(resolvedRequire, targetDeclarationKind);
// function/variable declaration should be ambient
return isInAmbientContext(decl);
}
return false;
}
function checkTaggedTemplateExpression(node: TaggedTemplateExpression): Type {
return getReturnTypeOfSignature(getResolvedSignature(node));
}
function checkAssertion(node: AssertionExpression) {
const exprType = getRegularTypeOfObjectLiteral(getBaseTypeOfLiteralType(checkExpression(node.expression)));
checkSourceElement(node.type);
const targetType = getTypeFromTypeNode(node.type);
if (produceDiagnostics && targetType !== unknownType) {
const widenedType = getWidenedType(exprType);
if (!isTypeComparableTo(targetType, widenedType)) {
checkTypeComparableTo(exprType, targetType, node, Diagnostics.Type_0_cannot_be_converted_to_type_1);
}
}
return targetType;
}
function checkNonNullAssertion(node: NonNullExpression) {
return getNonNullableType(checkExpression(node.expression));
}
function checkMetaProperty(node: MetaProperty) {
checkGrammarMetaProperty(node);
const container = getNewTargetContainer(node);
if (!container) {
error(node, Diagnostics.Meta_property_0_is_only_allowed_in_the_body_of_a_function_declaration_function_expression_or_constructor, "new.target");
return unknownType;
}
else if (container.kind === SyntaxKind.Constructor) {
const symbol = getSymbolOfNode(container.parent);
return getTypeOfSymbol(symbol);
}
else {
const symbol = getSymbolOfNode(container);
return getTypeOfSymbol(symbol);
}
}
function getTypeOfParameter(symbol: Symbol) {
const type = getTypeOfSymbol(symbol);
if (strictNullChecks) {
const declaration = symbol.valueDeclaration;
if (declaration && (<VariableLikeDeclaration>declaration).initializer) {
return getNullableType(type, TypeFlags.Undefined);
}
}
return type;
}
function getTypeAtPosition(signature: Signature, pos: number): Type {
return signature.hasRestParameter ?
pos < signature.parameters.length - 1 ? getTypeOfParameter(signature.parameters[pos]) : getRestTypeOfSignature(signature) :
pos < signature.parameters.length ? getTypeOfParameter(signature.parameters[pos]) : anyType;
}
function getTypeOfFirstParameterOfSignature(signature: Signature) {
return signature.parameters.length > 0 ? getTypeAtPosition(signature, 0) : neverType;
}
function assignContextualParameterTypes(signature: Signature, context: Signature, mapper: TypeMapper, checkMode: CheckMode) {
const len = signature.parameters.length - (signature.hasRestParameter ? 1 : 0);
if (checkMode === CheckMode.Inferential) {
for (let i = 0; i < len; i++) {
const declaration = <ParameterDeclaration>signature.parameters[i].valueDeclaration;
if (declaration.type) {
inferTypesWithContext(mapper.context, getTypeFromTypeNode(declaration.type), getTypeAtPosition(context, i));
}
}
}
if (context.thisParameter) {
const parameter = signature.thisParameter;
if (!parameter || parameter.valueDeclaration && !(<ParameterDeclaration>parameter.valueDeclaration).type) {
if (!parameter) {
signature.thisParameter = createSymbolWithType(context.thisParameter, /*type*/ undefined);
}
assignTypeToParameterAndFixTypeParameters(signature.thisParameter, getTypeOfSymbol(context.thisParameter), mapper, checkMode);
}
}
for (let i = 0; i < len; i++) {
const parameter = signature.parameters[i];
if (!(<ParameterDeclaration>parameter.valueDeclaration).type) {
const contextualParameterType = getTypeAtPosition(context, i);
assignTypeToParameterAndFixTypeParameters(parameter, contextualParameterType, mapper, checkMode);
}
}
if (signature.hasRestParameter && isRestParameterIndex(context, signature.parameters.length - 1)) {
const parameter = lastOrUndefined(signature.parameters);
if (!(<ParameterDeclaration>parameter.valueDeclaration).type) {
const contextualParameterType = getTypeOfSymbol(lastOrUndefined(context.parameters));
assignTypeToParameterAndFixTypeParameters(parameter, contextualParameterType, mapper, checkMode);
}
}
}
// When contextual typing assigns a type to a parameter that contains a binding pattern, we also need to push
// the destructured type into the contained binding elements.
function assignBindingElementTypes(node: VariableLikeDeclaration) {
if (isBindingPattern(node.name)) {
for (const element of node.name.elements) {
if (!isOmittedExpression(element)) {
if (element.name.kind === SyntaxKind.Identifier) {
getSymbolLinks(getSymbolOfNode(element)).type = getTypeForBindingElement(element);
}
assignBindingElementTypes(element);
}
}
}
}
function assignTypeToParameterAndFixTypeParameters(parameter: Symbol, contextualType: Type, mapper: TypeMapper, checkMode: CheckMode) {
const links = getSymbolLinks(parameter);
if (!links.type) {
links.type = instantiateType(contextualType, mapper);
const name = getNameOfDeclaration(parameter.valueDeclaration);
// if inference didn't come up with anything but {}, fall back to the binding pattern if present.
if (links.type === emptyObjectType &&
(name.kind === SyntaxKind.ObjectBindingPattern || name.kind === SyntaxKind.ArrayBindingPattern)) {
links.type = getTypeFromBindingPattern(<BindingPattern>name);
}
assignBindingElementTypes(<ParameterDeclaration>parameter.valueDeclaration);
}
else if (checkMode === CheckMode.Inferential) {
// Even if the parameter already has a type, it might be because it was given a type while
// processing the function as an argument to a prior signature during overload resolution.
// If this was the case, it may have caused some type parameters to be fixed. So here,
// we need to ensure that type parameters at the same positions get fixed again. This is
// done by calling instantiateType to attach the mapper to the contextualType, and then
// calling inferTypes to force a walk of contextualType so that all the correct fixing
// happens. The choice to pass in links.type may seem kind of arbitrary, but it serves
// to make sure that all the correct positions in contextualType are reached by the walk.
// Here is an example:
//
// interface Base {
// baseProp;
// }
// interface Derived extends Base {
// toBase(): Base;
// }
//
// var derived: Derived;
//
// declare function foo<T>(x: T, func: (p: T) => T): T;
// declare function foo<T>(x: T, func: (p: T) => T): T;
//
// var result = foo(derived, d => d.toBase());
//
// We are typing d while checking the second overload. But we've already given d
// a type (Derived) from the first overload. However, we still want to fix the
// T in the second overload so that we do not infer Base as a candidate for T
// (inferring Base would make type argument inference inconsistent between the two
// overloads).
inferTypesWithContext(mapper.context, links.type, instantiateType(contextualType, mapper));
}
}
function getReturnTypeFromJSDocComment(func: SignatureDeclaration | FunctionDeclaration): Type {
const returnTag = getJSDocReturnTag(func);
if (returnTag && returnTag.typeExpression) {
return getTypeFromTypeNode(returnTag.typeExpression.type);
}
return undefined;
}
function createPromiseType(promisedType: Type): Type {
// creates a `Promise<T>` type where `T` is the promisedType argument
const globalPromiseType = getGlobalPromiseType(/*reportErrors*/ true);
if (globalPromiseType !== emptyGenericType) {
// if the promised type is itself a promise, get the underlying type; otherwise, fallback to the promised type
promisedType = getAwaitedType(promisedType) || emptyObjectType;
return createTypeReference(<GenericType>globalPromiseType, [promisedType]);
}
return emptyObjectType;
}
function createPromiseReturnType(func: FunctionLikeDeclaration, promisedType: Type) {
const promiseType = createPromiseType(promisedType);
if (promiseType === emptyObjectType) {
error(func, Diagnostics.An_async_function_or_method_must_return_a_Promise_Make_sure_you_have_a_declaration_for_Promise_or_include_ES2015_in_your_lib_option);
return unknownType;
}
else if (!getGlobalPromiseConstructorSymbol(/*reportErrors*/ true)) {
error(func, Diagnostics.An_async_function_or_method_in_ES5_SlashES3_requires_the_Promise_constructor_Make_sure_you_have_a_declaration_for_the_Promise_constructor_or_include_ES2015_in_your_lib_option);
}
return promiseType;
}
function getReturnTypeFromBody(func: FunctionLikeDeclaration, checkMode?: CheckMode): Type {
const contextualSignature = getContextualSignatureForFunctionLikeDeclaration(func);
if (!func.body) {
return unknownType;
}
const functionFlags = getFunctionFlags(func);
let type: Type;
if (func.body.kind !== SyntaxKind.Block) {
type = checkExpressionCached(<Expression>func.body, checkMode);
if (functionFlags & FunctionFlags.Async) {
// From within an async function you can return either a non-promise value or a promise. Any
// Promise/A+ compatible implementation will always assimilate any foreign promise, so the
// return type of the body should be unwrapped to its awaited type, which we will wrap in
// the native Promise<T> type later in this function.
type = checkAwaitedType(type, /*errorNode*/ func, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
}
}
else {
let types: Type[];
if (functionFlags & FunctionFlags.Generator) { // Generator or AsyncGenerator function
types = concatenate(checkAndAggregateYieldOperandTypes(func, checkMode), checkAndAggregateReturnExpressionTypes(func, checkMode));
if (!types || types.length === 0) {
const iterableIteratorAny = functionFlags & FunctionFlags.Async
? createAsyncIterableIteratorType(anyType) // AsyncGenerator function
: createIterableIteratorType(anyType); // Generator function
if (noImplicitAny) {
error(func.asteriskToken,
Diagnostics.Generator_implicitly_has_type_0_because_it_does_not_yield_any_values_Consider_supplying_a_return_type, typeToString(iterableIteratorAny));
}
return iterableIteratorAny;
}
}
else {
types = checkAndAggregateReturnExpressionTypes(func, checkMode);
if (!types) {
// For an async function, the return type will not be never, but rather a Promise for never.
return functionFlags & FunctionFlags.Async
? createPromiseReturnType(func, neverType) // Async function
: neverType; // Normal function
}
if (types.length === 0) {
// For an async function, the return type will not be void, but rather a Promise for void.
return functionFlags & FunctionFlags.Async
? createPromiseReturnType(func, voidType) // Async function
: voidType; // Normal function
}
}
// Return a union of the return expression types.
type = getUnionType(types, /*subtypeReduction*/ true);
if (functionFlags & FunctionFlags.Generator) { // AsyncGenerator function or Generator function
type = functionFlags & FunctionFlags.Async
? createAsyncIterableIteratorType(type) // AsyncGenerator function
: createIterableIteratorType(type); // Generator function
}
}
if (!contextualSignature) {
reportErrorsFromWidening(func, type);
}
if (isUnitType(type) &&
!(contextualSignature &&
isLiteralContextualType(
contextualSignature === getSignatureFromDeclaration(func) ? type : getReturnTypeOfSignature(contextualSignature)))) {
type = getWidenedLiteralType(type);
}
const widenedType = getWidenedType(type);
// From within an async function you can return either a non-promise value or a promise. Any
// Promise/A+ compatible implementation will always assimilate any foreign promise, so the
// return type of the body is awaited type of the body, wrapped in a native Promise<T> type.
return (functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Async
? createPromiseReturnType(func, widenedType) // Async function
: widenedType; // Generator function, AsyncGenerator function, or normal function
}
function checkAndAggregateYieldOperandTypes(func: FunctionLikeDeclaration, checkMode: CheckMode): Type[] {
const aggregatedTypes: Type[] = [];
const functionFlags = getFunctionFlags(func);
forEachYieldExpression(<Block>func.body, yieldExpression => {
const expr = yieldExpression.expression;
if (expr) {
let type = checkExpressionCached(expr, checkMode);
if (yieldExpression.asteriskToken) {
// A yield* expression effectively yields everything that its operand yields
type = checkIteratedTypeOrElementType(type, yieldExpression.expression, /*allowStringInput*/ false, (functionFlags & FunctionFlags.Async) !== 0);
}
if (functionFlags & FunctionFlags.Async) {
type = checkAwaitedType(type, expr, yieldExpression.asteriskToken
? Diagnostics.Type_of_iterated_elements_of_a_yield_Asterisk_operand_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member
: Diagnostics.Type_of_yield_operand_in_an_async_generator_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
}
if (!contains(aggregatedTypes, type)) {
aggregatedTypes.push(type);
}
}
});
return aggregatedTypes;
}
function isExhaustiveSwitchStatement(node: SwitchStatement): boolean {
if (!node.possiblyExhaustive) {
return false;
}
const type = getTypeOfExpression(node.expression);
if (!isLiteralType(type)) {
return false;
}
const switchTypes = getSwitchClauseTypes(node);
if (!switchTypes.length) {
return false;
}
return eachTypeContainedIn(mapType(type, getRegularTypeOfLiteralType), switchTypes);
}
function functionHasImplicitReturn(func: FunctionLikeDeclaration) {
if (!(func.flags & NodeFlags.HasImplicitReturn)) {
return false;
}
const lastStatement = lastOrUndefined((<Block>func.body).statements);
if (lastStatement && lastStatement.kind === SyntaxKind.SwitchStatement && isExhaustiveSwitchStatement(<SwitchStatement>lastStatement)) {
return false;
}
return true;
}
function checkAndAggregateReturnExpressionTypes(func: FunctionLikeDeclaration, checkMode: CheckMode): Type[] {
const functionFlags = getFunctionFlags(func);
const aggregatedTypes: Type[] = [];
let hasReturnWithNoExpression = functionHasImplicitReturn(func);
let hasReturnOfTypeNever = false;
forEachReturnStatement(<Block>func.body, returnStatement => {
const expr = returnStatement.expression;
if (expr) {
let type = checkExpressionCached(expr, checkMode);
if (functionFlags & FunctionFlags.Async) {
// From within an async function you can return either a non-promise value or a promise. Any
// Promise/A+ compatible implementation will always assimilate any foreign promise, so the
// return type of the body should be unwrapped to its awaited type, which should be wrapped in
// the native Promise<T> type by the caller.
type = checkAwaitedType(type, func, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
}
if (type.flags & TypeFlags.Never) {
hasReturnOfTypeNever = true;
}
else if (!contains(aggregatedTypes, type)) {
aggregatedTypes.push(type);
}
}
else {
hasReturnWithNoExpression = true;
}
});
if (aggregatedTypes.length === 0 && !hasReturnWithNoExpression && (hasReturnOfTypeNever ||
func.kind === SyntaxKind.FunctionExpression || func.kind === SyntaxKind.ArrowFunction)) {
return undefined;
}
if (strictNullChecks && aggregatedTypes.length && hasReturnWithNoExpression) {
if (!contains(aggregatedTypes, undefinedType)) {
aggregatedTypes.push(undefinedType);
}
}
return aggregatedTypes;
}
/**
* TypeScript Specification 1.0 (6.3) - July 2014
* An explicitly typed function whose return type isn't the Void type,
* the Any type, or a union type containing the Void or Any type as a constituent
* must have at least one return statement somewhere in its body.
* An exception to this rule is if the function implementation consists of a single 'throw' statement.
*
* @param returnType - return type of the function, can be undefined if return type is not explicitly specified
*/
function checkAllCodePathsInNonVoidFunctionReturnOrThrow(func: FunctionLikeDeclaration, returnType: Type): void {
if (!produceDiagnostics) {
return;
}
// Functions with with an explicitly specified 'void' or 'any' return type don't need any return expressions.
if (returnType && maybeTypeOfKind(returnType, TypeFlags.Any | TypeFlags.Void)) {
return;
}
// If all we have is a function signature, or an arrow function with an expression body, then there is nothing to check.
// also if HasImplicitReturn flag is not set this means that all codepaths in function body end with return or throw
if (nodeIsMissing(func.body) || func.body.kind !== SyntaxKind.Block || !functionHasImplicitReturn(func)) {
return;
}
const hasExplicitReturn = func.flags & NodeFlags.HasExplicitReturn;
if (returnType && returnType.flags & TypeFlags.Never) {
error(func.type, Diagnostics.A_function_returning_never_cannot_have_a_reachable_end_point);
}
else if (returnType && !hasExplicitReturn) {
// minimal check: function has syntactic return type annotation and no explicit return statements in the body
// this function does not conform to the specification.
// NOTE: having returnType !== undefined is a precondition for entering this branch so func.type will always be present
error(func.type, Diagnostics.A_function_whose_declared_type_is_neither_void_nor_any_must_return_a_value);
}
else if (returnType && strictNullChecks && !isTypeAssignableTo(undefinedType, returnType)) {
error(func.type, Diagnostics.Function_lacks_ending_return_statement_and_return_type_does_not_include_undefined);
}
else if (compilerOptions.noImplicitReturns) {
if (!returnType) {
// If return type annotation is omitted check if function has any explicit return statements.
// If it does not have any - its inferred return type is void - don't do any checks.
// Otherwise get inferred return type from function body and report error only if it is not void / anytype
if (!hasExplicitReturn) {
return;
}
const inferredReturnType = getReturnTypeOfSignature(getSignatureFromDeclaration(func));
if (isUnwrappedReturnTypeVoidOrAny(func, inferredReturnType)) {
return;
}
}
error(func.type || func, Diagnostics.Not_all_code_paths_return_a_value);
}
}
function checkFunctionExpressionOrObjectLiteralMethod(node: FunctionExpression | MethodDeclaration, checkMode?: CheckMode): Type {
Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isObjectLiteralMethod(node));
// Grammar checking
const hasGrammarError = checkGrammarFunctionLikeDeclaration(node);
if (!hasGrammarError && node.kind === SyntaxKind.FunctionExpression) {
checkGrammarForGenerator(node);
}
// The identityMapper object is used to indicate that function expressions are wildcards
if (checkMode === CheckMode.SkipContextSensitive && isContextSensitive(node)) {
checkNodeDeferred(node);
return anyFunctionType;
}
const links = getNodeLinks(node);
const type = getTypeOfSymbol(node.symbol);
const contextSensitive = isContextSensitive(node);
const mightFixTypeParameters = contextSensitive && checkMode === CheckMode.Inferential;
// Check if function expression is contextually typed and assign parameter types if so.
// See the comment in assignTypeToParameterAndFixTypeParameters to understand why we need to
// check mightFixTypeParameters.
if (mightFixTypeParameters || !(links.flags & NodeCheckFlags.ContextChecked)) {
const contextualSignature = getContextualSignature(node);
// If a type check is started at a function expression that is an argument of a function call, obtaining the
// contextual type may recursively get back to here during overload resolution of the call. If so, we will have
// already assigned contextual types.
const contextChecked = !!(links.flags & NodeCheckFlags.ContextChecked);
if (mightFixTypeParameters || !contextChecked) {
links.flags |= NodeCheckFlags.ContextChecked;
if (contextualSignature) {
const signature = getSignaturesOfType(type, SignatureKind.Call)[0];
if (contextSensitive) {
assignContextualParameterTypes(signature, contextualSignature, getContextualMapper(node), checkMode);
}
if (mightFixTypeParameters || !node.type && !signature.resolvedReturnType) {
const returnType = getReturnTypeFromBody(node, checkMode);
if (!signature.resolvedReturnType) {
signature.resolvedReturnType = returnType;
}
}
}
if (!contextChecked) {
checkSignatureDeclaration(node);
checkNodeDeferred(node);
}
}
}
if (produceDiagnostics && node.kind !== SyntaxKind.MethodDeclaration) {
checkCollisionWithCapturedSuperVariable(node, (<FunctionExpression>node).name);
checkCollisionWithCapturedThisVariable(node, (<FunctionExpression>node).name);
checkCollisionWithCapturedNewTargetVariable(node, (<FunctionExpression>node).name);
}
return type;
}
function checkFunctionExpressionOrObjectLiteralMethodDeferred(node: ArrowFunction | FunctionExpression | MethodDeclaration) {
Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isObjectLiteralMethod(node));
const functionFlags = getFunctionFlags(node);
const returnOrPromisedType = node.type &&
((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Async ?
checkAsyncFunctionReturnType(node) : // Async function
getTypeFromTypeNode(node.type)); // AsyncGenerator function, Generator function, or normal function
if ((functionFlags & FunctionFlags.Generator) === 0) { // Async function or normal function
// return is not necessary in the body of generators
checkAllCodePathsInNonVoidFunctionReturnOrThrow(node, returnOrPromisedType);
}
if (node.body) {
if (!node.type) {
// There are some checks that are only performed in getReturnTypeFromBody, that may produce errors
// we need. An example is the noImplicitAny errors resulting from widening the return expression
// of a function. Because checking of function expression bodies is deferred, there was never an
// appropriate time to do this during the main walk of the file (see the comment at the top of
// checkFunctionExpressionBodies). So it must be done now.
getReturnTypeOfSignature(getSignatureFromDeclaration(node));
}
if (node.body.kind === SyntaxKind.Block) {
checkSourceElement(node.body);
}
else {
// From within an async function you can return either a non-promise value or a promise. Any
// Promise/A+ compatible implementation will always assimilate any foreign promise, so we
// should not be checking assignability of a promise to the return type. Instead, we need to
// check assignability of the awaited type of the expression body against the promised type of
// its return type annotation.
const exprType = checkExpression(<Expression>node.body);
if (returnOrPromisedType) {
if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Async) { // Async function
const awaitedType = checkAwaitedType(exprType, node.body, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
checkTypeAssignableTo(awaitedType, returnOrPromisedType, node.body);
}
else { // Normal function
checkTypeAssignableTo(exprType, returnOrPromisedType, node.body);
}
}
}
registerForUnusedIdentifiersCheck(node);
}
}
function checkArithmeticOperandType(operand: Node, type: Type, diagnostic: DiagnosticMessage): boolean {
if (!isTypeAnyOrAllConstituentTypesHaveKind(type, TypeFlags.NumberLike)) {
error(operand, diagnostic);
return false;
}
return true;
}
function isReadonlySymbol(symbol: Symbol): boolean {
// The following symbols are considered read-only:
// Properties with a 'readonly' modifier
// Variables declared with 'const'
// Get accessors without matching set accessors
// Enum members
// Unions and intersections of the above (unions and intersections eagerly set isReadonly on creation)
return !!(getCheckFlags(symbol) & CheckFlags.Readonly ||
symbol.flags & SymbolFlags.Property && getDeclarationModifierFlagsFromSymbol(symbol) & ModifierFlags.Readonly ||
symbol.flags & SymbolFlags.Variable && getDeclarationNodeFlagsFromSymbol(symbol) & NodeFlags.Const ||
symbol.flags & SymbolFlags.Accessor && !(symbol.flags & SymbolFlags.SetAccessor) ||
symbol.flags & SymbolFlags.EnumMember);
}
function isReferenceToReadonlyEntity(expr: Expression, symbol: Symbol): boolean {
if (isReadonlySymbol(symbol)) {
// Allow assignments to readonly properties within constructors of the same class declaration.
if (symbol.flags & SymbolFlags.Property &&
(expr.kind === SyntaxKind.PropertyAccessExpression || expr.kind === SyntaxKind.ElementAccessExpression) &&
(expr as PropertyAccessExpression | ElementAccessExpression).expression.kind === SyntaxKind.ThisKeyword) {
// Look for if this is the constructor for the class that `symbol` is a property of.
const func = getContainingFunction(expr);
if (!(func && func.kind === SyntaxKind.Constructor))
return true;
// If func.parent is a class and symbol is a (readonly) property of that class, or
// if func is a constructor and symbol is a (readonly) parameter property declared in it,
// then symbol is writeable here.
return !(func.parent === symbol.valueDeclaration.parent || func === symbol.valueDeclaration.parent);
}
return true;
}
return false;
}
function isReferenceThroughNamespaceImport(expr: Expression): boolean {
if (expr.kind === SyntaxKind.PropertyAccessExpression || expr.kind === SyntaxKind.ElementAccessExpression) {
const node = skipParentheses((expr as PropertyAccessExpression | ElementAccessExpression).expression);
if (node.kind === SyntaxKind.Identifier) {
const symbol = getNodeLinks(node).resolvedSymbol;
if (symbol.flags & SymbolFlags.Alias) {
const declaration = getDeclarationOfAliasSymbol(symbol);
return declaration && declaration.kind === SyntaxKind.NamespaceImport;
}
}
}
return false;
}
function checkReferenceExpression(expr: Expression, invalidReferenceMessage: DiagnosticMessage): boolean {
// References are combinations of identifiers, parentheses, and property accesses.
const node = skipParentheses(expr);
if (node.kind !== SyntaxKind.Identifier && node.kind !== SyntaxKind.PropertyAccessExpression && node.kind !== SyntaxKind.ElementAccessExpression) {
error(expr, invalidReferenceMessage);
return false;
}
return true;
}
function checkDeleteExpression(node: DeleteExpression): Type {
checkExpression(node.expression);
const expr = skipParentheses(node.expression);
if (expr.kind !== SyntaxKind.PropertyAccessExpression && expr.kind !== SyntaxKind.ElementAccessExpression) {
error(expr, Diagnostics.The_operand_of_a_delete_operator_must_be_a_property_reference);
return booleanType;
}
const links = getNodeLinks(expr);
const symbol = getExportSymbolOfValueSymbolIfExported(links.resolvedSymbol);
if (symbol && isReadonlySymbol(symbol)) {
error(expr, Diagnostics.The_operand_of_a_delete_operator_cannot_be_a_read_only_property);
}
return booleanType;
}
function checkTypeOfExpression(node: TypeOfExpression): Type {
checkExpression(node.expression);
return typeofType;
}
function checkVoidExpression(node: VoidExpression): Type {
checkExpression(node.expression);
return undefinedWideningType;
}
function checkAwaitExpression(node: AwaitExpression): Type {
// Grammar checking
if (produceDiagnostics) {
if (!(node.flags & NodeFlags.AwaitContext)) {
grammarErrorOnFirstToken(node, Diagnostics.await_expression_is_only_allowed_within_an_async_function);
}
if (isInParameterInitializerBeforeContainingFunction(node)) {
error(node, Diagnostics.await_expressions_cannot_be_used_in_a_parameter_initializer);
}
}
const operandType = checkExpression(node.expression);
return checkAwaitedType(operandType, node, Diagnostics.Type_of_await_operand_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
}
function checkPrefixUnaryExpression(node: PrefixUnaryExpression): Type {
const operandType = checkExpression(node.operand);
if (operandType === silentNeverType) {
return silentNeverType;
}
if (node.operator === SyntaxKind.MinusToken && node.operand.kind === SyntaxKind.NumericLiteral) {
return getFreshTypeOfLiteralType(getLiteralType(-(<LiteralExpression>node.operand).text));
}
switch (node.operator) {
case SyntaxKind.PlusToken:
case SyntaxKind.MinusToken:
case SyntaxKind.TildeToken:
checkNonNullType(operandType, node.operand);
if (maybeTypeOfKind(operandType, TypeFlags.ESSymbol)) {
error(node.operand, Diagnostics.The_0_operator_cannot_be_applied_to_type_symbol, tokenToString(node.operator));
}
return numberType;
case SyntaxKind.ExclamationToken:
const facts = getTypeFacts(operandType) & (TypeFacts.Truthy | TypeFacts.Falsy);
return facts === TypeFacts.Truthy ? falseType :
facts === TypeFacts.Falsy ? trueType :
booleanType;
case SyntaxKind.PlusPlusToken:
case SyntaxKind.MinusMinusToken:
const ok = checkArithmeticOperandType(node.operand, checkNonNullType(operandType, node.operand),
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_or_a_property_access);
}
return numberType;
}
return unknownType;
}
function checkPostfixUnaryExpression(node: PostfixUnaryExpression): Type {
const operandType = checkExpression(node.operand);
if (operandType === silentNeverType) {
return silentNeverType;
}
const ok = checkArithmeticOperandType(node.operand, checkNonNullType(operandType, node.operand),
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_or_a_property_access);
}
return numberType;
}
// Return true if type might be of the given kind. A union or intersection type might be of a given
// kind if at least one constituent type is of the given kind.
function maybeTypeOfKind(type: Type, kind: TypeFlags): boolean {
if (type.flags & kind) {
return true;
}
if (type.flags & TypeFlags.UnionOrIntersection) {
const types = (<UnionOrIntersectionType>type).types;
for (const t of types) {
if (maybeTypeOfKind(t, kind)) {
return true;
}
}
}
return false;
}
// Return true if type is of the given kind. A union type is of a given kind if all constituent types
// are of the given kind. An intersection type is of a given kind if at least one constituent type is
// of the given kind.
function isTypeOfKind(type: Type, kind: TypeFlags): boolean {
if (type.flags & kind) {
return true;
}
if (type.flags & TypeFlags.Union) {
const types = (<UnionOrIntersectionType>type).types;
for (const t of types) {
if (!isTypeOfKind(t, kind)) {
return false;
}
}
return true;
}
if (type.flags & TypeFlags.Intersection) {
const types = (<UnionOrIntersectionType>type).types;
for (const t of types) {
if (isTypeOfKind(t, kind)) {
return true;
}
}
}
return false;
}
function isConstEnumObjectType(type: Type): boolean {
return getObjectFlags(type) & ObjectFlags.Anonymous && type.symbol && isConstEnumSymbol(type.symbol);
}
function isConstEnumSymbol(symbol: Symbol): boolean {
return (symbol.flags & SymbolFlags.ConstEnum) !== 0;
}
function checkInstanceOfExpression(left: Expression, right: Expression, leftType: Type, rightType: Type): Type {
if (leftType === silentNeverType || rightType === silentNeverType) {
return silentNeverType;
}
// 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, a subtype of the 'Function' interface type, or have a call or construct signature.
// The result is always of the Boolean primitive type.
// NOTE: do not raise error if leftType is unknown as related error was already reported
if (isTypeOfKind(leftType, TypeFlags.Primitive)) {
error(left, Diagnostics.The_left_hand_side_of_an_instanceof_expression_must_be_of_type_any_an_object_type_or_a_type_parameter);
}
// NOTE: do not raise error if right is unknown as related error was already reported
if (!(isTypeAny(rightType) ||
getSignaturesOfType(rightType, SignatureKind.Call).length ||
getSignaturesOfType(rightType, SignatureKind.Construct).length ||
isTypeSubtypeOf(rightType, globalFunctionType))) {
error(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(left: Expression, right: Expression, leftType: Type, rightType: Type): Type {
if (leftType === silentNeverType || rightType === silentNeverType) {
return silentNeverType;
}
leftType = checkNonNullType(leftType, left);
rightType = checkNonNullType(rightType, right);
// 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 (!(isTypeComparableTo(leftType, stringType) || isTypeOfKind(leftType, TypeFlags.NumberLike | TypeFlags.ESSymbol))) {
error(left, Diagnostics.The_left_hand_side_of_an_in_expression_must_be_of_type_any_string_number_or_symbol);
}
if (!isTypeAnyOrAllConstituentTypesHaveKind(rightType, TypeFlags.Object | TypeFlags.TypeVariable | TypeFlags.NonPrimitive)) {
error(right, Diagnostics.The_right_hand_side_of_an_in_expression_must_be_of_type_any_an_object_type_or_a_type_parameter);
}
return booleanType;
}
function checkObjectLiteralAssignment(node: ObjectLiteralExpression, sourceType: Type): Type {
const properties = node.properties;
for (const p of properties) {
checkObjectLiteralDestructuringPropertyAssignment(sourceType, p, properties);
}
return sourceType;
}
/** Note: If property cannot be a SpreadAssignment, then allProperties does not need to be provided */
function checkObjectLiteralDestructuringPropertyAssignment(objectLiteralType: Type, property: ObjectLiteralElementLike, allProperties?: ObjectLiteralElementLike[]) {
if (property.kind === SyntaxKind.PropertyAssignment || property.kind === SyntaxKind.ShorthandPropertyAssignment) {
const name = <PropertyName>(<PropertyAssignment>property).name;
if (name.kind === SyntaxKind.ComputedPropertyName) {
checkComputedPropertyName(<ComputedPropertyName>name);
}
if (isComputedNonLiteralName(name)) {
return undefined;
}
const text = getTextOfPropertyName(name);
const type = isTypeAny(objectLiteralType)
? objectLiteralType
: getTypeOfPropertyOfType(objectLiteralType, text) ||
isNumericLiteralName(text) && getIndexTypeOfType(objectLiteralType, IndexKind.Number) ||
getIndexTypeOfType(objectLiteralType, IndexKind.String);
if (type) {
if (property.kind === SyntaxKind.ShorthandPropertyAssignment) {
return checkDestructuringAssignment(<ShorthandPropertyAssignment>property, type);
}
else {
// non-shorthand property assignments should always have initializers
return checkDestructuringAssignment((<PropertyAssignment>property).initializer, type);
}
}
else {
error(name, Diagnostics.Type_0_has_no_property_1_and_no_string_index_signature, typeToString(objectLiteralType), declarationNameToString(name));
}
}
else if (property.kind === SyntaxKind.SpreadAssignment) {
if (languageVersion < ScriptTarget.ESNext) {
checkExternalEmitHelpers(property, ExternalEmitHelpers.Rest);
}
const nonRestNames: PropertyName[] = [];
if (allProperties) {
for (let i = 0; i < allProperties.length - 1; i++) {
nonRestNames.push(allProperties[i].name);
}
}
const type = getRestType(objectLiteralType, nonRestNames, objectLiteralType.symbol);
return checkDestructuringAssignment(property.expression, type);
}
else {
error(property, Diagnostics.Property_assignment_expected);
}
}
function checkArrayLiteralAssignment(node: ArrayLiteralExpression, sourceType: Type, checkMode?: CheckMode): Type {
if (languageVersion < ScriptTarget.ES2015 && compilerOptions.downlevelIteration) {
checkExternalEmitHelpers(node, ExternalEmitHelpers.Read);
}
// This elementType will be used if the specific property corresponding to this index is not
// present (aka the tuple element property). This call also checks that the parentType is in
// fact an iterable or array (depending on target language).
const elementType = checkIteratedTypeOrElementType(sourceType, node, /*allowStringInput*/ false, /*allowAsyncIterable*/ false) || unknownType;
const elements = node.elements;
for (let i = 0; i < elements.length; i++) {
checkArrayLiteralDestructuringElementAssignment(node, sourceType, i, elementType, checkMode);
}
return sourceType;
}
function checkArrayLiteralDestructuringElementAssignment(node: ArrayLiteralExpression, sourceType: Type,
elementIndex: number, elementType: Type, checkMode?: CheckMode) {
const elements = node.elements;
const element = elements[elementIndex];
if (element.kind !== SyntaxKind.OmittedExpression) {
if (element.kind !== SyntaxKind.SpreadElement) {
const propName = "" + elementIndex;
const type = isTypeAny(sourceType)
? sourceType
: isTupleLikeType(sourceType)
? getTypeOfPropertyOfType(sourceType, propName)
: elementType;
if (type) {
return checkDestructuringAssignment(element, type, checkMode);
}
else {
// We still need to check element expression here because we may need to set appropriate flag on the expression
// such as NodeCheckFlags.LexicalThis on "this"expression.
checkExpression(element);
if (isTupleType(sourceType)) {
error(element, Diagnostics.Tuple_type_0_with_length_1_cannot_be_assigned_to_tuple_with_length_2, typeToString(sourceType), getTypeReferenceArity(<TypeReference>sourceType), elements.length);
}
else {
error(element, Diagnostics.Type_0_has_no_property_1, typeToString(sourceType), propName);
}
}
}
else {
if (elementIndex < elements.length - 1) {
error(element, Diagnostics.A_rest_element_must_be_last_in_a_destructuring_pattern);
}
else {
const restExpression = (<SpreadElement>element).expression;
if (restExpression.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>restExpression).operatorToken.kind === SyntaxKind.EqualsToken) {
error((<BinaryExpression>restExpression).operatorToken, Diagnostics.A_rest_element_cannot_have_an_initializer);
}
else {
return checkDestructuringAssignment(restExpression, createArrayType(elementType), checkMode);
}
}
}
}
return undefined;
}
function checkDestructuringAssignment(exprOrAssignment: Expression | ShorthandPropertyAssignment, sourceType: Type, checkMode?: CheckMode): Type {
let target: Expression;
if (exprOrAssignment.kind === SyntaxKind.ShorthandPropertyAssignment) {
const prop = <ShorthandPropertyAssignment>exprOrAssignment;
if (prop.objectAssignmentInitializer) {
// In strict null checking mode, if a default value of a non-undefined type is specified, remove
// undefined from the final type.
if (strictNullChecks &&
!(getFalsyFlags(checkExpression(prop.objectAssignmentInitializer)) & TypeFlags.Undefined)) {
sourceType = getTypeWithFacts(sourceType, TypeFacts.NEUndefined);
}
checkBinaryLikeExpression(prop.name, prop.equalsToken, prop.objectAssignmentInitializer, checkMode);
}
target = (<ShorthandPropertyAssignment>exprOrAssignment).name;
}
else {
target = <Expression>exprOrAssignment;
}
if (target.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>target).operatorToken.kind === SyntaxKind.EqualsToken) {
checkBinaryExpression(<BinaryExpression>target, checkMode);
target = (<BinaryExpression>target).left;
}
if (target.kind === SyntaxKind.ObjectLiteralExpression) {
return checkObjectLiteralAssignment(<ObjectLiteralExpression>target, sourceType);
}
if (target.kind === SyntaxKind.ArrayLiteralExpression) {
return checkArrayLiteralAssignment(<ArrayLiteralExpression>target, sourceType, checkMode);
}
return checkReferenceAssignment(target, sourceType, checkMode);
}
function checkReferenceAssignment(target: Expression, sourceType: Type, checkMode?: CheckMode): Type {
const targetType = checkExpression(target, checkMode);
const error = target.parent.kind === SyntaxKind.SpreadAssignment ?
Diagnostics.The_target_of_an_object_rest_assignment_must_be_a_variable_or_a_property_access :
Diagnostics.The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access;
if (checkReferenceExpression(target, error)) {
checkTypeAssignableTo(sourceType, targetType, target, /*headMessage*/ undefined);
}
return sourceType;
}
/**
* This is a *shallow* check: An expression is side-effect-free if the
* evaluation of the expression *itself* cannot produce side effects.
* For example, x++ / 3 is side-effect free because the / operator
* does not have side effects.
* The intent is to "smell test" an expression for correctness in positions where
* its value is discarded (e.g. the left side of the comma operator).
*/
function isSideEffectFree(node: Node): boolean {
node = skipParentheses(node);
switch (node.kind) {
case SyntaxKind.Identifier:
case SyntaxKind.StringLiteral:
case SyntaxKind.RegularExpressionLiteral:
case SyntaxKind.TaggedTemplateExpression:
case SyntaxKind.TemplateExpression:
case SyntaxKind.NoSubstitutionTemplateLiteral:
case SyntaxKind.NumericLiteral:
case SyntaxKind.TrueKeyword:
case SyntaxKind.FalseKeyword:
case SyntaxKind.NullKeyword:
case SyntaxKind.UndefinedKeyword:
case SyntaxKind.FunctionExpression:
case SyntaxKind.ClassExpression:
case SyntaxKind.ArrowFunction:
case SyntaxKind.ArrayLiteralExpression:
case SyntaxKind.ObjectLiteralExpression:
case SyntaxKind.TypeOfExpression:
case SyntaxKind.NonNullExpression:
case SyntaxKind.JsxSelfClosingElement:
case SyntaxKind.JsxElement:
return true;
case SyntaxKind.ConditionalExpression:
return isSideEffectFree((node as ConditionalExpression).whenTrue) &&
isSideEffectFree((node as ConditionalExpression).whenFalse);
case SyntaxKind.BinaryExpression:
if (isAssignmentOperator((node as BinaryExpression).operatorToken.kind)) {
return false;
}
return isSideEffectFree((node as BinaryExpression).left) &&
isSideEffectFree((node as BinaryExpression).right);
case SyntaxKind.PrefixUnaryExpression:
case SyntaxKind.PostfixUnaryExpression:
// Unary operators ~, !, +, and - have no side effects.
// The rest do.
switch ((node as PrefixUnaryExpression).operator) {
case SyntaxKind.ExclamationToken:
case SyntaxKind.PlusToken:
case SyntaxKind.MinusToken:
case SyntaxKind.TildeToken:
return true;
}
return false;
// Some forms listed here for clarity
case SyntaxKind.VoidExpression: // Explicit opt-out
case SyntaxKind.TypeAssertionExpression: // Not SEF, but can produce useful type warnings
case SyntaxKind.AsExpression: // Not SEF, but can produce useful type warnings
default:
return false;
}
}
function isTypeEqualityComparableTo(source: Type, target: Type) {
return (target.flags & TypeFlags.Nullable) !== 0 || isTypeComparableTo(source, target);
}
function getBestChoiceType(type1: Type, type2: Type): Type {
const firstAssignableToSecond = isTypeAssignableTo(type1, type2);
const secondAssignableToFirst = isTypeAssignableTo(type2, type1);
return secondAssignableToFirst && !firstAssignableToSecond ? type1 :
firstAssignableToSecond && !secondAssignableToFirst ? type2 :
getUnionType([type1, type2], /*subtypeReduction*/ true);
}
function checkBinaryExpression(node: BinaryExpression, checkMode?: CheckMode) {
return checkBinaryLikeExpression(node.left, node.operatorToken, node.right, checkMode, node);
}
function checkBinaryLikeExpression(left: Expression, operatorToken: Node, right: Expression, checkMode?: CheckMode, errorNode?: Node) {
const operator = operatorToken.kind;
if (operator === SyntaxKind.EqualsToken && (left.kind === SyntaxKind.ObjectLiteralExpression || left.kind === SyntaxKind.ArrayLiteralExpression)) {
return checkDestructuringAssignment(left, checkExpression(right, checkMode), checkMode);
}
let leftType = checkExpression(left, checkMode);
let rightType = checkExpression(right, checkMode);
switch (operator) {
case SyntaxKind.AsteriskToken:
case SyntaxKind.AsteriskAsteriskToken:
case SyntaxKind.AsteriskEqualsToken:
case SyntaxKind.AsteriskAsteriskEqualsToken:
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:
if (leftType === silentNeverType || rightType === silentNeverType) {
return silentNeverType;
}
leftType = checkNonNullType(leftType, left);
rightType = checkNonNullType(rightType, right);
let suggestedOperator: SyntaxKind;
// if a user tries to apply a bitwise operator to 2 boolean operands
// try and return them a helpful suggestion
if ((leftType.flags & TypeFlags.BooleanLike) &&
(rightType.flags & TypeFlags.BooleanLike) &&
(suggestedOperator = getSuggestedBooleanOperator(operatorToken.kind)) !== undefined) {
error(errorNode || operatorToken, Diagnostics.The_0_operator_is_not_allowed_for_boolean_types_Consider_using_1_instead, tokenToString(operatorToken.kind), tokenToString(suggestedOperator));
}
else {
// otherwise just check each operand separately and report errors as normal
const leftOk = checkArithmeticOperandType(left, leftType, Diagnostics.The_left_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_or_an_enum_type);
const rightOk = checkArithmeticOperandType(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:
if (leftType === silentNeverType || rightType === silentNeverType) {
return silentNeverType;
}
if (!isTypeOfKind(leftType, TypeFlags.Any | TypeFlags.StringLike) && !isTypeOfKind(rightType, TypeFlags.Any | TypeFlags.StringLike)) {
leftType = checkNonNullType(leftType, left);
rightType = checkNonNullType(rightType, right);
}
let resultType: Type;
if (isTypeOfKind(leftType, TypeFlags.NumberLike) && isTypeOfKind(rightType, TypeFlags.NumberLike)) {
// Operands of an enum type are treated as having the primitive type Number.
// If both operands are of the Number primitive type, the result is of the Number primitive type.
resultType = numberType;
}
else {
if (isTypeOfKind(leftType, TypeFlags.StringLike) || isTypeOfKind(rightType, TypeFlags.StringLike)) {
// If one or both operands are of the String primitive type, the result is of the String primitive type.
resultType = stringType;
}
else if (isTypeAny(leftType) || isTypeAny(rightType)) {
// 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 = leftType === unknownType || rightType === unknownType ? unknownType : anyType;
}
// Symbols are not allowed at all in arithmetic expressions
if (resultType && !checkForDisallowedESSymbolOperand(operator)) {
return resultType;
}
}
if (!resultType) {
reportOperatorError();
return anyType;
}
if (operator === SyntaxKind.PlusEqualsToken) {
checkAssignmentOperator(resultType);
}
return resultType;
case SyntaxKind.LessThanToken:
case SyntaxKind.GreaterThanToken:
case SyntaxKind.LessThanEqualsToken:
case SyntaxKind.GreaterThanEqualsToken:
if (checkForDisallowedESSymbolOperand(operator)) {
leftType = getBaseTypeOfLiteralType(checkNonNullType(leftType, left));
rightType = getBaseTypeOfLiteralType(checkNonNullType(rightType, right));
if (!isTypeComparableTo(leftType, rightType) && !isTypeComparableTo(rightType, leftType)) {
reportOperatorError();
}
}
return booleanType;
case SyntaxKind.EqualsEqualsToken:
case SyntaxKind.ExclamationEqualsToken:
case SyntaxKind.EqualsEqualsEqualsToken:
case SyntaxKind.ExclamationEqualsEqualsToken:
const leftIsLiteral = isLiteralType(leftType);
const rightIsLiteral = isLiteralType(rightType);
if (!leftIsLiteral || !rightIsLiteral) {
leftType = leftIsLiteral ? getBaseTypeOfLiteralType(leftType) : leftType;
rightType = rightIsLiteral ? getBaseTypeOfLiteralType(rightType) : rightType;
}
if (!isTypeEqualityComparableTo(leftType, rightType) && !isTypeEqualityComparableTo(rightType, leftType)) {
reportOperatorError();
}
return booleanType;
case SyntaxKind.InstanceOfKeyword:
return checkInstanceOfExpression(left, right, leftType, rightType);
case SyntaxKind.InKeyword:
return checkInExpression(left, right, leftType, rightType);
case SyntaxKind.AmpersandAmpersandToken:
return getTypeFacts(leftType) & TypeFacts.Truthy ?
getUnionType([extractDefinitelyFalsyTypes(strictNullChecks ? leftType : getBaseTypeOfLiteralType(rightType)), rightType]) :
leftType;
case SyntaxKind.BarBarToken:
return getTypeFacts(leftType) & TypeFacts.Falsy ?
getBestChoiceType(removeDefinitelyFalsyTypes(leftType), rightType) :
leftType;
case SyntaxKind.EqualsToken:
checkAssignmentOperator(rightType);
return getRegularTypeOfObjectLiteral(rightType);
case SyntaxKind.CommaToken:
if (!compilerOptions.allowUnreachableCode && isSideEffectFree(left) && !isEvalNode(right)) {
error(left, Diagnostics.Left_side_of_comma_operator_is_unused_and_has_no_side_effects);
}
return rightType;
}
function isEvalNode(node: Expression) {
return node.kind === SyntaxKind.Identifier && (node as Identifier).text === "eval";
}
// Return true if there was no error, false if there was an error.
function checkForDisallowedESSymbolOperand(operator: SyntaxKind): boolean {
const offendingSymbolOperand =
maybeTypeOfKind(leftType, TypeFlags.ESSymbol) ? left :
maybeTypeOfKind(rightType, TypeFlags.ESSymbol) ? right :
undefined;
if (offendingSymbolOperand) {
error(offendingSymbolOperand, Diagnostics.The_0_operator_cannot_be_applied_to_type_symbol, tokenToString(operator));
return false;
}
return true;
}
function getSuggestedBooleanOperator(operator: SyntaxKind): SyntaxKind {
switch (operator) {
case SyntaxKind.BarToken:
case SyntaxKind.BarEqualsToken:
return SyntaxKind.BarBarToken;
case SyntaxKind.CaretToken:
case SyntaxKind.CaretEqualsToken:
return SyntaxKind.ExclamationEqualsEqualsToken;
case SyntaxKind.AmpersandToken:
case SyntaxKind.AmpersandEqualsToken:
return SyntaxKind.AmpersandAmpersandToken;
default:
return undefined;
}
}
function checkAssignmentOperator(valueType: Type): void {
if (produceDiagnostics && isAssignmentOperator(operator)) {
// 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.
if (checkReferenceExpression(left, Diagnostics.The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access)) {
// to avoid cascading errors check assignability only if 'isReference' check succeeded and no errors were reported
checkTypeAssignableTo(valueType, leftType, left, /*headMessage*/ undefined);
}
}
}
function reportOperatorError() {
error(errorNode || operatorToken, Diagnostics.Operator_0_cannot_be_applied_to_types_1_and_2, tokenToString(operatorToken.kind), typeToString(leftType), typeToString(rightType));
}
}
function isYieldExpressionInClass(node: YieldExpression): boolean {
let current: Node = node;
let parent = node.parent;
while (parent) {
if (isFunctionLike(parent) && current === (<FunctionLikeDeclaration>parent).body) {
return false;
}
else if (isClassLike(current)) {
return true;
}
current = parent;
parent = parent.parent;
}
return false;
}
function checkYieldExpression(node: YieldExpression): Type {
// Grammar checking
if (produceDiagnostics) {
if (!(node.flags & NodeFlags.YieldContext) || isYieldExpressionInClass(node)) {
grammarErrorOnFirstToken(node, Diagnostics.A_yield_expression_is_only_allowed_in_a_generator_body);
}
if (isInParameterInitializerBeforeContainingFunction(node)) {
error(node, Diagnostics.yield_expressions_cannot_be_used_in_a_parameter_initializer);
}
}
if (node.expression) {
const func = getContainingFunction(node);
// If the user's code is syntactically correct, the func should always have a star. After all,
// we are in a yield context.
const functionFlags = func && getFunctionFlags(func);
if (node.asteriskToken) {
// Async generator functions prior to ESNext require the __await, __asyncDelegator,
// and __asyncValues helpers
if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.AsyncGenerator &&
languageVersion < ScriptTarget.ESNext) {
checkExternalEmitHelpers(node, ExternalEmitHelpers.AsyncDelegatorIncludes);
}
// Generator functions prior to ES2015 require the __values helper
if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Generator &&
languageVersion < ScriptTarget.ES2015 && compilerOptions.downlevelIteration) {
checkExternalEmitHelpers(node, ExternalEmitHelpers.Values);
}
}
if (functionFlags & FunctionFlags.Generator) {
const expressionType = checkExpressionCached(node.expression, /*contextualMapper*/ undefined);
let expressionElementType: Type;
const nodeIsYieldStar = !!node.asteriskToken;
if (nodeIsYieldStar) {
expressionElementType = checkIteratedTypeOrElementType(expressionType, node.expression, /*allowStringInput*/ false, (functionFlags & FunctionFlags.Async) !== 0);
}
// There is no point in doing an assignability check if the function
// has no explicit return type because the return type is directly computed
// from the yield expressions.
if (func.type) {
const signatureElementType = getIteratedTypeOfGenerator(getTypeFromTypeNode(func.type), (functionFlags & FunctionFlags.Async) !== 0) || anyType;
if (nodeIsYieldStar) {
checkTypeAssignableTo(
functionFlags & FunctionFlags.Async
? getAwaitedType(expressionElementType, node.expression, Diagnostics.Type_of_iterated_elements_of_a_yield_Asterisk_operand_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member)
: expressionElementType,
signatureElementType,
node.expression,
/*headMessage*/ undefined);
}
else {
checkTypeAssignableTo(
functionFlags & FunctionFlags.Async
? getAwaitedType(expressionType, node.expression, Diagnostics.Type_of_yield_operand_in_an_async_generator_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member)
: expressionType,
signatureElementType,
node.expression,
/*headMessage*/ undefined);
}
}
}
}
// Both yield and yield* expressions have type 'any'
return anyType;
}
function checkConditionalExpression(node: ConditionalExpression, checkMode?: CheckMode): Type {
checkExpression(node.condition);
const type1 = checkExpression(node.whenTrue, checkMode);
const type2 = checkExpression(node.whenFalse, checkMode);
return getBestChoiceType(type1, type2);
}
function checkLiteralExpression(node: Expression): Type {
if (node.kind === SyntaxKind.NumericLiteral) {
checkGrammarNumericLiteral(<NumericLiteral>node);
}
switch (node.kind) {
case SyntaxKind.StringLiteral:
return getFreshTypeOfLiteralType(getLiteralType((<LiteralExpression>node).text));
case SyntaxKind.NumericLiteral:
return getFreshTypeOfLiteralType(getLiteralType(+(<LiteralExpression>node).text));
case SyntaxKind.TrueKeyword:
return trueType;
case SyntaxKind.FalseKeyword:
return falseType;
}
}
function checkTemplateExpression(node: TemplateExpression): Type {
// We just want to check each expressions, but we are unconcerned with
// the type of each expression, as any value may be coerced into a string.
// It is worth asking whether this is what we really want though.
// A place where we actually *are* concerned with the expressions' types are
// in tagged templates.
forEach((<TemplateExpression>node).templateSpans, templateSpan => {
checkExpression(templateSpan.expression);
});
return stringType;
}
function checkExpressionWithContextualType(node: Expression, contextualType: Type, contextualMapper: TypeMapper): Type {
const saveContextualType = node.contextualType;
const saveContextualMapper = node.contextualMapper;
node.contextualType = contextualType;
node.contextualMapper = contextualMapper;
const checkMode = contextualMapper === identityMapper ? CheckMode.SkipContextSensitive :
contextualMapper ? CheckMode.Inferential : CheckMode.Normal;
const result = checkExpression(node, checkMode);
node.contextualType = saveContextualType;
node.contextualMapper = saveContextualMapper;
return result;
}
function checkExpressionCached(node: Expression, checkMode?: CheckMode): Type {
const links = getNodeLinks(node);
if (!links.resolvedType) {
// When computing a type that we're going to cache, we need to ignore any ongoing control flow
// analysis because variables may have transient types in indeterminable states. Moving flowLoopStart
// to the top of the stack ensures all transient types are computed from a known point.
const saveFlowLoopStart = flowLoopStart;
flowLoopStart = flowLoopCount;
links.resolvedType = checkExpression(node, checkMode);
flowLoopStart = saveFlowLoopStart;
}
return links.resolvedType;
}
function isTypeAssertion(node: Expression) {
node = skipParentheses(node);
return node.kind === SyntaxKind.TypeAssertionExpression || node.kind === SyntaxKind.AsExpression;
}
function checkDeclarationInitializer(declaration: VariableLikeDeclaration) {
const type = getTypeOfExpression(declaration.initializer, /*cache*/ true);
return getCombinedNodeFlags(declaration) & NodeFlags.Const ||
getCombinedModifierFlags(declaration) & ModifierFlags.Readonly && !isParameterPropertyDeclaration(declaration) ||
isTypeAssertion(declaration.initializer) ? type : getWidenedLiteralType(type);
}
function isLiteralContextualType(contextualType: Type) {
if (contextualType) {
if (contextualType.flags & TypeFlags.TypeVariable) {
const constraint = getBaseConstraintOfType(contextualType) || emptyObjectType;
// If the type parameter is constrained to the base primitive type we're checking for,
// consider this a literal context. For example, given a type parameter 'T extends string',
// this causes us to infer string literal types for T.
if (constraint.flags & (TypeFlags.String | TypeFlags.Number | TypeFlags.Boolean | TypeFlags.Enum)) {
return true;
}
contextualType = constraint;
}
return maybeTypeOfKind(contextualType, (TypeFlags.Literal | TypeFlags.Index));
}
return false;
}
function checkExpressionForMutableLocation(node: Expression, checkMode?: CheckMode): Type {
const type = checkExpression(node, checkMode);
return isTypeAssertion(node) || isLiteralContextualType(getContextualType(node)) ? type : getWidenedLiteralType(type);
}
function checkPropertyAssignment(node: PropertyAssignment, checkMode?: CheckMode): Type {
// Do not use hasDynamicName here, because that returns false for well known symbols.
// We want to perform checkComputedPropertyName for all computed properties, including
// well known symbols.
if (node.name.kind === SyntaxKind.ComputedPropertyName) {
checkComputedPropertyName(<ComputedPropertyName>node.name);
}
return checkExpressionForMutableLocation((<PropertyAssignment>node).initializer, checkMode);
}
function checkObjectLiteralMethod(node: MethodDeclaration, checkMode?: CheckMode): Type {
// Grammar checking
checkGrammarMethod(node);
// Do not use hasDynamicName here, because that returns false for well known symbols.
// We want to perform checkComputedPropertyName for all computed properties, including
// well known symbols.
if (node.name.kind === SyntaxKind.ComputedPropertyName) {
checkComputedPropertyName(<ComputedPropertyName>node.name);
}
const uninstantiatedType = checkFunctionExpressionOrObjectLiteralMethod(node, checkMode);
return instantiateTypeWithSingleGenericCallSignature(node, uninstantiatedType, checkMode);
}
function instantiateTypeWithSingleGenericCallSignature(node: Expression | MethodDeclaration, type: Type, checkMode?: CheckMode) {
if (checkMode === CheckMode.Inferential) {
const signature = getSingleCallSignature(type);
if (signature && signature.typeParameters) {
const contextualType = getApparentTypeOfContextualType(<Expression>node);
if (contextualType) {
const contextualSignature = getSingleCallSignature(contextualType);
if (contextualSignature && !contextualSignature.typeParameters) {
return getOrCreateTypeFromSignature(instantiateSignatureInContextOf(signature, contextualSignature, getContextualMapper(node)));
}
}
}
}
return type;
}
/**
* Returns the type of an expression. Unlike checkExpression, this function is simply concerned
* with computing the type and may not fully check all contained sub-expressions for errors.
* A cache argument of true indicates that if the function performs a full type check, it is ok
* to cache the result.
*/
function getTypeOfExpression(node: Expression, cache?: boolean) {
// Optimize for the common case of a call to a function with a single non-generic call
// signature where we can just fetch the return type without checking the arguments.
if (node.kind === SyntaxKind.CallExpression && (<CallExpression>node).expression.kind !== SyntaxKind.SuperKeyword && !isRequireCall(node, /*checkArgumentIsStringLiteral*/ true)) {
const funcType = checkNonNullExpression((<CallExpression>node).expression);
const signature = getSingleCallSignature(funcType);
if (signature && !signature.typeParameters) {
return getReturnTypeOfSignature(signature);
}
}
// Otherwise simply call checkExpression. Ideally, the entire family of checkXXX functions
// should have a parameter that indicates whether full error checking is required such that
// we can perform the optimizations locally.
return cache ? checkExpressionCached(node) : checkExpression(node);
}
/**
* Returns the type of an expression. Unlike checkExpression, this function is simply concerned
* with computing the type and may not fully check all contained sub-expressions for errors.
* It is intended for uses where you know there is no contextual type,
* and requesting the contextual type might cause a circularity or other bad behaviour.
* It sets the contextual type of the node to any before calling getTypeOfExpression.
*/
function getContextFreeTypeOfExpression(node: Expression) {
const saveContextualType = node.contextualType;
node.contextualType = anyType;
const type = getTypeOfExpression(node);
node.contextualType = saveContextualType;
return type;
}
// Checks an expression and returns its type. The contextualMapper parameter serves two purposes: When
// contextualMapper is not undefined and not equal to the identityMapper function object it indicates that the
// expression is being inferentially typed (section 4.15.2 in spec) and provides the type mapper to use in
// conjunction with the generic contextual type. When contextualMapper is equal to the identityMapper function
// object, it serves as an indicator that all contained function and arrow expressions should be considered to
// have the wildcard function type; this form of type check is used during overload resolution to exclude
// contextually typed function and arrow expressions in the initial phase.
function checkExpression(node: Expression | QualifiedName, checkMode?: CheckMode): Type {
let type: Type;
if (node.kind === SyntaxKind.QualifiedName) {
type = checkQualifiedName(<QualifiedName>node);
}
else {
const uninstantiatedType = checkExpressionWorker(<Expression>node, checkMode);
type = instantiateTypeWithSingleGenericCallSignature(<Expression>node, uninstantiatedType, checkMode);
}
if (isConstEnumObjectType(type)) {
// enum object type for const enums are only permitted in:
// - 'left' in property access
// - 'object' in indexed access
// - target in rhs of import statement
const ok =
(node.parent.kind === SyntaxKind.PropertyAccessExpression && (<PropertyAccessExpression>node.parent).expression === node) ||
(node.parent.kind === SyntaxKind.ElementAccessExpression && (<ElementAccessExpression>node.parent).expression === node) ||
((node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.QualifiedName) && isInRightSideOfImportOrExportAssignment(<Identifier>node));
if (!ok) {
error(node, Diagnostics.const_enums_can_only_be_used_in_property_or_index_access_expressions_or_the_right_hand_side_of_an_import_declaration_or_export_assignment);
}
}
return type;
}
function checkExpressionWorker(node: Expression, checkMode: CheckMode): Type {
switch (node.kind) {
case SyntaxKind.Identifier:
return checkIdentifier(<Identifier>node);
case SyntaxKind.ThisKeyword:
return checkThisExpression(node);
case SyntaxKind.SuperKeyword:
return checkSuperExpression(node);
case SyntaxKind.NullKeyword:
return nullWideningType;
case SyntaxKind.StringLiteral:
case SyntaxKind.NumericLiteral:
case SyntaxKind.TrueKeyword:
case SyntaxKind.FalseKeyword:
return checkLiteralExpression(node);
case SyntaxKind.TemplateExpression:
return checkTemplateExpression(<TemplateExpression>node);
case SyntaxKind.NoSubstitutionTemplateLiteral:
return stringType;
case SyntaxKind.RegularExpressionLiteral:
return globalRegExpType;
case SyntaxKind.ArrayLiteralExpression:
return checkArrayLiteral(<ArrayLiteralExpression>node, checkMode);
case SyntaxKind.ObjectLiteralExpression:
return checkObjectLiteral(<ObjectLiteralExpression>node, checkMode);
case SyntaxKind.PropertyAccessExpression:
return checkPropertyAccessExpression(<PropertyAccessExpression>node);
case SyntaxKind.ElementAccessExpression:
return checkIndexedAccess(<ElementAccessExpression>node);
case SyntaxKind.CallExpression:
case SyntaxKind.NewExpression:
return checkCallExpression(<CallExpression>node);
case SyntaxKind.TaggedTemplateExpression:
return checkTaggedTemplateExpression(<TaggedTemplateExpression>node);
case SyntaxKind.ParenthesizedExpression:
return checkExpression((<ParenthesizedExpression>node).expression, checkMode);
case SyntaxKind.ClassExpression:
return checkClassExpression(<ClassExpression>node);
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
return checkFunctionExpressionOrObjectLiteralMethod(<FunctionExpression>node, checkMode);
case SyntaxKind.TypeOfExpression:
return checkTypeOfExpression(<TypeOfExpression>node);
case SyntaxKind.TypeAssertionExpression:
case SyntaxKind.AsExpression:
return checkAssertion(<AssertionExpression>node);
case SyntaxKind.NonNullExpression:
return checkNonNullAssertion(<NonNullExpression>node);
case SyntaxKind.MetaProperty:
return checkMetaProperty(<MetaProperty>node);
case SyntaxKind.DeleteExpression:
return checkDeleteExpression(<DeleteExpression>node);
case SyntaxKind.VoidExpression:
return checkVoidExpression(<VoidExpression>node);
case SyntaxKind.AwaitExpression:
return checkAwaitExpression(<AwaitExpression>node);
case SyntaxKind.PrefixUnaryExpression:
return checkPrefixUnaryExpression(<PrefixUnaryExpression>node);
case SyntaxKind.PostfixUnaryExpression:
return checkPostfixUnaryExpression(<PostfixUnaryExpression>node);
case SyntaxKind.BinaryExpression:
return checkBinaryExpression(<BinaryExpression>node, checkMode);
case SyntaxKind.ConditionalExpression:
return checkConditionalExpression(<ConditionalExpression>node, checkMode);
case SyntaxKind.SpreadElement:
return checkSpreadExpression(<SpreadElement>node, checkMode);
case SyntaxKind.OmittedExpression:
return undefinedWideningType;
case SyntaxKind.YieldExpression:
return checkYieldExpression(<YieldExpression>node);
case SyntaxKind.JsxExpression:
return checkJsxExpression(<JsxExpression>node, checkMode);
case SyntaxKind.JsxElement:
return checkJsxElement(<JsxElement>node);
case SyntaxKind.JsxSelfClosingElement:
return checkJsxSelfClosingElement(<JsxSelfClosingElement>node);
case SyntaxKind.JsxAttributes:
return checkJsxAttributes(<JsxAttributes>node, checkMode);
case SyntaxKind.JsxOpeningElement:
Debug.fail("Shouldn't ever directly check a JsxOpeningElement");
}
return unknownType;
}
// DECLARATION AND STATEMENT TYPE CHECKING
function checkTypeParameter(node: TypeParameterDeclaration) {
// Grammar Checking
if (node.expression) {
grammarErrorOnFirstToken(node.expression, Diagnostics.Type_expected);
}
checkSourceElement(node.constraint);
checkSourceElement(node.default);
const typeParameter = getDeclaredTypeOfTypeParameter(getSymbolOfNode(node));
if (!hasNonCircularBaseConstraint(typeParameter)) {
error(node.constraint, Diagnostics.Type_parameter_0_has_a_circular_constraint, typeToString(typeParameter));
}
const constraintType = getConstraintOfTypeParameter(typeParameter);
const defaultType = getDefaultFromTypeParameter(typeParameter);
if (constraintType && defaultType) {
checkTypeAssignableTo(defaultType, getTypeWithThisArgument(constraintType, defaultType), node.default, Diagnostics.Type_0_does_not_satisfy_the_constraint_1);
}
if (produceDiagnostics) {
checkTypeNameIsReserved(node.name, Diagnostics.Type_parameter_name_cannot_be_0);
}
}
function checkParameter(node: ParameterDeclaration) {
// Grammar checking
// It is a SyntaxError if the Identifier "eval" or the Identifier "arguments" occurs as the
// Identifier in a PropertySetParameterList of a PropertyAssignment that is contained in strict code
// or if its FunctionBody is strict code(11.1.5).
// Grammar checking
checkGrammarDecorators(node) || checkGrammarModifiers(node);
checkVariableLikeDeclaration(node);
let func = getContainingFunction(node);
if (getModifierFlags(node) & ModifierFlags.ParameterPropertyModifier) {
func = getContainingFunction(node);
if (!(func.kind === SyntaxKind.Constructor && nodeIsPresent(func.body))) {
error(node, Diagnostics.A_parameter_property_is_only_allowed_in_a_constructor_implementation);
}
}
if (node.questionToken && isBindingPattern(node.name) && func.body) {
error(node, Diagnostics.A_binding_pattern_parameter_cannot_be_optional_in_an_implementation_signature);
}
if ((<Identifier>node.name).text === "this") {
if (indexOf(func.parameters, node) !== 0) {
error(node, Diagnostics.A_this_parameter_must_be_the_first_parameter);
}
if (func.kind === SyntaxKind.Constructor || func.kind === SyntaxKind.ConstructSignature || func.kind === SyntaxKind.ConstructorType) {
error(node, Diagnostics.A_constructor_cannot_have_a_this_parameter);
}
}
// Only check rest parameter type if it's not a binding pattern. Since binding patterns are
// not allowed in a rest parameter, we already have an error from checkGrammarParameterList.
if (node.dotDotDotToken && !isBindingPattern(node.name) && !isArrayType(getTypeOfSymbol(node.symbol))) {
error(node, Diagnostics.A_rest_parameter_must_be_of_an_array_type);
}
}
function getTypePredicateParameterIndex(parameterList: NodeArray<ParameterDeclaration>, parameter: Identifier): number {
if (parameterList) {
for (let i = 0; i < parameterList.length; i++) {
const param = parameterList[i];
if (param.name.kind === SyntaxKind.Identifier &&
(<Identifier>param.name).text === parameter.text) {
return i;
}
}
}
return -1;
}
function checkTypePredicate(node: TypePredicateNode): void {
const parent = getTypePredicateParent(node);
if (!parent) {
// The parent must not be valid.
error(node, Diagnostics.A_type_predicate_is_only_allowed_in_return_type_position_for_functions_and_methods);
return;
}
const typePredicate = getSignatureFromDeclaration(parent).typePredicate;
if (!typePredicate) {
return;
}
const { parameterName } = node;
if (isThisTypePredicate(typePredicate)) {
getTypeFromThisTypeNode(parameterName as ThisTypeNode);
}
else {
if (typePredicate.parameterIndex >= 0) {
if (parent.parameters[typePredicate.parameterIndex].dotDotDotToken) {
error(parameterName,
Diagnostics.A_type_predicate_cannot_reference_a_rest_parameter);
}
else {
const leadingError = chainDiagnosticMessages(/*details*/ undefined, Diagnostics.A_type_predicate_s_type_must_be_assignable_to_its_parameter_s_type);
checkTypeAssignableTo(typePredicate.type,
getTypeOfNode(parent.parameters[typePredicate.parameterIndex]),
node.type,
/*headMessage*/ undefined,
leadingError);
}
}
else if (parameterName) {
let hasReportedError = false;
for (const { name } of parent.parameters) {
if (isBindingPattern(name) &&
checkIfTypePredicateVariableIsDeclaredInBindingPattern(name, parameterName, typePredicate.parameterName)) {
hasReportedError = true;
break;
}
}
if (!hasReportedError) {
error(node.parameterName, Diagnostics.Cannot_find_parameter_0, typePredicate.parameterName);
}
}
}
}
function getTypePredicateParent(node: Node): SignatureDeclaration {
switch (node.parent.kind) {
case SyntaxKind.ArrowFunction:
case SyntaxKind.CallSignature:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.FunctionExpression:
case SyntaxKind.FunctionType:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
const parent = <SignatureDeclaration>node.parent;
if (node === parent.type) {
return parent;
}
}
}
function checkIfTypePredicateVariableIsDeclaredInBindingPattern(
pattern: BindingPattern,
predicateVariableNode: Node,
predicateVariableName: string) {
for (const element of pattern.elements) {
if (isOmittedExpression(element)) {
continue;
}
const name = element.name;
if (name.kind === SyntaxKind.Identifier &&
(<Identifier>name).text === predicateVariableName) {
error(predicateVariableNode,
Diagnostics.A_type_predicate_cannot_reference_element_0_in_a_binding_pattern,
predicateVariableName);
return true;
}
else if (name.kind === SyntaxKind.ArrayBindingPattern ||
name.kind === SyntaxKind.ObjectBindingPattern) {
if (checkIfTypePredicateVariableIsDeclaredInBindingPattern(
<BindingPattern>name,
predicateVariableNode,
predicateVariableName)) {
return true;
}
}
}
}
function checkSignatureDeclaration(node: SignatureDeclaration) {
// Grammar checking
if (node.kind === SyntaxKind.IndexSignature) {
checkGrammarIndexSignature(<SignatureDeclaration>node);
}
// TODO (yuisu): Remove this check in else-if when SyntaxKind.Construct is moved and ambient context is handled
else if (node.kind === SyntaxKind.FunctionType || node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.ConstructorType ||
node.kind === SyntaxKind.CallSignature || node.kind === SyntaxKind.Constructor ||
node.kind === SyntaxKind.ConstructSignature) {
checkGrammarFunctionLikeDeclaration(<FunctionLikeDeclaration>node);
}
const functionFlags = getFunctionFlags(<FunctionLikeDeclaration>node);
if (!(functionFlags & FunctionFlags.Invalid)) {
// Async generators prior to ESNext require the __await and __asyncGenerator helpers
if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.AsyncGenerator && languageVersion < ScriptTarget.ESNext) {
checkExternalEmitHelpers(node, ExternalEmitHelpers.AsyncGeneratorIncludes);
}
// Async functions prior to ES2017 require the __awaiter helper
if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Async && languageVersion < ScriptTarget.ES2017) {
checkExternalEmitHelpers(node, ExternalEmitHelpers.Awaiter);
}
// Generator functions, Async functions, and Async Generator functions prior to
// ES2015 require the __generator helper
if ((functionFlags & FunctionFlags.AsyncGenerator) !== FunctionFlags.Normal && languageVersion < ScriptTarget.ES2015) {
checkExternalEmitHelpers(node, ExternalEmitHelpers.Generator);
}
}
checkTypeParameters(node.typeParameters);
forEach(node.parameters, checkParameter);
if (node.type) {
checkSourceElement(node.type);
}
if (produceDiagnostics) {
checkCollisionWithArgumentsInGeneratedCode(node);
if (noImplicitAny && !node.type) {
switch (node.kind) {
case SyntaxKind.ConstructSignature:
error(node, Diagnostics.Construct_signature_which_lacks_return_type_annotation_implicitly_has_an_any_return_type);
break;
case SyntaxKind.CallSignature:
error(node, Diagnostics.Call_signature_which_lacks_return_type_annotation_implicitly_has_an_any_return_type);
break;
}
}
if (node.type) {
const functionFlags = getFunctionFlags(<FunctionDeclaration>node);
if ((functionFlags & (FunctionFlags.Invalid | FunctionFlags.Generator)) === FunctionFlags.Generator) {
const returnType = getTypeFromTypeNode(node.type);
if (returnType === voidType) {
error(node.type, Diagnostics.A_generator_cannot_have_a_void_type_annotation);
}
else {
const generatorElementType = getIteratedTypeOfGenerator(returnType, (functionFlags & FunctionFlags.Async) !== 0) || anyType;
const iterableIteratorInstantiation = functionFlags & FunctionFlags.Async
? createAsyncIterableIteratorType(generatorElementType) // AsyncGenerator function
: createIterableIteratorType(generatorElementType); // Generator function
// Naively, one could check that IterableIterator<any> is assignable to the return type annotation.
// However, that would not catch the error in the following case.
//
// interface BadGenerator extends Iterable<number>, Iterator<string> { }
// function* g(): BadGenerator { } // Iterable and Iterator have different types!
//
checkTypeAssignableTo(iterableIteratorInstantiation, returnType, node.type);
}
}
else if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Async) {
checkAsyncFunctionReturnType(<FunctionLikeDeclaration>node);
}
}
if (noUnusedIdentifiers && !(<FunctionDeclaration>node).body) {
checkUnusedTypeParameters(node);
}
}
}
function checkClassForDuplicateDeclarations(node: ClassLikeDeclaration) {
const enum Declaration {
Getter = 1,
Setter = 2,
Method = 4,
Property = Getter | Setter
}
const instanceNames = createMap<Declaration>();
const staticNames = createMap<Declaration>();
for (const member of node.members) {
if (member.kind === SyntaxKind.Constructor) {
for (const param of (member as ConstructorDeclaration).parameters) {
if (isParameterPropertyDeclaration(param)) {
addName(instanceNames, param.name, (param.name as Identifier).text, Declaration.Property);
}
}
}
else {
const isStatic = getModifierFlags(member) & ModifierFlags.Static;
const names = isStatic ? staticNames : instanceNames;
const memberName = member.name && getPropertyNameForPropertyNameNode(member.name);
if (memberName) {
switch (member.kind) {
case SyntaxKind.GetAccessor:
addName(names, member.name, memberName, Declaration.Getter);
break;
case SyntaxKind.SetAccessor:
addName(names, member.name, memberName, Declaration.Setter);
break;
case SyntaxKind.PropertyDeclaration:
addName(names, member.name, memberName, Declaration.Property);
break;
case SyntaxKind.MethodDeclaration:
addName(names, member.name, memberName, Declaration.Method);
break;
}
}
}
}
function addName(names: Map<Declaration>, location: Node, name: string, meaning: Declaration) {
const prev = names.get(name);
if (prev) {
if (prev & Declaration.Method) {
if (meaning !== Declaration.Method) {
error(location, Diagnostics.Duplicate_identifier_0, getTextOfNode(location));
}
}
else if (prev & meaning) {
error(location, Diagnostics.Duplicate_identifier_0, getTextOfNode(location));
}
else {
names.set(name, prev | meaning);
}
}
else {
names.set(name, meaning);
}
}
}
/**
* Static members being set on a constructor function may conflict with built-in properties
* of Function. Esp. in ECMAScript 5 there are non-configurable and non-writable
* built-in properties. This check issues a transpile error when a class has a static
* member with the same name as a non-writable built-in property.
*
* @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.3.3
* @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.3.5
* @see http://www.ecma-international.org/ecma-262/6.0/#sec-properties-of-the-function-constructor
* @see http://www.ecma-international.org/ecma-262/6.0/#sec-function-instances
*/
function checkClassForStaticPropertyNameConflicts(node: ClassLikeDeclaration) {
for (const member of node.members) {
const memberNameNode = member.name;
const isStatic = getModifierFlags(member) & ModifierFlags.Static;
if (isStatic && memberNameNode) {
const memberName = getPropertyNameForPropertyNameNode(memberNameNode);
switch (memberName) {
case "name":
case "length":
case "caller":
case "arguments":
case "prototype":
const message = Diagnostics.Static_property_0_conflicts_with_built_in_property_Function_0_of_constructor_function_1;
const className = getNameOfSymbol(getSymbolOfNode(node));
error(memberNameNode, message, memberName, className);
break;
}
}
}
}
function checkObjectTypeForDuplicateDeclarations(node: TypeLiteralNode | InterfaceDeclaration) {
const names = createMap<boolean>();
for (const member of node.members) {
if (member.kind === SyntaxKind.PropertySignature) {
let memberName: string;
switch (member.name.kind) {
case SyntaxKind.StringLiteral:
case SyntaxKind.NumericLiteral:
case SyntaxKind.Identifier:
memberName = (member.name as LiteralExpression | Identifier).text;
break;
default:
continue;
}
if (names.get(memberName)) {
error(getNameOfDeclaration(member.symbol.valueDeclaration), Diagnostics.Duplicate_identifier_0, memberName);
error(member.name, Diagnostics.Duplicate_identifier_0, memberName);
}
else {
names.set(memberName, true);
}
}
}
}
function checkTypeForDuplicateIndexSignatures(node: Node) {
if (node.kind === SyntaxKind.InterfaceDeclaration) {
const 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
const indexSymbol = getIndexSymbol(getSymbolOfNode(node));
if (indexSymbol) {
let seenNumericIndexer = false;
let seenStringIndexer = false;
for (const decl of indexSymbol.declarations) {
const declaration = <SignatureDeclaration>decl;
if (declaration.parameters.length === 1 && declaration.parameters[0].type) {
switch (declaration.parameters[0].type.kind) {
case SyntaxKind.StringKeyword:
if (!seenStringIndexer) {
seenStringIndexer = true;
}
else {
error(declaration, Diagnostics.Duplicate_string_index_signature);
}
break;
case SyntaxKind.NumberKeyword:
if (!seenNumericIndexer) {
seenNumericIndexer = true;
}
else {
error(declaration, Diagnostics.Duplicate_number_index_signature);
}
break;
}
}
}
}
}
function checkPropertyDeclaration(node: PropertyDeclaration) {
// Grammar checking
checkGrammarDecorators(node) || checkGrammarModifiers(node) || checkGrammarProperty(node) || checkGrammarComputedPropertyName(node.name);
checkVariableLikeDeclaration(node);
}
function checkMethodDeclaration(node: MethodDeclaration) {
// Grammar checking
checkGrammarMethod(node) || checkGrammarComputedPropertyName(node.name);
// Grammar checking for modifiers is done inside the function checkGrammarFunctionLikeDeclaration
checkFunctionOrMethodDeclaration(node);
// Abstract methods cannot have an implementation.
// Extra checks are to avoid reporting multiple errors relating to the "abstractness" of the node.
if (getModifierFlags(node) & ModifierFlags.Abstract && node.body) {
error(node, Diagnostics.Method_0_cannot_have_an_implementation_because_it_is_marked_abstract, declarationNameToString(node.name));
}
}
function checkConstructorDeclaration(node: ConstructorDeclaration) {
// Grammar check on signature of constructor and modifier of the constructor is done in checkSignatureDeclaration function.
checkSignatureDeclaration(node);
// Grammar check for checking only related to constructorDeclaration
checkGrammarConstructorTypeParameters(node) || checkGrammarConstructorTypeAnnotation(node);
checkSourceElement(node.body);
registerForUnusedIdentifiersCheck(node);
const symbol = getSymbolOfNode(node);
const firstDeclaration = getDeclarationOfKind(symbol, node.kind);
// Only type check the symbol once
if (node === firstDeclaration) {
checkFunctionOrConstructorSymbol(symbol);
}
// exit early in the case of signature - super checks are not relevant to them
if (nodeIsMissing(node.body)) {
return;
}
if (!produceDiagnostics) {
return;
}
function containsSuperCallAsComputedPropertyName(n: Declaration): boolean {
const name = getNameOfDeclaration(n);
return name && containsSuperCall(name);
}
function containsSuperCall(n: Node): boolean {
if (isSuperCall(n)) {
return true;
}
else if (isFunctionLike(n)) {
return false;
}
else if (isClassLike(n)) {
return forEach((<ClassLikeDeclaration>n).members, containsSuperCallAsComputedPropertyName);
}
return forEachChild(n, containsSuperCall);
}
function markThisReferencesAsErrors(n: Node): void {
if (n.kind === SyntaxKind.ThisKeyword) {
error(n, Diagnostics.this_cannot_be_referenced_in_current_location);
}
else if (n.kind !== SyntaxKind.FunctionExpression && n.kind !== SyntaxKind.FunctionDeclaration) {
forEachChild(n, markThisReferencesAsErrors);
}
}
function isInstancePropertyWithInitializer(n: Node): boolean {
return n.kind === SyntaxKind.PropertyDeclaration &&
!(getModifierFlags(n) & ModifierFlags.Static) &&
!!(<PropertyDeclaration>n).initializer;
}
// TS 1.0 spec (April 2014): 8.3.2
// Constructors of classes with no extends clause may not contain super calls, whereas
// constructors of derived classes must contain at least one super call somewhere in their function body.
const containingClassDecl = <ClassDeclaration>node.parent;
if (getClassExtendsHeritageClauseElement(containingClassDecl)) {
captureLexicalThis(node.parent, containingClassDecl);
const classExtendsNull = classDeclarationExtendsNull(containingClassDecl);
const superCall = getSuperCallInConstructor(node);
if (superCall) {
if (classExtendsNull) {
error(superCall, Diagnostics.A_constructor_cannot_contain_a_super_call_when_its_class_extends_null);
}
// The first statement in the body of a constructor (excluding prologue directives) 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.
const superCallShouldBeFirst =
forEach((<ClassDeclaration>node.parent).members, isInstancePropertyWithInitializer) ||
forEach(node.parameters, p => getModifierFlags(p) & ModifierFlags.ParameterPropertyModifier);
// Skip past any prologue directives to find the first statement
// to ensure that it was a super call.
if (superCallShouldBeFirst) {
const statements = (<Block>node.body).statements;
let superCallStatement: ExpressionStatement;
for (const statement of statements) {
if (statement.kind === SyntaxKind.ExpressionStatement && isSuperCall((<ExpressionStatement>statement).expression)) {
superCallStatement = <ExpressionStatement>statement;
break;
}
if (!isPrologueDirective(statement)) {
break;
}
}
if (!superCallStatement) {
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 if (!classExtendsNull) {
error(node, Diagnostics.Constructors_for_derived_classes_must_contain_a_super_call);
}
}
}
function checkAccessorDeclaration(node: AccessorDeclaration) {
if (produceDiagnostics) {
// Grammar checking accessors
checkGrammarFunctionLikeDeclaration(node) || checkGrammarAccessor(node) || checkGrammarComputedPropertyName(node.name);
checkDecorators(node);
checkSignatureDeclaration(node);
if (node.kind === SyntaxKind.GetAccessor) {
if (!isInAmbientContext(node) && nodeIsPresent(node.body) && (node.flags & NodeFlags.HasImplicitReturn)) {
if (!(node.flags & NodeFlags.HasExplicitReturn)) {
error(node.name, Diagnostics.A_get_accessor_must_return_a_value);
}
}
}
// Do not use hasDynamicName here, because that returns false for well known symbols.
// We want to perform checkComputedPropertyName for all computed properties, including
// well known symbols.
if (node.name.kind === SyntaxKind.ComputedPropertyName) {
checkComputedPropertyName(<ComputedPropertyName>node.name);
}
if (!hasDynamicName(node)) {
// TypeScript 1.0 spec (April 2014): 8.4.3
// Accessors for the same member name must specify the same accessibility.
const otherKind = node.kind === SyntaxKind.GetAccessor ? SyntaxKind.SetAccessor : SyntaxKind.GetAccessor;
const otherAccessor = getDeclarationOfKind<AccessorDeclaration>(node.symbol, otherKind);
if (otherAccessor) {
if ((getModifierFlags(node) & ModifierFlags.AccessibilityModifier) !== (getModifierFlags(otherAccessor) & ModifierFlags.AccessibilityModifier)) {
error(node.name, Diagnostics.Getter_and_setter_accessors_do_not_agree_in_visibility);
}
if (hasModifier(node, ModifierFlags.Abstract) !== hasModifier(otherAccessor, ModifierFlags.Abstract)) {
error(node.name, Diagnostics.Accessors_must_both_be_abstract_or_non_abstract);
}
// TypeScript 1.0 spec (April 2014): 4.5
// If both accessors include type annotations, the specified types must be identical.
checkAccessorDeclarationTypesIdentical(node, otherAccessor, getAnnotatedAccessorType, Diagnostics.get_and_set_accessor_must_have_the_same_type);
checkAccessorDeclarationTypesIdentical(node, otherAccessor, getThisTypeOfDeclaration, Diagnostics.get_and_set_accessor_must_have_the_same_this_type);
}
}
const returnType = getTypeOfAccessors(getSymbolOfNode(node));
if (node.kind === SyntaxKind.GetAccessor) {
checkAllCodePathsInNonVoidFunctionReturnOrThrow(node, returnType);
}
}
checkSourceElement(node.body);
registerForUnusedIdentifiersCheck(node);
}
function checkAccessorDeclarationTypesIdentical(first: AccessorDeclaration, second: AccessorDeclaration, getAnnotatedType: (a: AccessorDeclaration) => Type, message: DiagnosticMessage) {
const firstType = getAnnotatedType(first);
const secondType = getAnnotatedType(second);
if (firstType && secondType && !isTypeIdenticalTo(firstType, secondType)) {
error(first, message);
}
}
function checkMissingDeclaration(node: Node) {
checkDecorators(node);
}
function checkTypeArgumentConstraints(typeParameters: TypeParameter[], typeArgumentNodes: TypeNode[]): boolean {
const minTypeArgumentCount = getMinTypeArgumentCount(typeParameters);
let typeArguments: Type[];
let mapper: TypeMapper;
let result = true;
for (let i = 0; i < typeParameters.length; i++) {
const constraint = getConstraintOfTypeParameter(typeParameters[i]);
if (constraint) {
if (!typeArguments) {
typeArguments = fillMissingTypeArguments(map(typeArgumentNodes, getTypeFromTypeNode), typeParameters, minTypeArgumentCount);
mapper = createTypeMapper(typeParameters, typeArguments);
}
const typeArgument = typeArguments[i];
result = result && checkTypeAssignableTo(
typeArgument,
getTypeWithThisArgument(instantiateType(constraint, mapper), typeArgument),
typeArgumentNodes[i],
Diagnostics.Type_0_does_not_satisfy_the_constraint_1);
}
}
return result;
}
function checkTypeReferenceNode(node: TypeReferenceNode | ExpressionWithTypeArguments) {
checkGrammarTypeArguments(node, node.typeArguments);
const type = getTypeFromTypeReference(node);
if (type !== unknownType) {
if (node.typeArguments) {
// Do type argument local checks only if referenced type is successfully resolved
forEach(node.typeArguments, checkSourceElement);
if (produceDiagnostics) {
const symbol = getNodeLinks(node).resolvedSymbol;
const typeParameters = symbol.flags & SymbolFlags.TypeAlias ? getSymbolLinks(symbol).typeParameters : (<TypeReference>type).target.localTypeParameters;
checkTypeArgumentConstraints(typeParameters, node.typeArguments);
}
}
if (type.flags & TypeFlags.Enum && getNodeLinks(node).resolvedSymbol.flags & SymbolFlags.EnumMember) {
error(node, Diagnostics.Enum_type_0_has_members_with_initializers_that_are_not_literals, typeToString(type));
}
}
}
function checkTypeQuery(node: TypeQueryNode) {
getTypeFromTypeQueryNode(node);
}
function checkTypeLiteral(node: TypeLiteralNode) {
forEach(node.members, checkSourceElement);
if (produceDiagnostics) {
const type = getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node);
checkIndexConstraints(type);
checkTypeForDuplicateIndexSignatures(node);
checkObjectTypeForDuplicateDeclarations(node);
}
}
function checkArrayType(node: ArrayTypeNode) {
checkSourceElement(node.elementType);
}
function checkTupleType(node: TupleTypeNode) {
// Grammar checking
const hasErrorFromDisallowedTrailingComma = checkGrammarForDisallowedTrailingComma(node.elementTypes);
if (!hasErrorFromDisallowedTrailingComma && node.elementTypes.length === 0) {
grammarErrorOnNode(node, Diagnostics.A_tuple_type_element_list_cannot_be_empty);
}
forEach(node.elementTypes, checkSourceElement);
}
function checkUnionOrIntersectionType(node: UnionOrIntersectionTypeNode) {
forEach(node.types, checkSourceElement);
}
function checkIndexedAccessIndexType(type: Type, accessNode: ElementAccessExpression | IndexedAccessTypeNode) {
if (!(type.flags & TypeFlags.IndexedAccess)) {
return type;
}
// Check if the index type is assignable to 'keyof T' for the object type.
const objectType = (<IndexedAccessType>type).objectType;
const indexType = (<IndexedAccessType>type).indexType;
if (isTypeAssignableTo(indexType, getIndexType(objectType))) {
return type;
}
// Check if we're indexing with a numeric type and the object type is a generic
// type with a constraint that has a numeric index signature.
if (maybeTypeOfKind(objectType, TypeFlags.TypeVariable) && isTypeOfKind(indexType, TypeFlags.NumberLike)) {
const constraint = getBaseConstraintOfType(objectType);
if (constraint && getIndexInfoOfType(constraint, IndexKind.Number)) {
return type;
}
}
error(accessNode, Diagnostics.Type_0_cannot_be_used_to_index_type_1, typeToString(indexType), typeToString(objectType));
return type;
}
function checkIndexedAccessType(node: IndexedAccessTypeNode) {
checkIndexedAccessIndexType(getTypeFromIndexedAccessTypeNode(node), node);
}
function checkMappedType(node: MappedTypeNode) {
checkSourceElement(node.typeParameter);
checkSourceElement(node.type);
const type = <MappedType>getTypeFromMappedTypeNode(node);
const constraintType = getConstraintTypeFromMappedType(type);
checkTypeAssignableTo(constraintType, stringType, node.typeParameter.constraint);
}
function isPrivateWithinAmbient(node: Node): boolean {
return (getModifierFlags(node) & ModifierFlags.Private) && isInAmbientContext(node);
}
function getEffectiveDeclarationFlags(n: Node, flagsToCheck: ModifierFlags): ModifierFlags {
let flags = getCombinedModifierFlags(n);
// children of classes (even ambient classes) should not be marked as ambient or export
// because those flags have no useful semantics there.
if (n.parent.kind !== SyntaxKind.InterfaceDeclaration &&
n.parent.kind !== SyntaxKind.ClassDeclaration &&
n.parent.kind !== SyntaxKind.ClassExpression &&
isInAmbientContext(n)) {
if (!(flags & ModifierFlags.Ambient)) {
// It is nested in an ambient context, which means it is automatically exported
flags |= ModifierFlags.Export;
}
flags |= ModifierFlags.Ambient;
}
return flags & flagsToCheck;
}
function checkFunctionOrConstructorSymbol(symbol: Symbol): void {
if (!produceDiagnostics) {
return;
}
function getCanonicalOverload(overloads: Declaration[], implementation: FunctionLikeDeclaration) {
// Consider the canonical set of flags to be the flags of the bodyDeclaration or the first declaration
// Error on all deviations from this canonical set of flags
// The caveat is that if some overloads are defined in lib.d.ts, we don't want to
// report the errors on those. To achieve this, we will say that the implementation is
// the canonical signature only if it is in the same container as the first overload
const implementationSharesContainerWithFirstOverload = implementation !== undefined && implementation.parent === overloads[0].parent;
return implementationSharesContainerWithFirstOverload ? implementation : overloads[0];
}
function checkFlagAgreementBetweenOverloads(overloads: Declaration[], implementation: FunctionLikeDeclaration, flagsToCheck: ModifierFlags, someOverloadFlags: ModifierFlags, allOverloadFlags: ModifierFlags): void {
// Error if some overloads have a flag that is not shared by all overloads. To find the
// deviations, we XOR someOverloadFlags with allOverloadFlags
const someButNotAllOverloadFlags = someOverloadFlags ^ allOverloadFlags;
if (someButNotAllOverloadFlags !== 0) {
const canonicalFlags = getEffectiveDeclarationFlags(getCanonicalOverload(overloads, implementation), flagsToCheck);
forEach(overloads, o => {
const deviation = getEffectiveDeclarationFlags(o, flagsToCheck) ^ canonicalFlags;
if (deviation & ModifierFlags.Export) {
error(getNameOfDeclaration(o), Diagnostics.Overload_signatures_must_all_be_exported_or_non_exported);
}
else if (deviation & ModifierFlags.Ambient) {
error(getNameOfDeclaration(o), Diagnostics.Overload_signatures_must_all_be_ambient_or_non_ambient);
}
else if (deviation & (ModifierFlags.Private | ModifierFlags.Protected)) {
error(getNameOfDeclaration(o) || o, Diagnostics.Overload_signatures_must_all_be_public_private_or_protected);
}
else if (deviation & ModifierFlags.Abstract) {
error(getNameOfDeclaration(o), Diagnostics.Overload_signatures_must_all_be_abstract_or_non_abstract);
}
});
}
}
function checkQuestionTokenAgreementBetweenOverloads(overloads: Declaration[], implementation: FunctionLikeDeclaration, someHaveQuestionToken: boolean, allHaveQuestionToken: boolean): void {
if (someHaveQuestionToken !== allHaveQuestionToken) {
const canonicalHasQuestionToken = hasQuestionToken(getCanonicalOverload(overloads, implementation));
forEach(overloads, o => {
const deviation = hasQuestionToken(o) !== canonicalHasQuestionToken;
if (deviation) {
error(getNameOfDeclaration(o), Diagnostics.Overload_signatures_must_all_be_optional_or_required);
}
});
}
}
const flagsToCheck: ModifierFlags = ModifierFlags.Export | ModifierFlags.Ambient | ModifierFlags.Private | ModifierFlags.Protected | ModifierFlags.Abstract;
let someNodeFlags: ModifierFlags = ModifierFlags.None;
let allNodeFlags = flagsToCheck;
let someHaveQuestionToken = false;
let allHaveQuestionToken = true;
let hasOverloads = false;
let bodyDeclaration: FunctionLikeDeclaration;
let lastSeenNonAmbientDeclaration: FunctionLikeDeclaration;
let previousDeclaration: FunctionLikeDeclaration;
const declarations = symbol.declarations;
const isConstructor = (symbol.flags & SymbolFlags.Constructor) !== 0;
function reportImplementationExpectedError(node: FunctionLikeDeclaration): void {
if (node.name && nodeIsMissing(node.name)) {
return;
}
let seen = false;
const subsequentNode = forEachChild(node.parent, c => {
if (seen) {
return c;
}
else {
seen = c === node;
}
});
// We may be here because of some extra nodes between overloads that could not be parsed into a valid node.
// In this case the subsequent node is not really consecutive (.pos !== node.end), and we must ignore it here.
if (subsequentNode && subsequentNode.pos === node.end) {
if (subsequentNode.kind === node.kind) {
const errorNode: Node = (<FunctionLikeDeclaration>subsequentNode).name || subsequentNode;
// TODO(jfreeman): These are methods, so handle computed name case
if (node.name && (<FunctionLikeDeclaration>subsequentNode).name && (<Identifier>node.name).text === (<Identifier>(<FunctionLikeDeclaration>subsequentNode).name).text) {
const reportError =
(node.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.MethodSignature) &&
(getModifierFlags(node) & ModifierFlags.Static) !== (getModifierFlags(subsequentNode) & ModifierFlags.Static);
// we can get here in two cases
// 1. mixed static and instance class members
// 2. something with the same name was defined before the set of overloads that prevents them from merging
// here we'll report error only for the first case since for second we should already report error in binder
if (reportError) {
const diagnostic = getModifierFlags(node) & ModifierFlags.Static ? Diagnostics.Function_overload_must_be_static : Diagnostics.Function_overload_must_not_be_static;
error(errorNode, diagnostic);
}
return;
}
else if (nodeIsPresent((<FunctionLikeDeclaration>subsequentNode).body)) {
error(errorNode, Diagnostics.Function_implementation_name_must_be_0, declarationNameToString(node.name));
return;
}
}
}
const errorNode: Node = node.name || node;
if (isConstructor) {
error(errorNode, Diagnostics.Constructor_implementation_is_missing);
}
else {
// Report different errors regarding non-consecutive blocks of declarations depending on whether
// the node in question is abstract.
if (getModifierFlags(node) & ModifierFlags.Abstract) {
error(errorNode, Diagnostics.All_declarations_of_an_abstract_method_must_be_consecutive);
}
else {
error(errorNode, Diagnostics.Function_implementation_is_missing_or_not_immediately_following_the_declaration);
}
}
}
let duplicateFunctionDeclaration = false;
let multipleConstructorImplementation = false;
for (const current of declarations) {
const node = <FunctionLikeDeclaration>current;
const inAmbientContext = isInAmbientContext(node);
const inAmbientContextOrInterface = node.parent.kind === SyntaxKind.InterfaceDeclaration || node.parent.kind === SyntaxKind.TypeLiteral || inAmbientContext;
if (inAmbientContextOrInterface) {
// check if declarations are consecutive only if they are non-ambient
// 1. ambient declarations can be interleaved
// i.e. this is legal
// declare function foo();
// declare function bar();
// declare function foo();
// 2. mixing ambient and non-ambient declarations is a separate error that will be reported - do not want to report an extra one
previousDeclaration = undefined;
}
if (node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.MethodSignature || node.kind === SyntaxKind.Constructor) {
const currentNodeFlags = getEffectiveDeclarationFlags(node, flagsToCheck);
someNodeFlags |= currentNodeFlags;
allNodeFlags &= currentNodeFlags;
someHaveQuestionToken = someHaveQuestionToken || hasQuestionToken(node);
allHaveQuestionToken = allHaveQuestionToken && hasQuestionToken(node);
if (nodeIsPresent(node.body) && bodyDeclaration) {
if (isConstructor) {
multipleConstructorImplementation = true;
}
else {
duplicateFunctionDeclaration = true;
}
}
else if (previousDeclaration && previousDeclaration.parent === node.parent && previousDeclaration.end !== node.pos) {
reportImplementationExpectedError(previousDeclaration);
}
if (nodeIsPresent(node.body)) {
if (!bodyDeclaration) {
bodyDeclaration = node;
}
}
else {
hasOverloads = true;
}
previousDeclaration = node;
if (!inAmbientContextOrInterface) {
lastSeenNonAmbientDeclaration = node;
}
}
}
if (multipleConstructorImplementation) {
forEach(declarations, declaration => {
error(declaration, Diagnostics.Multiple_constructor_implementations_are_not_allowed);
});
}
if (duplicateFunctionDeclaration) {
forEach(declarations, declaration => {
error(getNameOfDeclaration(declaration), Diagnostics.Duplicate_function_implementation);
});
}
// Abstract methods can't have an implementation -- in particular, they don't need one.
if (lastSeenNonAmbientDeclaration && !lastSeenNonAmbientDeclaration.body &&
!(getModifierFlags(lastSeenNonAmbientDeclaration) & ModifierFlags.Abstract) && !lastSeenNonAmbientDeclaration.questionToken) {
reportImplementationExpectedError(lastSeenNonAmbientDeclaration);
}
if (hasOverloads) {
checkFlagAgreementBetweenOverloads(declarations, bodyDeclaration, flagsToCheck, someNodeFlags, allNodeFlags);
checkQuestionTokenAgreementBetweenOverloads(declarations, bodyDeclaration, someHaveQuestionToken, allHaveQuestionToken);
if (bodyDeclaration) {
const signatures = getSignaturesOfSymbol(symbol);
const bodySignature = getSignatureFromDeclaration(bodyDeclaration);
for (const signature of signatures) {
if (!isImplementationCompatibleWithOverload(bodySignature, signature)) {
error(signature.declaration, Diagnostics.Overload_signature_is_not_compatible_with_function_implementation);
break;
}
}
}
}
}
function checkExportsOnMergedDeclarations(node: Node): void {
if (!produceDiagnostics) {
return;
}
// if localSymbol is defined on node then node itself is exported - check is required
let symbol = node.localSymbol;
if (!symbol) {
// local symbol is undefined => this declaration is non-exported.
// however symbol might contain other declarations that are exported
symbol = getSymbolOfNode(node);
if (!(symbol.flags & SymbolFlags.Export)) {
// this is a pure local symbol (all declarations are non-exported) - no need to check anything
return;
}
}
// run the check only for the first declaration in the list
if (getDeclarationOfKind(symbol, node.kind) !== node) {
return;
}
// we use SymbolFlags.ExportValue, SymbolFlags.ExportType and SymbolFlags.ExportNamespace
// to denote disjoint declarationSpaces (without making new enum type).
let exportedDeclarationSpaces = SymbolFlags.None;
let nonExportedDeclarationSpaces = SymbolFlags.None;
let defaultExportedDeclarationSpaces = SymbolFlags.None;
for (const d of symbol.declarations) {
const declarationSpaces = getDeclarationSpaces(d);
const effectiveDeclarationFlags = getEffectiveDeclarationFlags(d, ModifierFlags.Export | ModifierFlags.Default);
if (effectiveDeclarationFlags & ModifierFlags.Export) {
if (effectiveDeclarationFlags & ModifierFlags.Default) {
defaultExportedDeclarationSpaces |= declarationSpaces;
}
else {
exportedDeclarationSpaces |= declarationSpaces;
}
}
else {
nonExportedDeclarationSpaces |= declarationSpaces;
}
}
// Spaces for anything not declared a 'default export'.
const nonDefaultExportedDeclarationSpaces = exportedDeclarationSpaces | nonExportedDeclarationSpaces;
const commonDeclarationSpacesForExportsAndLocals = exportedDeclarationSpaces & nonExportedDeclarationSpaces;
const commonDeclarationSpacesForDefaultAndNonDefault = defaultExportedDeclarationSpaces & nonDefaultExportedDeclarationSpaces;
if (commonDeclarationSpacesForExportsAndLocals || commonDeclarationSpacesForDefaultAndNonDefault) {
// declaration spaces for exported and non-exported declarations intersect
for (const d of symbol.declarations) {
const declarationSpaces = getDeclarationSpaces(d);
const name = getNameOfDeclaration(d);
// Only error on the declarations that contributed to the intersecting spaces.
if (declarationSpaces & commonDeclarationSpacesForDefaultAndNonDefault) {
error(name, Diagnostics.Merged_declaration_0_cannot_include_a_default_export_declaration_Consider_adding_a_separate_export_default_0_declaration_instead, declarationNameToString(name));
}
else if (declarationSpaces & commonDeclarationSpacesForExportsAndLocals) {
error(name, Diagnostics.Individual_declarations_in_merged_declaration_0_must_be_all_exported_or_all_local, declarationNameToString(name));
}
}
}
function getDeclarationSpaces(d: Declaration): SymbolFlags {
switch (d.kind) {
case SyntaxKind.InterfaceDeclaration:
return SymbolFlags.ExportType;
case SyntaxKind.ModuleDeclaration:
return isAmbientModule(d) || getModuleInstanceState(d) !== ModuleInstanceState.NonInstantiated
? SymbolFlags.ExportNamespace | SymbolFlags.ExportValue
: SymbolFlags.ExportNamespace;
case SyntaxKind.ClassDeclaration:
case SyntaxKind.EnumDeclaration:
return SymbolFlags.ExportType | SymbolFlags.ExportValue;
case SyntaxKind.ImportEqualsDeclaration:
let result: SymbolFlags = 0;
const target = resolveAlias(getSymbolOfNode(d));
forEach(target.declarations, d => { result |= getDeclarationSpaces(d); });
return result;
default:
return SymbolFlags.ExportValue;
}
}
}
function getAwaitedTypeOfPromise(type: Type, errorNode?: Node, diagnosticMessage?: DiagnosticMessage): Type | undefined {
const promisedType = getPromisedTypeOfPromise(type, errorNode);
return promisedType && getAwaitedType(promisedType, errorNode, diagnosticMessage);
}
/**
* Gets the "promised type" of a promise.
* @param type The type of the promise.
* @remarks The "promised type" of a type is the type of the "value" parameter of the "onfulfilled" callback.
*/
function getPromisedTypeOfPromise(promise: Type, errorNode?: Node): Type {
//
// { // promise
// then( // thenFunction
// onfulfilled: ( // onfulfilledParameterType
// value: T // valueParameterType
// ) => any
// ): any;
// }
//
if (isTypeAny(promise)) {
return undefined;
}
const typeAsPromise = <PromiseOrAwaitableType>promise;
if (typeAsPromise.promisedTypeOfPromise) {
return typeAsPromise.promisedTypeOfPromise;
}
if (isReferenceToType(promise, getGlobalPromiseType(/*reportErrors*/ false))) {
return typeAsPromise.promisedTypeOfPromise = (<GenericType>promise).typeArguments[0];
}
const thenFunction = getTypeOfPropertyOfType(promise, "then");
if (isTypeAny(thenFunction)) {
return undefined;
}
const thenSignatures = thenFunction ? getSignaturesOfType(thenFunction, SignatureKind.Call) : emptyArray;
if (thenSignatures.length === 0) {
if (errorNode) {
error(errorNode, Diagnostics.A_promise_must_have_a_then_method);
}
return undefined;
}
const onfulfilledParameterType = getTypeWithFacts(getUnionType(map(thenSignatures, getTypeOfFirstParameterOfSignature)), TypeFacts.NEUndefinedOrNull);
if (isTypeAny(onfulfilledParameterType)) {
return undefined;
}
const onfulfilledParameterSignatures = getSignaturesOfType(onfulfilledParameterType, SignatureKind.Call);
if (onfulfilledParameterSignatures.length === 0) {
if (errorNode) {
error(errorNode, Diagnostics.The_first_parameter_of_the_then_method_of_a_promise_must_be_a_callback);
}
return undefined;
}
return typeAsPromise.promisedTypeOfPromise = getUnionType(map(onfulfilledParameterSignatures, getTypeOfFirstParameterOfSignature), /*subtypeReduction*/ true);
}
/**
* Gets the "awaited type" of a type.
* @param type The type to await.
* @remarks The "awaited type" of an expression is its "promised type" if the expression is a
* Promise-like type; otherwise, it is the type of the expression. This is used to reflect
* The runtime behavior of the `await` keyword.
*/
function checkAwaitedType(type: Type, errorNode: Node, diagnosticMessage: DiagnosticMessage): Type {
return getAwaitedType(type, errorNode, diagnosticMessage) || unknownType;
}
function getAwaitedType(type: Type, errorNode?: Node, diagnosticMessage?: DiagnosticMessage): Type | undefined {
const typeAsAwaitable = <PromiseOrAwaitableType>type;
if (typeAsAwaitable.awaitedTypeOfType) {
return typeAsAwaitable.awaitedTypeOfType;
}
if (isTypeAny(type)) {
return typeAsAwaitable.awaitedTypeOfType = type;
}
if (type.flags & TypeFlags.Union) {
let types: Type[];
for (const constituentType of (<UnionType>type).types) {
types = append(types, getAwaitedType(constituentType, errorNode, diagnosticMessage));
}
if (!types) {
return undefined;
}
return typeAsAwaitable.awaitedTypeOfType = getUnionType(types, /*subtypeReduction*/ true);
}
const promisedType = getPromisedTypeOfPromise(type);
if (promisedType) {
if (type.id === promisedType.id || indexOf(awaitedTypeStack, promisedType.id) >= 0) {
// Verify that we don't have a bad actor in the form of a promise whose
// promised type is the same as the promise type, or a mutually recursive
// promise. If so, we return undefined as we cannot guess the shape. If this
// were the actual case in the JavaScript, this Promise would never resolve.
//
// An example of a bad actor with a singly-recursive promise type might
// be:
//
// interface BadPromise {
// then(
// onfulfilled: (value: BadPromise) => any,
// onrejected: (error: any) => any): BadPromise;
// }
// The above interface will pass the PromiseLike check, and return a
// promised type of `BadPromise`. Since this is a self reference, we
// don't want to keep recursing ad infinitum.
//
// An example of a bad actor in the form of a mutually-recursive
// promise type might be:
//
// interface BadPromiseA {
// then(
// onfulfilled: (value: BadPromiseB) => any,
// onrejected: (error: any) => any): BadPromiseB;
// }
//
// interface BadPromiseB {
// then(
// onfulfilled: (value: BadPromiseA) => any,
// onrejected: (error: any) => any): BadPromiseA;
// }
//
if (errorNode) {
error(errorNode, Diagnostics.Type_is_referenced_directly_or_indirectly_in_the_fulfillment_callback_of_its_own_then_method);
}
return undefined;
}
// Keep track of the type we're about to unwrap to avoid bad recursive promise types.
// See the comments above for more information.
awaitedTypeStack.push(type.id);
const awaitedType = getAwaitedType(promisedType, errorNode, diagnosticMessage);
awaitedTypeStack.pop();
if (!awaitedType) {
return undefined;
}
return typeAsAwaitable.awaitedTypeOfType = awaitedType;
}
// The type was not a promise, so it could not be unwrapped any further.
// As long as the type does not have a callable "then" property, it is
// safe to return the type; otherwise, an error will be reported in
// the call to getNonThenableType and we will return undefined.
//
// An example of a non-promise "thenable" might be:
//
// await { then(): void {} }
//
// The "thenable" does not match the minimal definition for a promise. When
// a Promise/A+-compatible or ES6 promise tries to adopt this value, the promise
// will never settle. We treat this as an error to help flag an early indicator
// of a runtime problem. If the user wants to return this value from an async
// function, they would need to wrap it in some other value. If they want it to
// be treated as a promise, they can cast to <any>.
const thenFunction = getTypeOfPropertyOfType(type, "then");
if (thenFunction && getSignaturesOfType(thenFunction, SignatureKind.Call).length > 0) {
if (errorNode) {
Debug.assert(!!diagnosticMessage);
error(errorNode, diagnosticMessage);
}
return undefined;
}
return typeAsAwaitable.awaitedTypeOfType = type;
}
/**
* Checks the return type of an async function to ensure it is a compatible
* Promise implementation.
*
* This checks that an async function has a valid Promise-compatible return type,
* and returns the *awaited type* of the promise. An async function has a valid
* Promise-compatible return type if the resolved value of the return type has a
* construct signature that takes in an `initializer` function that in turn supplies
* a `resolve` function as one of its arguments and results in an object with a
* callable `then` signature.
*
* @param node The signature to check
*/
function checkAsyncFunctionReturnType(node: FunctionLikeDeclaration): Type {
// As part of our emit for an async function, we will need to emit the entity name of
// the return type annotation as an expression. To meet the necessary runtime semantics
// for __awaiter, we must also check that the type of the declaration (e.g. the static
// side or "constructor" of the promise type) is compatible `PromiseConstructorLike`.
//
// An example might be (from lib.es6.d.ts):
//
// interface Promise<T> { ... }
// interface PromiseConstructor {
// new <T>(...): Promise<T>;
// }
// declare var Promise: PromiseConstructor;
//
// When an async function declares a return type annotation of `Promise<T>`, we
// need to get the type of the `Promise` variable declaration above, which would
// be `PromiseConstructor`.
//
// The same case applies to a class:
//
// declare class Promise<T> {
// constructor(...);
// then<U>(...): Promise<U>;
// }
//
const returnType = getTypeFromTypeNode(node.type);
if (languageVersion >= ScriptTarget.ES2015) {
if (returnType === unknownType) {
return unknownType;
}
const globalPromiseType = getGlobalPromiseType(/*reportErrors*/ true);
if (globalPromiseType !== emptyGenericType && !isReferenceToType(returnType, globalPromiseType)) {
// The promise type was not a valid type reference to the global promise type, so we
// report an error and return the unknown type.
error(node.type, Diagnostics.The_return_type_of_an_async_function_or_method_must_be_the_global_Promise_T_type);
return unknownType;
}
}
else {
// Always mark the type node as referenced if it points to a value
markTypeNodeAsReferenced(node.type);
if (returnType === unknownType) {
return unknownType;
}
const promiseConstructorName = getEntityNameFromTypeNode(node.type);
if (promiseConstructorName === undefined) {
error(node.type, Diagnostics.Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value, typeToString(returnType));
return unknownType;
}
const promiseConstructorSymbol = resolveEntityName(promiseConstructorName, SymbolFlags.Value, /*ignoreErrors*/ true);
const promiseConstructorType = promiseConstructorSymbol ? getTypeOfSymbol(promiseConstructorSymbol) : unknownType;
if (promiseConstructorType === unknownType) {
if (promiseConstructorName.kind === SyntaxKind.Identifier && promiseConstructorName.text === "Promise" && getTargetType(returnType) === getGlobalPromiseType(/*reportErrors*/ false)) {
error(node.type, Diagnostics.An_async_function_or_method_in_ES5_SlashES3_requires_the_Promise_constructor_Make_sure_you_have_a_declaration_for_the_Promise_constructor_or_include_ES2015_in_your_lib_option);
}
else {
error(node.type, Diagnostics.Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value, entityNameToString(promiseConstructorName));
}
return unknownType;
}
const globalPromiseConstructorLikeType = getGlobalPromiseConstructorLikeType(/*reportErrors*/ true);
if (globalPromiseConstructorLikeType === emptyObjectType) {
// If we couldn't resolve the global PromiseConstructorLike type we cannot verify
// compatibility with __awaiter.
error(node.type, Diagnostics.Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value, entityNameToString(promiseConstructorName));
return unknownType;
}
if (!checkTypeAssignableTo(promiseConstructorType, globalPromiseConstructorLikeType, node.type,
Diagnostics.Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value)) {
return unknownType;
}
// Verify there is no local declaration that could collide with the promise constructor.
const rootName = promiseConstructorName && getFirstIdentifier(promiseConstructorName);
const collidingSymbol = getSymbol(node.locals, rootName.text, SymbolFlags.Value);
if (collidingSymbol) {
error(collidingSymbol.valueDeclaration, Diagnostics.Duplicate_identifier_0_Compiler_uses_declaration_1_to_support_async_functions,
rootName.text,
entityNameToString(promiseConstructorName));
return unknownType;
}
}
// Get and return the awaited type of the return type.
return checkAwaitedType(returnType, node, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
}
/** Check a decorator */
function checkDecorator(node: Decorator): void {
const signature = getResolvedSignature(node);
const returnType = getReturnTypeOfSignature(signature);
if (returnType.flags & TypeFlags.Any) {
return;
}
let expectedReturnType: Type;
const headMessage = getDiagnosticHeadMessageForDecoratorResolution(node);
let errorInfo: DiagnosticMessageChain;
switch (node.parent.kind) {
case SyntaxKind.ClassDeclaration:
const classSymbol = getSymbolOfNode(node.parent);
const classConstructorType = getTypeOfSymbol(classSymbol);
expectedReturnType = getUnionType([classConstructorType, voidType]);
break;
case SyntaxKind.Parameter:
expectedReturnType = voidType;
errorInfo = chainDiagnosticMessages(
errorInfo,
Diagnostics.The_return_type_of_a_parameter_decorator_function_must_be_either_void_or_any);
break;
case SyntaxKind.PropertyDeclaration:
expectedReturnType = voidType;
errorInfo = chainDiagnosticMessages(
errorInfo,
Diagnostics.The_return_type_of_a_property_decorator_function_must_be_either_void_or_any);
break;
case SyntaxKind.MethodDeclaration:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
const methodType = getTypeOfNode(node.parent);
const descriptorType = createTypedPropertyDescriptorType(methodType);
expectedReturnType = getUnionType([descriptorType, voidType]);
break;
}
checkTypeAssignableTo(
returnType,
expectedReturnType,
node,
headMessage,
errorInfo);
}
/**
* If a TypeNode can be resolved to a value symbol imported from an external module, it is
* marked as referenced to prevent import elision.
*/
function markTypeNodeAsReferenced(node: TypeNode) {
markEntityNameOrEntityExpressionAsReference(node && getEntityNameFromTypeNode(node));
}
function markEntityNameOrEntityExpressionAsReference(typeName: EntityNameOrEntityNameExpression) {
const rootName = typeName && getFirstIdentifier(typeName);
const rootSymbol = rootName && resolveName(rootName, rootName.text, (typeName.kind === SyntaxKind.Identifier ? SymbolFlags.Type : SymbolFlags.Namespace) | SymbolFlags.Alias, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined);
if (rootSymbol
&& rootSymbol.flags & SymbolFlags.Alias
&& symbolIsValue(rootSymbol)
&& !isConstEnumOrConstEnumOnlyModule(resolveAlias(rootSymbol))) {
markAliasSymbolAsReferenced(rootSymbol);
}
}
/**
* This function marks the type used for metadata decorator as referenced if it is import
* from external module.
* This is different from markTypeNodeAsReferenced because it tries to simplify type nodes in
* union and intersection type
* @param node
*/
function markDecoratorMedataDataTypeNodeAsReferenced(node: TypeNode): void {
const entityName = getEntityNameForDecoratorMetadata(node);
if (entityName && isEntityName(entityName)) {
markEntityNameOrEntityExpressionAsReference(entityName);
}
}
function getEntityNameForDecoratorMetadata(node: TypeNode): EntityName {
if (node) {
switch (node.kind) {
case SyntaxKind.IntersectionType:
case SyntaxKind.UnionType:
let commonEntityName: EntityName;
for (const typeNode of (<UnionOrIntersectionTypeNode>node).types) {
const individualEntityName = getEntityNameForDecoratorMetadata(typeNode);
if (!individualEntityName) {
// Individual is something like string number
// So it would be serialized to either that type or object
// Safe to return here
return undefined;
}
if (commonEntityName) {
// Note this is in sync with the transformation that happens for type node.
// Keep this in sync with serializeUnionOrIntersectionType
// Verify if they refer to same entity and is identifier
// return undefined if they dont match because we would emit object
if (!isIdentifier(commonEntityName) ||
!isIdentifier(individualEntityName) ||
commonEntityName.text !== individualEntityName.text) {
return undefined;
}
}
else {
commonEntityName = individualEntityName;
}
}
return commonEntityName;
case SyntaxKind.ParenthesizedType:
return getEntityNameForDecoratorMetadata((<ParenthesizedTypeNode>node).type);
case SyntaxKind.TypeReference:
return (<TypeReferenceNode>node).typeName;
}
}
}
function getParameterTypeNodeForDecoratorCheck(node: ParameterDeclaration): TypeNode {
return node.dotDotDotToken ? getRestParameterElementType(node.type) : node.type;
}
/** Check the decorators of a node */
function checkDecorators(node: Node): void {
if (!node.decorators) {
return;
}
// skip this check for nodes that cannot have decorators. These should have already had an error reported by
// checkGrammarDecorators.
if (!nodeCanBeDecorated(node)) {
return;
}
if (!compilerOptions.experimentalDecorators) {
error(node, Diagnostics.Experimental_support_for_decorators_is_a_feature_that_is_subject_to_change_in_a_future_release_Set_the_experimentalDecorators_option_to_remove_this_warning);
}
const firstDecorator = node.decorators[0];
checkExternalEmitHelpers(firstDecorator, ExternalEmitHelpers.Decorate);
if (node.kind === SyntaxKind.Parameter) {
checkExternalEmitHelpers(firstDecorator, ExternalEmitHelpers.Param);
}
if (compilerOptions.emitDecoratorMetadata) {
checkExternalEmitHelpers(firstDecorator, ExternalEmitHelpers.Metadata);
// we only need to perform these checks if we are emitting serialized type metadata for the target of a decorator.
switch (node.kind) {
case SyntaxKind.ClassDeclaration:
const constructor = getFirstConstructorWithBody(<ClassDeclaration>node);
if (constructor) {
for (const parameter of constructor.parameters) {
markDecoratorMedataDataTypeNodeAsReferenced(getParameterTypeNodeForDecoratorCheck(parameter));
}
}
break;
case SyntaxKind.MethodDeclaration:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
for (const parameter of (<FunctionLikeDeclaration>node).parameters) {
markDecoratorMedataDataTypeNodeAsReferenced(getParameterTypeNodeForDecoratorCheck(parameter));
}
markDecoratorMedataDataTypeNodeAsReferenced((<FunctionLikeDeclaration>node).type);
break;
case SyntaxKind.PropertyDeclaration:
markDecoratorMedataDataTypeNodeAsReferenced(getParameterTypeNodeForDecoratorCheck(<ParameterDeclaration>node));
break;
case SyntaxKind.Parameter:
markDecoratorMedataDataTypeNodeAsReferenced((<PropertyDeclaration>node).type);
break;
}
}
forEach(node.decorators, checkDecorator);
}
function checkFunctionDeclaration(node: FunctionDeclaration): void {
if (produceDiagnostics) {
checkFunctionOrMethodDeclaration(node) || checkGrammarForGenerator(node);
checkCollisionWithCapturedSuperVariable(node, node.name);
checkCollisionWithCapturedThisVariable(node, node.name);
checkCollisionWithCapturedNewTargetVariable(node, node.name);
checkCollisionWithRequireExportsInGeneratedCode(node, node.name);
checkCollisionWithGlobalPromiseInGeneratedCode(node, node.name);
}
}
function checkFunctionOrMethodDeclaration(node: FunctionDeclaration | MethodDeclaration): void {
checkDecorators(node);
checkSignatureDeclaration(node);
const functionFlags = getFunctionFlags(node);
// Do not use hasDynamicName here, because that returns false for well known symbols.
// We want to perform checkComputedPropertyName for all computed properties, including
// well known symbols.
if (node.name && node.name.kind === SyntaxKind.ComputedPropertyName) {
// This check will account for methods in class/interface declarations,
// as well as accessors in classes/object literals
checkComputedPropertyName(<ComputedPropertyName>node.name);
}
if (!hasDynamicName(node)) {
// first we want to check the local symbol that contain this declaration
// - if node.localSymbol !== undefined - this is current declaration is exported and localSymbol points to the local symbol
// - if node.localSymbol === undefined - this node is non-exported so we can just pick the result of getSymbolOfNode
const symbol = getSymbolOfNode(node);
const localSymbol = node.localSymbol || symbol;
// Since the javascript won't do semantic analysis like typescript,
// if the javascript file comes before the typescript file and both contain same name functions,
// checkFunctionOrConstructorSymbol wouldn't be called if we didnt ignore javascript function.
const firstDeclaration = forEach(localSymbol.declarations,
// Get first non javascript function declaration
declaration => declaration.kind === node.kind && !isSourceFileJavaScript(getSourceFileOfNode(declaration)) ?
declaration : undefined);
// Only type check the symbol once
if (node === firstDeclaration) {
checkFunctionOrConstructorSymbol(localSymbol);
}
if (symbol.parent) {
// run check once for the first declaration
if (getDeclarationOfKind(symbol, node.kind) === node) {
// run check on export symbol to check that modifiers agree across all exported declarations
checkFunctionOrConstructorSymbol(symbol);
}
}
}
checkSourceElement(node.body);
if ((functionFlags & FunctionFlags.Generator) === 0) { // Async function or normal function
const returnOrPromisedType = node.type && (functionFlags & FunctionFlags.Async
? checkAsyncFunctionReturnType(node) // Async function
: getTypeFromTypeNode(node.type)); // normal function
checkAllCodePathsInNonVoidFunctionReturnOrThrow(node, returnOrPromisedType);
}
if (produceDiagnostics && !node.type) {
// Report an implicit any error if there is no body, no explicit return type, and node is not a private method
// in an ambient context
if (noImplicitAny && nodeIsMissing(node.body) && !isPrivateWithinAmbient(node)) {
reportImplicitAnyError(node, anyType);
}
if (functionFlags & FunctionFlags.Generator && nodeIsPresent(node.body)) {
// A generator with a body and no type annotation can still cause errors. It can error if the
// yielded values have no common supertype, or it can give an implicit any error if it has no
// yielded values. The only way to trigger these errors is to try checking its return type.
getReturnTypeOfSignature(getSignatureFromDeclaration(node));
}
}
registerForUnusedIdentifiersCheck(node);
}
function registerForUnusedIdentifiersCheck(node: Node) {
if (deferredUnusedIdentifierNodes) {
deferredUnusedIdentifierNodes.push(node);
}
}
function checkUnusedIdentifiers() {
if (deferredUnusedIdentifierNodes) {
for (const node of deferredUnusedIdentifierNodes) {
switch (node.kind) {
case SyntaxKind.SourceFile:
case SyntaxKind.ModuleDeclaration:
checkUnusedModuleMembers(<ModuleDeclaration | SourceFile>node);
break;
case SyntaxKind.ClassDeclaration:
case SyntaxKind.ClassExpression:
checkUnusedClassMembers(<ClassDeclaration | ClassExpression>node);
checkUnusedTypeParameters(<ClassDeclaration | ClassExpression>node);
break;
case SyntaxKind.InterfaceDeclaration:
checkUnusedTypeParameters(<InterfaceDeclaration>node);
break;
case SyntaxKind.Block:
case SyntaxKind.CaseBlock:
case SyntaxKind.ForStatement:
case SyntaxKind.ForInStatement:
case SyntaxKind.ForOfStatement:
checkUnusedLocalsAndParameters(node);
break;
case SyntaxKind.Constructor:
case SyntaxKind.FunctionExpression:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.ArrowFunction:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
if ((<FunctionLikeDeclaration>node).body) {
checkUnusedLocalsAndParameters(<FunctionLikeDeclaration>node);
}
checkUnusedTypeParameters(<FunctionLikeDeclaration>node);
break;
case SyntaxKind.MethodSignature:
case SyntaxKind.CallSignature:
case SyntaxKind.ConstructSignature:
case SyntaxKind.IndexSignature:
case SyntaxKind.FunctionType:
case SyntaxKind.ConstructorType:
checkUnusedTypeParameters(<FunctionLikeDeclaration>node);
break;
}
}
}
}
function checkUnusedLocalsAndParameters(node: Node): void {
if (node.parent.kind !== SyntaxKind.InterfaceDeclaration && noUnusedIdentifiers && !isInAmbientContext(node)) {
node.locals.forEach(local => {
if (!local.isReferenced) {
if (local.valueDeclaration && getRootDeclaration(local.valueDeclaration).kind === SyntaxKind.Parameter) {
const parameter = <ParameterDeclaration>getRootDeclaration(local.valueDeclaration);
const name = getNameOfDeclaration(local.valueDeclaration);
if (compilerOptions.noUnusedParameters &&
!isParameterPropertyDeclaration(parameter) &&
!parameterIsThisKeyword(parameter) &&
!parameterNameStartsWithUnderscore(name)) {
error(name, Diagnostics._0_is_declared_but_never_used, local.name);
}
}
else if (compilerOptions.noUnusedLocals) {
forEach(local.declarations, d => errorUnusedLocal(getNameOfDeclaration(d) || d, local.name));
}
}
});
}
}
function isRemovedPropertyFromObjectSpread(node: Node) {
if (isBindingElement(node) && isObjectBindingPattern(node.parent)) {
const lastElement = lastOrUndefined(node.parent.elements);
return lastElement !== node && !!lastElement.dotDotDotToken;
}
return false;
}
function errorUnusedLocal(node: Node, name: string) {
if (isIdentifierThatStartsWithUnderScore(node)) {
const declaration = getRootDeclaration(node.parent);
if (declaration.kind === SyntaxKind.VariableDeclaration &&
(declaration.parent.parent.kind === SyntaxKind.ForInStatement ||
declaration.parent.parent.kind === SyntaxKind.ForOfStatement)) {
return;
}
}
if (!isRemovedPropertyFromObjectSpread(node.kind === SyntaxKind.Identifier ? node.parent : node)) {
error(node, Diagnostics._0_is_declared_but_never_used, name);
}
}
function parameterNameStartsWithUnderscore(parameterName: DeclarationName) {
return parameterName && isIdentifierThatStartsWithUnderScore(parameterName);
}
function isIdentifierThatStartsWithUnderScore(node: Node) {
return node.kind === SyntaxKind.Identifier && (<Identifier>node).text.charCodeAt(0) === CharacterCodes._;
}
function checkUnusedClassMembers(node: ClassDeclaration | ClassExpression): void {
if (compilerOptions.noUnusedLocals && !isInAmbientContext(node)) {
if (node.members) {
for (const member of node.members) {
if (member.kind === SyntaxKind.MethodDeclaration || member.kind === SyntaxKind.PropertyDeclaration) {
if (!member.symbol.isReferenced && getModifierFlags(member) & ModifierFlags.Private) {
error(member.name, Diagnostics._0_is_declared_but_never_used, member.symbol.name);
}
}
else if (member.kind === SyntaxKind.Constructor) {
for (const parameter of (<ConstructorDeclaration>member).parameters) {
if (!parameter.symbol.isReferenced && getModifierFlags(parameter) & ModifierFlags.Private) {
error(parameter.name, Diagnostics.Property_0_is_declared_but_never_used, parameter.symbol.name);
}
}
}
}
}
}
}
function checkUnusedTypeParameters(node: ClassDeclaration | ClassExpression | FunctionDeclaration | MethodDeclaration | FunctionExpression | ArrowFunction | ConstructorDeclaration | SignatureDeclaration | InterfaceDeclaration) {
if (compilerOptions.noUnusedLocals && !isInAmbientContext(node)) {
if (node.typeParameters) {
// Only report errors on the last declaration for the type parameter container;
// this ensures that all uses have been accounted for.
const symbol = getSymbolOfNode(node);
const lastDeclaration = symbol && symbol.declarations && lastOrUndefined(symbol.declarations);
if (lastDeclaration !== node) {
return;
}
for (const typeParameter of node.typeParameters) {
if (!getMergedSymbol(typeParameter.symbol).isReferenced) {
error(typeParameter.name, Diagnostics._0_is_declared_but_never_used, typeParameter.symbol.name);
}
}
}
}
}
function checkUnusedModuleMembers(node: ModuleDeclaration | SourceFile): void {
if (compilerOptions.noUnusedLocals && !isInAmbientContext(node)) {
node.locals.forEach(local => {
if (!local.isReferenced && !local.exportSymbol) {
for (const declaration of local.declarations) {
if (!isAmbientModule(declaration)) {
errorUnusedLocal(getNameOfDeclaration(declaration), local.name);
}
}
}
});
}
}
function checkBlock(node: Block) {
// Grammar checking for SyntaxKind.Block
if (node.kind === SyntaxKind.Block) {
checkGrammarStatementInAmbientContext(node);
}
forEach(node.statements, checkSourceElement);
if (node.locals) {
registerForUnusedIdentifiersCheck(node);
}
}
function checkCollisionWithArgumentsInGeneratedCode(node: SignatureDeclaration) {
// no rest parameters \ declaration context \ overload - no codegen impact
if (!hasDeclaredRestParameter(node) || isInAmbientContext(node) || nodeIsMissing((<FunctionLikeDeclaration>node).body)) {
return;
}
forEach(node.parameters, p => {
if (p.name && !isBindingPattern(p.name) && (<Identifier>p.name).text === argumentsSymbol.name) {
error(p, Diagnostics.Duplicate_identifier_arguments_Compiler_uses_arguments_to_initialize_rest_parameters);
}
});
}
function needCollisionCheckForIdentifier(node: Node, identifier: Identifier, name: string): boolean {
if (!(identifier && identifier.text === name)) {
return false;
}
if (node.kind === SyntaxKind.PropertyDeclaration ||
node.kind === SyntaxKind.PropertySignature ||
node.kind === SyntaxKind.MethodDeclaration ||
node.kind === SyntaxKind.MethodSignature ||
node.kind === SyntaxKind.GetAccessor ||
node.kind === SyntaxKind.SetAccessor) {
// it is ok to have member named '_super' or '_this' - member access is always qualified
return false;
}
if (isInAmbientContext(node)) {
// ambient context - no codegen impact
return false;
}
const root = getRootDeclaration(node);
if (root.kind === SyntaxKind.Parameter && nodeIsMissing((<FunctionLikeDeclaration>root.parent).body)) {
// just an overload - no codegen impact
return false;
}
return true;
}
function checkCollisionWithCapturedThisVariable(node: Node, name: Identifier): void {
if (needCollisionCheckForIdentifier(node, name, "_this")) {
potentialThisCollisions.push(node);
}
}
function checkCollisionWithCapturedNewTargetVariable(node: Node, name: Identifier): void {
if (needCollisionCheckForIdentifier(node, name, "_newTarget")) {
potentialNewTargetCollisions.push(node);
}
}
// this function will run after checking the source file so 'CaptureThis' is correct for all nodes
function checkIfThisIsCapturedInEnclosingScope(node: Node): void {
findAncestor(node, current => {
if (getNodeCheckFlags(current) & NodeCheckFlags.CaptureThis) {
const isDeclaration = node.kind !== SyntaxKind.Identifier;
if (isDeclaration) {
error(getNameOfDeclaration(<Declaration>node), Diagnostics.Duplicate_identifier_this_Compiler_uses_variable_declaration_this_to_capture_this_reference);
}
else {
error(node, Diagnostics.Expression_resolves_to_variable_declaration_this_that_compiler_uses_to_capture_this_reference);
}
return true;
}
});
}
function checkIfNewTargetIsCapturedInEnclosingScope(node: Node): void {
findAncestor(node, current => {
if (getNodeCheckFlags(current) & NodeCheckFlags.CaptureNewTarget) {
const isDeclaration = node.kind !== SyntaxKind.Identifier;
if (isDeclaration) {
error(getNameOfDeclaration(<Declaration>node), Diagnostics.Duplicate_identifier_newTarget_Compiler_uses_variable_declaration_newTarget_to_capture_new_target_meta_property_reference);
}
else {
error(node, Diagnostics.Expression_resolves_to_variable_declaration_newTarget_that_compiler_uses_to_capture_new_target_meta_property_reference);
}
return true;
}
});
}
function checkCollisionWithCapturedSuperVariable(node: Node, name: Identifier) {
if (!needCollisionCheckForIdentifier(node, name, "_super")) {
return;
}
// bubble up and find containing type
const enclosingClass = getContainingClass(node);
// if containing type was not found or it is ambient - exit (no codegen)
if (!enclosingClass || isInAmbientContext(enclosingClass)) {
return;
}
if (getClassExtendsHeritageClauseElement(enclosingClass)) {
const isDeclaration = node.kind !== SyntaxKind.Identifier;
if (isDeclaration) {
error(node, Diagnostics.Duplicate_identifier_super_Compiler_uses_super_to_capture_base_class_reference);
}
else {
error(node, Diagnostics.Expression_resolves_to_super_that_compiler_uses_to_capture_base_class_reference);
}
}
}
function checkCollisionWithRequireExportsInGeneratedCode(node: Node, name: Identifier) {
// No need to check for require or exports for ES6 modules and later
if (modulekind >= ModuleKind.ES2015) {
return;
}
if (!needCollisionCheckForIdentifier(node, name, "require") && !needCollisionCheckForIdentifier(node, name, "exports")) {
return;
}
// Uninstantiated modules shouldnt do this check
if (node.kind === SyntaxKind.ModuleDeclaration && getModuleInstanceState(node) !== ModuleInstanceState.Instantiated) {
return;
}
// In case of variable declaration, node.parent is variable statement so look at the variable statement's parent
const parent = getDeclarationContainer(node);
if (parent.kind === SyntaxKind.SourceFile && isExternalOrCommonJsModule(<SourceFile>parent)) {
// If the declaration happens to be in external module, report error that require and exports are reserved keywords
error(name, Diagnostics.Duplicate_identifier_0_Compiler_reserves_name_1_in_top_level_scope_of_a_module,
declarationNameToString(name), declarationNameToString(name));
}
}
function checkCollisionWithGlobalPromiseInGeneratedCode(node: Node, name: Identifier): void {
if (languageVersion >= ScriptTarget.ES2017 || !needCollisionCheckForIdentifier(node, name, "Promise")) {
return;
}
// Uninstantiated modules shouldnt do this check
if (node.kind === SyntaxKind.ModuleDeclaration && getModuleInstanceState(node) !== ModuleInstanceState.Instantiated) {
return;
}
// In case of variable declaration, node.parent is variable statement so look at the variable statement's parent
const parent = getDeclarationContainer(node);
if (parent.kind === SyntaxKind.SourceFile && isExternalOrCommonJsModule(<SourceFile>parent) && parent.flags & NodeFlags.HasAsyncFunctions) {
// If the declaration happens to be in external module, report error that Promise is a reserved identifier.
error(name, Diagnostics.Duplicate_identifier_0_Compiler_reserves_name_1_in_top_level_scope_of_a_module_containing_async_functions,
declarationNameToString(name), declarationNameToString(name));
}
}
function checkVarDeclaredNamesNotShadowed(node: VariableDeclaration | BindingElement) {
// - ScriptBody : StatementList
// It is a Syntax Error if any element of the LexicallyDeclaredNames of StatementList
// also occurs in the VarDeclaredNames of StatementList.
// - Block : { StatementList }
// It is a Syntax Error if any element of the LexicallyDeclaredNames of StatementList
// also occurs in the VarDeclaredNames of StatementList.
// Variable declarations are hoisted to the top of their function scope. They can shadow
// block scoped declarations, which bind tighter. this will not be flagged as duplicate definition
// by the binder as the declaration scope is different.
// A non-initialized declaration is a no-op as the block declaration will resolve before the var
// declaration. the problem is if the declaration has an initializer. this will act as a write to the
// block declared value. this is fine for let, but not const.
// Only consider declarations with initializers, uninitialized const declarations will not
// step on a let/const variable.
// Do not consider const and const declarations, as duplicate block-scoped declarations
// are handled by the binder.
// We are only looking for const declarations that step on let\const declarations from a
// different scope. e.g.:
// {
// const x = 0; // localDeclarationSymbol obtained after name resolution will correspond to this declaration
// const x = 0; // symbol for this declaration will be 'symbol'
// }
// skip block-scoped variables and parameters
if ((getCombinedNodeFlags(node) & NodeFlags.BlockScoped) !== 0 || isParameterDeclaration(node)) {
return;
}
// skip variable declarations that don't have initializers
// NOTE: in ES6 spec initializer is required in variable declarations where name is binding pattern
// so we'll always treat binding elements as initialized
if (node.kind === SyntaxKind.VariableDeclaration && !node.initializer) {
return;
}
const symbol = getSymbolOfNode(node);
if (symbol.flags & SymbolFlags.FunctionScopedVariable) {
const localDeclarationSymbol = resolveName(node, (<Identifier>node.name).text, SymbolFlags.Variable, /*nodeNotFoundErrorMessage*/ undefined, /*nameArg*/ undefined);
if (localDeclarationSymbol &&
localDeclarationSymbol !== symbol &&
localDeclarationSymbol.flags & SymbolFlags.BlockScopedVariable) {
if (getDeclarationNodeFlagsFromSymbol(localDeclarationSymbol) & NodeFlags.BlockScoped) {
const varDeclList = getAncestor(localDeclarationSymbol.valueDeclaration, SyntaxKind.VariableDeclarationList);
const container =
varDeclList.parent.kind === SyntaxKind.VariableStatement && varDeclList.parent.parent
? varDeclList.parent.parent
: undefined;
// names of block-scoped and function scoped variables can collide only
// if block scoped variable is defined in the function\module\source file scope (because of variable hoisting)
const namesShareScope =
container &&
(container.kind === SyntaxKind.Block && isFunctionLike(container.parent) ||
container.kind === SyntaxKind.ModuleBlock ||
container.kind === SyntaxKind.ModuleDeclaration ||
container.kind === SyntaxKind.SourceFile);
// here we know that function scoped variable is shadowed by block scoped one
// if they are defined in the same scope - binder has already reported redeclaration error
// otherwise if variable has an initializer - show error that initialization will fail
// since LHS will be block scoped name instead of function scoped
if (!namesShareScope) {
const name = symbolToString(localDeclarationSymbol);
error(node, Diagnostics.Cannot_initialize_outer_scoped_variable_0_in_the_same_scope_as_block_scoped_declaration_1, name, name);
}
}
}
}
}
// Check that a parameter initializer contains no references to parameters declared to the right of itself
function checkParameterInitializer(node: VariableLikeDeclaration): void {
if (getRootDeclaration(node).kind !== SyntaxKind.Parameter) {
return;
}
const func = getContainingFunction(node);
visit(node.initializer);
function visit(n: Node): void {
if (isTypeNode(n) || isDeclarationName(n)) {
// do not dive in types
// skip declaration names (i.e. in object literal expressions)
return;
}
if (n.kind === SyntaxKind.PropertyAccessExpression) {
// skip property names in property access expression
return visit((<PropertyAccessExpression>n).expression);
}
else if (n.kind === SyntaxKind.Identifier) {
// check FunctionLikeDeclaration.locals (stores parameters\function local variable)
// if it contains entry with a specified name
const symbol = resolveName(n, (<Identifier>n).text, SymbolFlags.Value | SymbolFlags.Alias, /*nameNotFoundMessage*/undefined, /*nameArg*/undefined);
if (!symbol || symbol === unknownSymbol || !symbol.valueDeclaration) {
return;
}
if (symbol.valueDeclaration === node) {
error(n, Diagnostics.Parameter_0_cannot_be_referenced_in_its_initializer, declarationNameToString(node.name));
return;
}
// locals map for function contain both parameters and function locals
// so we need to do a bit of extra work to check if reference is legal
const enclosingContainer = getEnclosingBlockScopeContainer(symbol.valueDeclaration);
if (enclosingContainer === func) {
if (symbol.valueDeclaration.kind === SyntaxKind.Parameter ||
symbol.valueDeclaration.kind === SyntaxKind.BindingElement) {
// it is ok to reference parameter in initializer if either
// - parameter is located strictly on the left of current parameter declaration
if (symbol.valueDeclaration.pos < node.pos) {
return;
}
// - parameter is wrapped in function-like entity
if (findAncestor(
n,
current => {
if (current === node.initializer) {
return "quit";
}
return isFunctionLike(current.parent) ||
// computed property names/initializers in instance property declaration of class like entities
// are executed in constructor and thus deferred
(current.parent.kind === SyntaxKind.PropertyDeclaration &&
!(hasModifier(current.parent, ModifierFlags.Static)) &&
isClassLike(current.parent.parent));
})) {
return;
}
// fall through to report error
}
error(n, Diagnostics.Initializer_of_parameter_0_cannot_reference_identifier_1_declared_after_it, declarationNameToString(node.name), declarationNameToString(<Identifier>n));
}
}
else {
return forEachChild(n, visit);
}
}
}
function convertAutoToAny(type: Type) {
return type === autoType ? anyType : type === autoArrayType ? anyArrayType : type;
}
// Check variable, parameter, or property declaration
function checkVariableLikeDeclaration(node: VariableLikeDeclaration) {
checkDecorators(node);
checkSourceElement(node.type);
// For a computed property, just check the initializer and exit
// Do not use hasDynamicName here, because that returns false for well known symbols.
// We want to perform checkComputedPropertyName for all computed properties, including
// well known symbols.
if (node.name.kind === SyntaxKind.ComputedPropertyName) {
checkComputedPropertyName(<ComputedPropertyName>node.name);
if (node.initializer) {
checkExpressionCached(node.initializer);
}
}
if (node.kind === SyntaxKind.BindingElement) {
if (node.parent.kind === SyntaxKind.ObjectBindingPattern && languageVersion < ScriptTarget.ESNext) {
checkExternalEmitHelpers(node, ExternalEmitHelpers.Rest);
}
// check computed properties inside property names of binding elements
if (node.propertyName && node.propertyName.kind === SyntaxKind.ComputedPropertyName) {
checkComputedPropertyName(<ComputedPropertyName>node.propertyName);
}
// check private/protected variable access
const parent = <VariableLikeDeclaration>(<BindingPattern>node.parent).parent;
const parentType = getTypeForBindingElementParent(parent);
const name = node.propertyName || <Identifier>node.name;
const property = getPropertyOfType(parentType, getTextOfPropertyName(name));
markPropertyAsReferenced(property);
if (parent.initializer && property) {
checkPropertyAccessibility(parent, parent.initializer, parentType, property);
}
}
// For a binding pattern, check contained binding elements
if (isBindingPattern(node.name)) {
if (node.name.kind === SyntaxKind.ArrayBindingPattern && languageVersion < ScriptTarget.ES2015 && compilerOptions.downlevelIteration) {
checkExternalEmitHelpers(node, ExternalEmitHelpers.Read);
}
forEach((<BindingPattern>node.name).elements, checkSourceElement);
}
// For a parameter declaration with an initializer, error and exit if the containing function doesn't have a body
if (node.initializer && getRootDeclaration(node).kind === SyntaxKind.Parameter && nodeIsMissing(getContainingFunction(node).body)) {
error(node, Diagnostics.A_parameter_initializer_is_only_allowed_in_a_function_or_constructor_implementation);
return;
}
// For a binding pattern, validate the initializer and exit
if (isBindingPattern(node.name)) {
// Don't validate for-in initializer as it is already an error
if (node.initializer && node.parent.parent.kind !== SyntaxKind.ForInStatement) {
checkTypeAssignableTo(checkExpressionCached(node.initializer), getWidenedTypeForVariableLikeDeclaration(node), node, /*headMessage*/ undefined);
checkParameterInitializer(node);
}
return;
}
const symbol = getSymbolOfNode(node);
const type = convertAutoToAny(getTypeOfVariableOrParameterOrProperty(symbol));
if (node === symbol.valueDeclaration) {
// Node is the primary declaration of the symbol, just validate the initializer
// Don't validate for-in initializer as it is already an error
if (node.initializer && node.parent.parent.kind !== SyntaxKind.ForInStatement) {
checkTypeAssignableTo(checkExpressionCached(node.initializer), type, node, /*headMessage*/ undefined);
checkParameterInitializer(node);
}
}
else {
// Node is a secondary declaration, check that type is identical to primary declaration and check that
// initializer is consistent with type associated with the node
const declarationType = convertAutoToAny(getWidenedTypeForVariableLikeDeclaration(node));
if (type !== unknownType && declarationType !== unknownType && !isTypeIdenticalTo(type, declarationType)) {
error(node.name, Diagnostics.Subsequent_variable_declarations_must_have_the_same_type_Variable_0_must_be_of_type_1_but_here_has_type_2, declarationNameToString(node.name), typeToString(type), typeToString(declarationType));
}
if (node.initializer) {
checkTypeAssignableTo(checkExpressionCached(node.initializer), declarationType, node, /*headMessage*/ undefined);
}
if (!areDeclarationFlagsIdentical(node, symbol.valueDeclaration)) {
error(getNameOfDeclaration(symbol.valueDeclaration), Diagnostics.All_declarations_of_0_must_have_identical_modifiers, declarationNameToString(node.name));
error(node.name, Diagnostics.All_declarations_of_0_must_have_identical_modifiers, declarationNameToString(node.name));
}
}
if (node.kind !== SyntaxKind.PropertyDeclaration && node.kind !== SyntaxKind.PropertySignature) {
// We know we don't have a binding pattern or computed name here
checkExportsOnMergedDeclarations(node);
if (node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.BindingElement) {
checkVarDeclaredNamesNotShadowed(<VariableDeclaration | BindingElement>node);
}
checkCollisionWithCapturedSuperVariable(node, <Identifier>node.name);
checkCollisionWithCapturedThisVariable(node, <Identifier>node.name);
checkCollisionWithCapturedNewTargetVariable(node, <Identifier>node.name);
checkCollisionWithRequireExportsInGeneratedCode(node, <Identifier>node.name);
checkCollisionWithGlobalPromiseInGeneratedCode(node, <Identifier>node.name);
}
}
function areDeclarationFlagsIdentical(left: Declaration, right: Declaration) {
if ((left.kind === SyntaxKind.Parameter && right.kind === SyntaxKind.VariableDeclaration) ||
(left.kind === SyntaxKind.VariableDeclaration && right.kind === SyntaxKind.Parameter)) {
// Differences in optionality between parameters and variables are allowed.
return true;
}
if (hasQuestionToken(left) !== hasQuestionToken(right)) {
return false;
}
const interestingFlags = ModifierFlags.Private |
ModifierFlags.Protected |
ModifierFlags.Async |
ModifierFlags.Abstract |
ModifierFlags.Readonly |
ModifierFlags.Static;
return (getModifierFlags(left) & interestingFlags) === (getModifierFlags(right) & interestingFlags);
}
function checkVariableDeclaration(node: VariableDeclaration) {
checkGrammarVariableDeclaration(node);
return checkVariableLikeDeclaration(node);
}
function checkBindingElement(node: BindingElement) {
checkGrammarBindingElement(<BindingElement>node);
return checkVariableLikeDeclaration(node);
}
function checkVariableStatement(node: VariableStatement) {
// Grammar checking
checkGrammarDecorators(node) || checkGrammarModifiers(node) || checkGrammarVariableDeclarationList(node.declarationList) || checkGrammarForDisallowedLetOrConstStatement(node);
forEach(node.declarationList.declarations, checkSourceElement);
}
function checkGrammarDisallowedModifiersOnObjectLiteralExpressionMethod(node: MethodDeclaration) {
// We only disallow modifier on a method declaration if it is a property of object-literal-expression
if (node.modifiers && node.parent.kind === SyntaxKind.ObjectLiteralExpression) {
if (getFunctionFlags(node) & FunctionFlags.Async) {
if (node.modifiers.length > 1) {
return grammarErrorOnFirstToken(node, Diagnostics.Modifiers_cannot_appear_here);
}
}
else {
return grammarErrorOnFirstToken(node, Diagnostics.Modifiers_cannot_appear_here);
}
}
}
function checkExpressionStatement(node: ExpressionStatement) {
// Grammar checking
checkGrammarStatementInAmbientContext(node);
checkExpression(node.expression);
}
function checkIfStatement(node: IfStatement) {
// Grammar checking
checkGrammarStatementInAmbientContext(node);
checkExpression(node.expression);
checkSourceElement(node.thenStatement);
if (node.thenStatement.kind === SyntaxKind.EmptyStatement) {
error(node.thenStatement, Diagnostics.The_body_of_an_if_statement_cannot_be_the_empty_statement);
}
checkSourceElement(node.elseStatement);
}
function checkDoStatement(node: DoStatement) {
// Grammar checking
checkGrammarStatementInAmbientContext(node);
checkSourceElement(node.statement);
checkExpression(node.expression);
}
function checkWhileStatement(node: WhileStatement) {
// Grammar checking
checkGrammarStatementInAmbientContext(node);
checkExpression(node.expression);
checkSourceElement(node.statement);
}
function checkForStatement(node: ForStatement) {
// Grammar checking
if (!checkGrammarStatementInAmbientContext(node)) {
if (node.initializer && node.initializer.kind === SyntaxKind.VariableDeclarationList) {
checkGrammarVariableDeclarationList(<VariableDeclarationList>node.initializer);
}
}
if (node.initializer) {
if (node.initializer.kind === SyntaxKind.VariableDeclarationList) {
forEach((<VariableDeclarationList>node.initializer).declarations, checkVariableDeclaration);
}
else {
checkExpression(<Expression>node.initializer);
}
}
if (node.condition) checkExpression(node.condition);
if (node.incrementor) checkExpression(node.incrementor);
checkSourceElement(node.statement);
if (node.locals) {
registerForUnusedIdentifiersCheck(node);
}
}
function checkForOfStatement(node: ForOfStatement): void {
checkGrammarForInOrForOfStatement(node);
if (node.kind === SyntaxKind.ForOfStatement) {
if ((<ForOfStatement>node).awaitModifier) {
const functionFlags = getFunctionFlags(getContainingFunction(node));
if ((functionFlags & (FunctionFlags.Invalid | FunctionFlags.Async)) === FunctionFlags.Async && languageVersion < ScriptTarget.ESNext) {
// for..await..of in an async function or async generator function prior to ESNext requires the __asyncValues helper
checkExternalEmitHelpers(node, ExternalEmitHelpers.ForAwaitOfIncludes);
}
}
else if (compilerOptions.downlevelIteration && languageVersion < ScriptTarget.ES2015) {
// for..of prior to ES2015 requires the __values helper when downlevelIteration is enabled
checkExternalEmitHelpers(node, ExternalEmitHelpers.ForOfIncludes);
}
}
// Check the LHS and RHS
// If the LHS is a declaration, just check it as a variable declaration, which will in turn check the RHS
// via checkRightHandSideOfForOf.
// If the LHS is an expression, check the LHS, as a destructuring assignment or as a reference.
// Then check that the RHS is assignable to it.
if (node.initializer.kind === SyntaxKind.VariableDeclarationList) {
checkForInOrForOfVariableDeclaration(node);
}
else {
const varExpr = <Expression>node.initializer;
const iteratedType = checkRightHandSideOfForOf(node.expression, node.awaitModifier);
// There may be a destructuring assignment on the left side
if (varExpr.kind === SyntaxKind.ArrayLiteralExpression || varExpr.kind === SyntaxKind.ObjectLiteralExpression) {
// iteratedType may be undefined. In this case, we still want to check the structure of
// varExpr, in particular making sure it's a valid LeftHandSideExpression. But we'd like
// to short circuit the type relation checking as much as possible, so we pass the unknownType.
checkDestructuringAssignment(varExpr, iteratedType || unknownType);
}
else {
const leftType = checkExpression(varExpr);
checkReferenceExpression(varExpr, Diagnostics.The_left_hand_side_of_a_for_of_statement_must_be_a_variable_or_a_property_access);
// iteratedType will be undefined if the rightType was missing properties/signatures
// required to get its iteratedType (like [Symbol.iterator] or next). This may be
// because we accessed properties from anyType, or it may have led to an error inside
// getElementTypeOfIterable.
if (iteratedType) {
checkTypeAssignableTo(iteratedType, leftType, varExpr, /*headMessage*/ undefined);
}
}
}
checkSourceElement(node.statement);
if (node.locals) {
registerForUnusedIdentifiersCheck(node);
}
}
function checkForInStatement(node: ForInStatement) {
// Grammar checking
checkGrammarForInOrForOfStatement(node);
const rightType = checkNonNullExpression(node.expression);
// TypeScript 1.0 spec (April 2014): 5.4
// In a 'for-in' statement of the form
// for (let VarDecl in Expr) Statement
// VarDecl must be a variable declaration without a type annotation that declares a variable of type Any,
// and Expr must be an expression of type Any, an object type, or a type parameter type.
if (node.initializer.kind === SyntaxKind.VariableDeclarationList) {
const variable = (<VariableDeclarationList>node.initializer).declarations[0];
if (variable && isBindingPattern(variable.name)) {
error(variable.name, Diagnostics.The_left_hand_side_of_a_for_in_statement_cannot_be_a_destructuring_pattern);
}
checkForInOrForOfVariableDeclaration(node);
}
else {
// In a 'for-in' statement of the form
// for (Var in Expr) Statement
// Var must be an expression classified as a reference of type Any or the String primitive type,
// and Expr must be an expression of type Any, an object type, or a type parameter type.
const varExpr = <Expression>node.initializer;
const leftType = checkExpression(varExpr);
if (varExpr.kind === SyntaxKind.ArrayLiteralExpression || varExpr.kind === SyntaxKind.ObjectLiteralExpression) {
error(varExpr, Diagnostics.The_left_hand_side_of_a_for_in_statement_cannot_be_a_destructuring_pattern);
}
else if (!isTypeAssignableTo(getIndexTypeOrString(rightType), leftType)) {
error(varExpr, Diagnostics.The_left_hand_side_of_a_for_in_statement_must_be_of_type_string_or_any);
}
else {
// run check only former check succeeded to avoid cascading errors
checkReferenceExpression(varExpr, Diagnostics.The_left_hand_side_of_a_for_in_statement_must_be_a_variable_or_a_property_access);
}
}
// 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 (!isTypeAnyOrAllConstituentTypesHaveKind(rightType, TypeFlags.Object | TypeFlags.TypeVariable | TypeFlags.NonPrimitive)) {
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);
if (node.locals) {
registerForUnusedIdentifiersCheck(node);
}
}
function checkForInOrForOfVariableDeclaration(iterationStatement: ForInStatement | ForOfStatement): void {
const variableDeclarationList = <VariableDeclarationList>iterationStatement.initializer;
// checkGrammarForInOrForOfStatement will check that there is exactly one declaration.
if (variableDeclarationList.declarations.length >= 1) {
const decl = variableDeclarationList.declarations[0];
checkVariableDeclaration(decl);
}
}
function checkRightHandSideOfForOf(rhsExpression: Expression, awaitModifier: AwaitKeywordToken | undefined): Type {
const expressionType = checkNonNullExpression(rhsExpression);
return checkIteratedTypeOrElementType(expressionType, rhsExpression, /*allowStringInput*/ true, awaitModifier !== undefined);
}
function checkIteratedTypeOrElementType(inputType: Type, errorNode: Node, allowStringInput: boolean, allowAsyncIterable: boolean): Type {
if (isTypeAny(inputType)) {
return inputType;
}
return getIteratedTypeOrElementType(inputType, errorNode, allowStringInput, allowAsyncIterable, /*checkAssignability*/ true) || anyType;
}
/**
* When consuming an iterable type in a for..of, spread, or iterator destructuring assignment
* we want to get the iterated type of an iterable for ES2015 or later, or the iterated type
* of a iterable (if defined globally) or element type of an array like for ES2015 or earlier.
*/
function getIteratedTypeOrElementType(inputType: Type, errorNode: Node, allowStringInput: boolean, allowAsyncIterable: boolean, checkAssignability: boolean): Type {
const uplevelIteration = languageVersion >= ScriptTarget.ES2015;
const downlevelIteration = !uplevelIteration && compilerOptions.downlevelIteration;
// Get the iterated type of an `Iterable<T>` or `IterableIterator<T>` only in ES2015
// or higher, when inside of an async generator or for-await-if, or when
// downlevelIteration is requested.
if (uplevelIteration || downlevelIteration || allowAsyncIterable) {
// We only report errors for an invalid iterable type in ES2015 or higher.
const iteratedType = getIteratedTypeOfIterable(inputType, uplevelIteration ? errorNode : undefined, allowAsyncIterable, allowAsyncIterable, checkAssignability);
if (iteratedType || uplevelIteration) {
return iteratedType;
}
}
let arrayType = inputType;
let reportedError = false;
let hasStringConstituent = false;
// If strings are permitted, remove any string-like constituents from the array type.
// This allows us to find other non-string element types from an array unioned with
// a string.
if (allowStringInput) {
if (arrayType.flags & TypeFlags.Union) {
// After we remove all types that are StringLike, we will know if there was a string constituent
// based on whether the result of filter is a new array.
const arrayTypes = (<UnionType>inputType).types;
const filteredTypes = filter(arrayTypes, t => !(t.flags & TypeFlags.StringLike));
if (filteredTypes !== arrayTypes) {
arrayType = getUnionType(filteredTypes, /*subtypeReduction*/ true);
}
}
else if (arrayType.flags & TypeFlags.StringLike) {
arrayType = neverType;
}
hasStringConstituent = arrayType !== inputType;
if (hasStringConstituent) {
if (languageVersion < ScriptTarget.ES5) {
if (errorNode) {
error(errorNode, Diagnostics.Using_a_string_in_a_for_of_statement_is_only_supported_in_ECMAScript_5_and_higher);
reportedError = true;
}
}
// Now that we've removed all the StringLike types, if no constituents remain, then the entire
// arrayOrStringType was a string.
if (arrayType.flags & TypeFlags.Never) {
return stringType;
}
}
}
if (!isArrayLikeType(arrayType)) {
if (errorNode && !reportedError) {
// Which error we report depends on whether we allow strings or if there was a
// string constituent. For example, if the input type is number | string, we
// want to say that number is not an array type. But if the input was just
// number and string input is allowed, we want to say that number is not an
// array type or a string type.
const diagnostic = !allowStringInput || hasStringConstituent
? downlevelIteration
? Diagnostics.Type_0_is_not_an_array_type_or_does_not_have_a_Symbol_iterator_method_that_returns_an_iterator
: Diagnostics.Type_0_is_not_an_array_type
: downlevelIteration
? Diagnostics.Type_0_is_not_an_array_type_or_a_string_type_or_does_not_have_a_Symbol_iterator_method_that_returns_an_iterator
: Diagnostics.Type_0_is_not_an_array_type_or_a_string_type;
error(errorNode, diagnostic, typeToString(arrayType));
}
return hasStringConstituent ? stringType : undefined;
}
const arrayElementType = getIndexTypeOfType(arrayType, IndexKind.Number);
if (hasStringConstituent && arrayElementType) {
// This is just an optimization for the case where arrayOrStringType is string | string[]
if (arrayElementType.flags & TypeFlags.StringLike) {
return stringType;
}
return getUnionType([arrayElementType, stringType], /*subtypeReduction*/ true);
}
return arrayElementType;
}
/**
* We want to treat type as an iterable, and get the type it is an iterable of. The iterable
* must have the following structure (annotated with the names of the variables below):
*
* { // iterable
* [Symbol.iterator]: { // iteratorMethod
* (): Iterator<T>
* }
* }
*
* For an async iterable, we expect the following structure:
*
* { // iterable
* [Symbol.asyncIterator]: { // iteratorMethod
* (): AsyncIterator<T>
* }
* }
*
* T is the type we are after. At every level that involves analyzing return types
* of signatures, we union the return types of all the signatures.
*
* Another thing to note is that at any step of this process, we could run into a dead end,
* meaning either the property is missing, or we run into the anyType. If either of these things
* happens, we return undefined to signal that we could not find the iterated type. If a property
* is missing, and the previous step did not result in 'any', then we also give an error if the
* caller requested it. Then the caller can decide what to do in the case where there is no iterated
* type. This is different from returning anyType, because that would signify that we have matched the
* whole pattern and that T (above) is 'any'.
*
* For a **for-of** statement, `yield*` (in a normal generator), spread, array
* destructuring, or normal generator we will only ever look for a `[Symbol.iterator]()`
* method.
*
* For an async generator we will only ever look at the `[Symbol.asyncIterator]()` method.
*
* For a **for-await-of** statement or a `yield*` in an async generator we will look for
* the `[Symbol.asyncIterator]()` method first, and then the `[Symbol.iterator]()` method.
*/
function getIteratedTypeOfIterable(type: Type, errorNode: Node | undefined, isAsyncIterable: boolean, allowNonAsyncIterables: boolean, checkAssignability: boolean): Type | undefined {
if (isTypeAny(type)) {
return undefined;
}
const typeAsIterable = <IterableOrIteratorType>type;
if (isAsyncIterable ? typeAsIterable.iteratedTypeOfAsyncIterable : typeAsIterable.iteratedTypeOfIterable) {
return isAsyncIterable ? typeAsIterable.iteratedTypeOfAsyncIterable : typeAsIterable.iteratedTypeOfIterable;
}
if (isAsyncIterable) {
// As an optimization, if the type is an instantiation of the global `AsyncIterable<T>`
// or the global `AsyncIterableIterator<T>` then just grab its type argument.
if (isReferenceToType(type, getGlobalAsyncIterableType(/*reportErrors*/ false)) ||
isReferenceToType(type, getGlobalAsyncIterableIteratorType(/*reportErrors*/ false))) {
return typeAsIterable.iteratedTypeOfAsyncIterable = (<GenericType>type).typeArguments[0];
}
}
if (!isAsyncIterable || allowNonAsyncIterables) {
// As an optimization, if the type is an instantiation of the global `Iterable<T>` or
// `IterableIterator<T>` then just grab its type argument.
if (isReferenceToType(type, getGlobalIterableType(/*reportErrors*/ false)) ||
isReferenceToType(type, getGlobalIterableIteratorType(/*reportErrors*/ false))) {
return isAsyncIterable
? typeAsIterable.iteratedTypeOfAsyncIterable = (<GenericType>type).typeArguments[0]
: typeAsIterable.iteratedTypeOfIterable = (<GenericType>type).typeArguments[0];
}
}
let iteratorMethodSignatures: Signature[];
let isNonAsyncIterable = false;
if (isAsyncIterable) {
const iteratorMethod = getTypeOfPropertyOfType(type, getPropertyNameForKnownSymbolName("asyncIterator"));
if (isTypeAny(iteratorMethod)) {
return undefined;
}
iteratorMethodSignatures = iteratorMethod && getSignaturesOfType(iteratorMethod, SignatureKind.Call);
}
if (!isAsyncIterable || (allowNonAsyncIterables && !some(iteratorMethodSignatures))) {
const iteratorMethod = getTypeOfPropertyOfType(type, getPropertyNameForKnownSymbolName("iterator"));
if (isTypeAny(iteratorMethod)) {
return undefined;
}
iteratorMethodSignatures = iteratorMethod && getSignaturesOfType(iteratorMethod, SignatureKind.Call);
isNonAsyncIterable = true;
}
if (some(iteratorMethodSignatures)) {
const iteratorMethodReturnType = getUnionType(map(iteratorMethodSignatures, getReturnTypeOfSignature), /*subtypeReduction*/ true);
const iteratedType = getIteratedTypeOfIterator(iteratorMethodReturnType, errorNode, /*isAsyncIterator*/ !isNonAsyncIterable);
if (checkAssignability && errorNode && iteratedType) {
// If `checkAssignability` was specified, we were called from
// `checkIteratedTypeOrElementType`. As such, we need to validate that
// the type passed in is actually an Iterable.
checkTypeAssignableTo(type, isNonAsyncIterable
? createIterableType(iteratedType)
: createAsyncIterableType(iteratedType), errorNode);
}
return isAsyncIterable
? typeAsIterable.iteratedTypeOfAsyncIterable = iteratedType
: typeAsIterable.iteratedTypeOfIterable = iteratedType;
}
if (errorNode) {
error(errorNode,
isAsyncIterable
? Diagnostics.Type_must_have_a_Symbol_asyncIterator_method_that_returns_an_async_iterator
: Diagnostics.Type_must_have_a_Symbol_iterator_method_that_returns_an_iterator);
}
return undefined;
}
/**
* This function has very similar logic as getIteratedTypeOfIterable, except that it operates on
* Iterators instead of Iterables. Here is the structure:
*
* { // iterator
* next: { // nextMethod
* (): { // nextResult
* value: T // nextValue
* }
* }
* }
*
* For an async iterator, we expect the following structure:
*
* { // iterator
* next: { // nextMethod
* (): PromiseLike<{ // nextResult
* value: T // nextValue
* }>
* }
* }
*/
function getIteratedTypeOfIterator(type: Type, errorNode: Node | undefined, isAsyncIterator: boolean): Type | undefined {
if (isTypeAny(type)) {
return undefined;
}
const typeAsIterator = <IterableOrIteratorType>type;
if (isAsyncIterator ? typeAsIterator.iteratedTypeOfAsyncIterator : typeAsIterator.iteratedTypeOfIterator) {
return isAsyncIterator ? typeAsIterator.iteratedTypeOfAsyncIterator : typeAsIterator.iteratedTypeOfIterator;
}
// As an optimization, if the type is an instantiation of the global `Iterator<T>` (for
// a non-async iterator) or the global `AsyncIterator<T>` (for an async-iterator) then
// just grab its type argument.
const getIteratorType = isAsyncIterator ? getGlobalAsyncIteratorType : getGlobalIteratorType;
if (isReferenceToType(type, getIteratorType(/*reportErrors*/ false))) {
return isAsyncIterator
? typeAsIterator.iteratedTypeOfAsyncIterator = (<GenericType>type).typeArguments[0]
: typeAsIterator.iteratedTypeOfIterator = (<GenericType>type).typeArguments[0];
}
// Both async and non-async iterators must have a `next` method.
const nextMethod = getTypeOfPropertyOfType(type, "next");
if (isTypeAny(nextMethod)) {
return undefined;
}
const nextMethodSignatures = nextMethod ? getSignaturesOfType(nextMethod, SignatureKind.Call) : emptyArray;
if (nextMethodSignatures.length === 0) {
if (errorNode) {
error(errorNode, isAsyncIterator
? Diagnostics.An_async_iterator_must_have_a_next_method
: Diagnostics.An_iterator_must_have_a_next_method);
}
return undefined;
}
let nextResult = getUnionType(map(nextMethodSignatures, getReturnTypeOfSignature), /*subtypeReduction*/ true);
if (isTypeAny(nextResult)) {
return undefined;
}
// For an async iterator, we must get the awaited type of the return type.
if (isAsyncIterator) {
nextResult = getAwaitedTypeOfPromise(nextResult, errorNode, Diagnostics.The_type_returned_by_the_next_method_of_an_async_iterator_must_be_a_promise_for_a_type_with_a_value_property);
if (isTypeAny(nextResult)) {
return undefined;
}
}
const nextValue = nextResult && getTypeOfPropertyOfType(nextResult, "value");
if (!nextValue) {
if (errorNode) {
error(errorNode, isAsyncIterator
? Diagnostics.The_type_returned_by_the_next_method_of_an_async_iterator_must_be_a_promise_for_a_type_with_a_value_property
: Diagnostics.The_type_returned_by_the_next_method_of_an_iterator_must_have_a_value_property);
}
return undefined;
}
return isAsyncIterator
? typeAsIterator.iteratedTypeOfAsyncIterator = nextValue
: typeAsIterator.iteratedTypeOfIterator = nextValue;
}
/**
* A generator may have a return type of `Iterator<T>`, `Iterable<T>`, or
* `IterableIterator<T>`. An async generator may have a return type of `AsyncIterator<T>`,
* `AsyncIterable<T>`, or `AsyncIterableIterator<T>`. This function can be used to extract
* the iterated type from this return type for contextual typing and verifying signatures.
*/
function getIteratedTypeOfGenerator(returnType: Type, isAsyncGenerator: boolean): Type {
if (isTypeAny(returnType)) {
return undefined;
}
return getIteratedTypeOfIterable(returnType, /*errorNode*/ undefined, isAsyncGenerator, /*allowNonAsyncIterables*/ false, /*checkAssignability*/ false)
|| getIteratedTypeOfIterator(returnType, /*errorNode*/ undefined, isAsyncGenerator);
}
function checkBreakOrContinueStatement(node: BreakOrContinueStatement) {
// Grammar checking
checkGrammarStatementInAmbientContext(node) || checkGrammarBreakOrContinueStatement(node);
// TODO: Check that target label is valid
}
function isGetAccessorWithAnnotatedSetAccessor(node: FunctionLikeDeclaration) {
return !!(node.kind === SyntaxKind.GetAccessor && getSetAccessorTypeAnnotationNode(getDeclarationOfKind<SetAccessorDeclaration>(node.symbol, SyntaxKind.SetAccessor)));
}
function isUnwrappedReturnTypeVoidOrAny(func: FunctionLikeDeclaration, returnType: Type): boolean {
const unwrappedReturnType = (getFunctionFlags(func) & FunctionFlags.AsyncGenerator) === FunctionFlags.Async
? getPromisedTypeOfPromise(returnType) // Async function
: returnType; // AsyncGenerator function, Generator function, or normal function
return unwrappedReturnType && maybeTypeOfKind(unwrappedReturnType, TypeFlags.Void | TypeFlags.Any);
}
function checkReturnStatement(node: ReturnStatement) {
// Grammar checking
if (!checkGrammarStatementInAmbientContext(node)) {
const functionBlock = getContainingFunction(node);
if (!functionBlock) {
grammarErrorOnFirstToken(node, Diagnostics.A_return_statement_can_only_be_used_within_a_function_body);
}
}
const func = getContainingFunction(node);
if (func) {
const signature = getSignatureFromDeclaration(func);
const returnType = getReturnTypeOfSignature(signature);
if (strictNullChecks || node.expression || returnType.flags & TypeFlags.Never) {
const exprType = node.expression ? checkExpressionCached(node.expression) : undefinedType;
const functionFlags = getFunctionFlags(func);
if (functionFlags & FunctionFlags.Generator) { // AsyncGenerator function or Generator function
// A generator does not need its return expressions checked against its return type.
// Instead, the yield expressions are checked against the element type.
// TODO: Check return expressions of generators when return type tracking is added
// for generators.
return;
}
if (func.kind === SyntaxKind.SetAccessor) {
if (node.expression) {
error(node, Diagnostics.Setters_cannot_return_a_value);
}
}
else if (func.kind === SyntaxKind.Constructor) {
if (node.expression && !checkTypeAssignableTo(exprType, returnType, node)) {
error(node, Diagnostics.Return_type_of_constructor_signature_must_be_assignable_to_the_instance_type_of_the_class);
}
}
else if (func.type || isGetAccessorWithAnnotatedSetAccessor(func)) {
if (functionFlags & FunctionFlags.Async) { // Async function
const promisedType = getPromisedTypeOfPromise(returnType);
const awaitedType = checkAwaitedType(exprType, node, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
if (promisedType) {
// If the function has a return type, but promisedType is
// undefined, an error will be reported in checkAsyncFunctionReturnType
// so we don't need to report one here.
checkTypeAssignableTo(awaitedType, promisedType, node);
}
}
else {
checkTypeAssignableTo(exprType, returnType, node);
}
}
}
else if (func.kind !== SyntaxKind.Constructor && compilerOptions.noImplicitReturns && !isUnwrappedReturnTypeVoidOrAny(func, returnType)) {
// The function has a return type, but the return statement doesn't have an expression.
error(node, Diagnostics.Not_all_code_paths_return_a_value);
}
}
}
function checkWithStatement(node: WithStatement) {
// Grammar checking for withStatement
if (!checkGrammarStatementInAmbientContext(node)) {
if (node.flags & NodeFlags.AwaitContext) {
grammarErrorOnFirstToken(node, Diagnostics.with_statements_are_not_allowed_in_an_async_function_block);
}
}
checkExpression(node.expression);
const sourceFile = getSourceFileOfNode(node);
if (!hasParseDiagnostics(sourceFile)) {
const start = getSpanOfTokenAtPosition(sourceFile, node.pos).start;
const end = node.statement.pos;
grammarErrorAtPos(sourceFile, start, end - start, Diagnostics.The_with_statement_is_not_supported_All_symbols_in_a_with_block_will_have_type_any);
}
}
function checkSwitchStatement(node: SwitchStatement) {
// Grammar checking
checkGrammarStatementInAmbientContext(node);
let firstDefaultClause: CaseOrDefaultClause;
let hasDuplicateDefaultClause = false;
const expressionType = checkExpression(node.expression);
const expressionIsLiteral = isLiteralType(expressionType);
forEach(node.caseBlock.clauses, clause => {
// Grammar check for duplicate default clauses, skip if we already report duplicate default clause
if (clause.kind === SyntaxKind.DefaultClause && !hasDuplicateDefaultClause) {
if (firstDefaultClause === undefined) {
firstDefaultClause = clause;
}
else {
const sourceFile = getSourceFileOfNode(node);
const start = skipTrivia(sourceFile.text, clause.pos);
const end = clause.statements.length > 0 ? clause.statements[0].pos : clause.end;
grammarErrorAtPos(sourceFile, start, end - start, Diagnostics.A_default_clause_cannot_appear_more_than_once_in_a_switch_statement);
hasDuplicateDefaultClause = true;
}
}
if (produceDiagnostics && clause.kind === SyntaxKind.CaseClause) {
const caseClause = <CaseClause>clause;
// TypeScript 1.0 spec (April 2014): 5.9
// In a 'switch' statement, each 'case' expression must be of a type that is comparable
// to or from the type of the 'switch' expression.
let caseType = checkExpression(caseClause.expression);
const caseIsLiteral = isLiteralType(caseType);
let comparedExpressionType = expressionType;
if (!caseIsLiteral || !expressionIsLiteral) {
caseType = caseIsLiteral ? getBaseTypeOfLiteralType(caseType) : caseType;
comparedExpressionType = getBaseTypeOfLiteralType(expressionType);
}
if (!isTypeEqualityComparableTo(comparedExpressionType, caseType)) {
// expressionType is not comparable to caseType, try the reversed check and report errors if it fails
checkTypeComparableTo(caseType, comparedExpressionType, caseClause.expression, /*headMessage*/ undefined);
}
}
forEach(clause.statements, checkSourceElement);
});
if (node.caseBlock.locals) {
registerForUnusedIdentifiersCheck(node.caseBlock);
}
}
function checkLabeledStatement(node: LabeledStatement) {
// Grammar checking
if (!checkGrammarStatementInAmbientContext(node)) {
findAncestor(node.parent,
current => {
if (isFunctionLike(current)) {
return "quit";
}
if (current.kind === SyntaxKind.LabeledStatement && (<LabeledStatement>current).label.text === node.label.text) {
const sourceFile = getSourceFileOfNode(node);
grammarErrorOnNode(node.label, Diagnostics.Duplicate_label_0, getTextOfNodeFromSourceText(sourceFile.text, node.label));
return true;
}
});
}
// ensure that label is unique
checkSourceElement(node.statement);
}
function checkThrowStatement(node: ThrowStatement) {
// Grammar checking
if (!checkGrammarStatementInAmbientContext(node)) {
if (node.expression === undefined) {
grammarErrorAfterFirstToken(node, Diagnostics.Line_break_not_permitted_here);
}
}
if (node.expression) {
checkExpression(node.expression);
}
}
function checkTryStatement(node: TryStatement) {
// Grammar checking
checkGrammarStatementInAmbientContext(node);
checkBlock(node.tryBlock);
const catchClause = node.catchClause;
if (catchClause) {
// Grammar checking
if (catchClause.variableDeclaration) {
if (catchClause.variableDeclaration.type) {
grammarErrorOnFirstToken(catchClause.variableDeclaration.type, Diagnostics.Catch_clause_variable_cannot_have_a_type_annotation);
}
else if (catchClause.variableDeclaration.initializer) {
grammarErrorOnFirstToken(catchClause.variableDeclaration.initializer, Diagnostics.Catch_clause_variable_cannot_have_an_initializer);
}
else {
const blockLocals = catchClause.block.locals;
if (blockLocals) {
forEachKey(catchClause.locals, caughtName => {
const blockLocal = blockLocals.get(caughtName);
if (blockLocal && (blockLocal.flags & SymbolFlags.BlockScopedVariable) !== 0) {
grammarErrorOnNode(blockLocal.valueDeclaration, Diagnostics.Cannot_redeclare_identifier_0_in_catch_clause, caughtName);
}
});
}
}
}
checkBlock(catchClause.block);
}
if (node.finallyBlock) {
checkBlock(node.finallyBlock);
}
}
function checkIndexConstraints(type: Type) {
const declaredNumberIndexer = getIndexDeclarationOfSymbol(type.symbol, IndexKind.Number);
const declaredStringIndexer = getIndexDeclarationOfSymbol(type.symbol, IndexKind.String);
const stringIndexType = getIndexTypeOfType(type, IndexKind.String);
const numberIndexType = getIndexTypeOfType(type, IndexKind.Number);
if (stringIndexType || numberIndexType) {
forEach(getPropertiesOfObjectType(type), prop => {
const propType = getTypeOfSymbol(prop);
checkIndexConstraintForProperty(prop, propType, type, declaredStringIndexer, stringIndexType, IndexKind.String);
checkIndexConstraintForProperty(prop, propType, type, declaredNumberIndexer, numberIndexType, IndexKind.Number);
});
if (getObjectFlags(type) & ObjectFlags.Class && isClassLike(type.symbol.valueDeclaration)) {
const classDeclaration = <ClassLikeDeclaration>type.symbol.valueDeclaration;
for (const member of classDeclaration.members) {
// Only process instance properties with computed names here.
// Static properties cannot be in conflict with indexers,
// and properties with literal names were already checked.
if (!(getModifierFlags(member) & ModifierFlags.Static) && hasDynamicName(member)) {
const propType = getTypeOfSymbol(member.symbol);
checkIndexConstraintForProperty(member.symbol, propType, type, declaredStringIndexer, stringIndexType, IndexKind.String);
checkIndexConstraintForProperty(member.symbol, propType, type, declaredNumberIndexer, numberIndexType, IndexKind.Number);
}
}
}
}
let errorNode: Node;
if (stringIndexType && numberIndexType) {
errorNode = declaredNumberIndexer || declaredStringIndexer;
// condition 'errorNode === undefined' may appear if types does not declare nor string neither number indexer
if (!errorNode && (getObjectFlags(type) & ObjectFlags.Interface)) {
const someBaseTypeHasBothIndexers = forEach(getBaseTypes(<InterfaceType>type), base => getIndexTypeOfType(base, IndexKind.String) && getIndexTypeOfType(base, IndexKind.Number));
errorNode = someBaseTypeHasBothIndexers ? undefined : type.symbol.declarations[0];
}
}
if (errorNode && !isTypeAssignableTo(numberIndexType, stringIndexType)) {
error(errorNode, Diagnostics.Numeric_index_type_0_is_not_assignable_to_string_index_type_1,
typeToString(numberIndexType), typeToString(stringIndexType));
}
function checkIndexConstraintForProperty(
prop: Symbol,
propertyType: Type,
containingType: Type,
indexDeclaration: Declaration,
indexType: Type,
indexKind: IndexKind): void {
if (!indexType) {
return;
}
const propDeclaration = prop.valueDeclaration;
// index is numeric and property name is not valid numeric literal
if (indexKind === IndexKind.Number && !(propDeclaration ? isNumericName(getNameOfDeclaration(propDeclaration)) : isNumericLiteralName(prop.name))) {
return;
}
// perform property check if property or indexer is declared in 'type'
// this allows us to rule out cases when both property and indexer are inherited from the base class
let errorNode: Node;
if (propDeclaration &&
(propDeclaration.kind === SyntaxKind.BinaryExpression ||
getNameOfDeclaration(propDeclaration).kind === SyntaxKind.ComputedPropertyName ||
prop.parent === containingType.symbol)) {
errorNode = propDeclaration;
}
else if (indexDeclaration) {
errorNode = indexDeclaration;
}
else if (getObjectFlags(containingType) & ObjectFlags.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
const someBaseClassHasBothPropertyAndIndexer = forEach(getBaseTypes(<InterfaceType>containingType), base => getPropertyOfObjectType(base, prop.name) && getIndexTypeOfType(base, indexKind));
errorNode = someBaseClassHasBothPropertyAndIndexer ? undefined : containingType.symbol.declarations[0];
}
if (errorNode && !isTypeAssignableTo(propertyType, indexType)) {
const errorMessage =
indexKind === IndexKind.String
? Diagnostics.Property_0_of_type_1_is_not_assignable_to_string_index_type_2
: Diagnostics.Property_0_of_type_1_is_not_assignable_to_numeric_index_type_2;
error(errorNode, errorMessage, symbolToString(prop), typeToString(propertyType), typeToString(indexType));
}
}
}
function checkTypeNameIsReserved(name: DeclarationName, message: DiagnosticMessage): void {
// TS 1.0 spec (April 2014): 3.6.1
// The predefined type keywords are reserved and cannot be used as names of user defined types.
switch ((<Identifier>name).text) {
case "any":
case "number":
case "boolean":
case "string":
case "symbol":
case "void":
case "object":
error(name, message, (<Identifier>name).text);
}
}
/**
* Check each type parameter and check that type parameters have no duplicate type parameter declarations
*/
function checkTypeParameters(typeParameterDeclarations: TypeParameterDeclaration[]) {
if (typeParameterDeclarations) {
let seenDefault = false;
for (let i = 0; i < typeParameterDeclarations.length; i++) {
const node = typeParameterDeclarations[i];
checkTypeParameter(node);
if (produceDiagnostics) {
if (node.default) {
seenDefault = true;
}
else if (seenDefault) {
error(node, Diagnostics.Required_type_parameters_may_not_follow_optional_type_parameters);
}
for (let j = 0; j < i; j++) {
if (typeParameterDeclarations[j].symbol === node.symbol) {
error(node.name, Diagnostics.Duplicate_identifier_0, declarationNameToString(node.name));
}
}
}
}
}
}
/** Check that type parameter lists are identical across multiple declarations */
function checkTypeParameterListsIdentical(symbol: Symbol) {
if (symbol.declarations.length === 1) {
return;
}
const links = getSymbolLinks(symbol);
if (!links.typeParametersChecked) {
links.typeParametersChecked = true;
const declarations = getClassOrInterfaceDeclarationsOfSymbol(symbol);
if (declarations.length <= 1) {
return;
}
const type = <InterfaceType>getDeclaredTypeOfSymbol(symbol);
if (!areTypeParametersIdentical(declarations, type.localTypeParameters)) {
// Report an error on every conflicting declaration.
const name = symbolToString(symbol);
for (const declaration of declarations) {
error(declaration.name, Diagnostics.All_declarations_of_0_must_have_identical_type_parameters, name);
}
}
}
}
function areTypeParametersIdentical(declarations: (ClassDeclaration | InterfaceDeclaration)[], typeParameters: TypeParameter[]) {
const maxTypeArgumentCount = length(typeParameters);
const minTypeArgumentCount = getMinTypeArgumentCount(typeParameters);
for (const declaration of declarations) {
// If this declaration has too few or too many type parameters, we report an error
const numTypeParameters = length(declaration.typeParameters);
if (numTypeParameters < minTypeArgumentCount || numTypeParameters > maxTypeArgumentCount) {
return false;
}
for (let i = 0; i < numTypeParameters; i++) {
const source = declaration.typeParameters[i];
const target = typeParameters[i];
// If the type parameter node does not have the same as the resolved type
// parameter at this position, we report an error.
if (source.name.text !== target.symbol.name) {
return false;
}
// If the type parameter node does not have an identical constraint as the resolved
// type parameter at this position, we report an error.
const sourceConstraint = source.constraint && getTypeFromTypeNode(source.constraint);
const targetConstraint = getConstraintFromTypeParameter(target);
if ((sourceConstraint || targetConstraint) &&
(!sourceConstraint || !targetConstraint || !isTypeIdenticalTo(sourceConstraint, targetConstraint))) {
return false;
}
// If the type parameter node has a default and it is not identical to the default
// for the type parameter at this position, we report an error.
const sourceDefault = source.default && getTypeFromTypeNode(source.default);
const targetDefault = getDefaultFromTypeParameter(target);
if (sourceDefault && targetDefault && !isTypeIdenticalTo(sourceDefault, targetDefault)) {
return false;
}
}
}
return true;
}
function checkClassExpression(node: ClassExpression): Type {
checkClassLikeDeclaration(node);
checkNodeDeferred(node);
return getTypeOfSymbol(getSymbolOfNode(node));
}
function checkClassExpressionDeferred(node: ClassExpression) {
forEach(node.members, checkSourceElement);
registerForUnusedIdentifiersCheck(node);
}
function checkClassDeclaration(node: ClassDeclaration) {
if (!node.name && !(getModifierFlags(node) & ModifierFlags.Default)) {
grammarErrorOnFirstToken(node, Diagnostics.A_class_declaration_without_the_default_modifier_must_have_a_name);
}
checkClassLikeDeclaration(node);
forEach(node.members, checkSourceElement);
registerForUnusedIdentifiersCheck(node);
}
function checkClassLikeDeclaration(node: ClassLikeDeclaration) {
checkGrammarClassLikeDeclaration(node);
checkDecorators(node);
if (node.name) {
checkTypeNameIsReserved(node.name, Diagnostics.Class_name_cannot_be_0);
checkCollisionWithCapturedThisVariable(node, node.name);
checkCollisionWithCapturedNewTargetVariable(node, node.name);
checkCollisionWithRequireExportsInGeneratedCode(node, node.name);
checkCollisionWithGlobalPromiseInGeneratedCode(node, node.name);
}
checkTypeParameters(node.typeParameters);
checkExportsOnMergedDeclarations(node);
const symbol = getSymbolOfNode(node);
const type = <InterfaceType>getDeclaredTypeOfSymbol(symbol);
const typeWithThis = getTypeWithThisArgument(type);
const staticType = <ObjectType>getTypeOfSymbol(symbol);
checkTypeParameterListsIdentical(symbol);
checkClassForDuplicateDeclarations(node);
// Only check for reserved static identifiers on non-ambient context.
if (!isInAmbientContext(node)) {
checkClassForStaticPropertyNameConflicts(node);
}
const baseTypeNode = getClassExtendsHeritageClauseElement(node);
if (baseTypeNode) {
if (languageVersion < ScriptTarget.ES2015) {
checkExternalEmitHelpers(baseTypeNode.parent, ExternalEmitHelpers.Extends);
}
const baseTypes = getBaseTypes(type);
if (baseTypes.length && produceDiagnostics) {
const baseType = baseTypes[0];
const baseConstructorType = getBaseConstructorTypeOfClass(type);
const staticBaseType = getApparentType(baseConstructorType);
checkBaseTypeAccessibility(staticBaseType, baseTypeNode);
checkSourceElement(baseTypeNode.expression);
if (baseTypeNode.typeArguments) {
forEach(baseTypeNode.typeArguments, checkSourceElement);
for (const constructor of getConstructorsForTypeArguments(staticBaseType, baseTypeNode.typeArguments, baseTypeNode)) {
if (!checkTypeArgumentConstraints(constructor.typeParameters, baseTypeNode.typeArguments)) {
break;
}
}
}
checkTypeAssignableTo(typeWithThis, getTypeWithThisArgument(baseType, type.thisType), node.name || node, Diagnostics.Class_0_incorrectly_extends_base_class_1);
checkTypeAssignableTo(staticType, getTypeWithoutSignatures(staticBaseType), node.name || node,
Diagnostics.Class_static_side_0_incorrectly_extends_base_class_static_side_1);
if (baseConstructorType.flags & TypeFlags.TypeVariable && !isMixinConstructorType(staticType)) {
error(node.name || node, Diagnostics.A_mixin_class_must_have_a_constructor_with_a_single_rest_parameter_of_type_any);
}
if (!(staticBaseType.symbol && staticBaseType.symbol.flags & SymbolFlags.Class) && !(baseConstructorType.flags & TypeFlags.TypeVariable)) {
// When the static base type is a "class-like" constructor function (but not actually a class), we verify
// that all instantiated base constructor signatures return the same type. We can simply compare the type
// references (as opposed to checking the structure of the types) because elsewhere we have already checked
// that the base type is a class or interface type (and not, for example, an anonymous object type).
const constructors = getInstantiatedConstructorsForTypeArguments(staticBaseType, baseTypeNode.typeArguments, baseTypeNode);
if (forEach(constructors, sig => getReturnTypeOfSignature(sig) !== baseType)) {
error(baseTypeNode.expression, Diagnostics.Base_constructors_must_all_have_the_same_return_type);
}
}
checkKindsOfPropertyMemberOverrides(type, baseType);
}
}
const implementedTypeNodes = getClassImplementsHeritageClauseElements(node);
if (implementedTypeNodes) {
for (const typeRefNode of implementedTypeNodes) {
if (!isEntityNameExpression(typeRefNode.expression)) {
error(typeRefNode.expression, Diagnostics.A_class_can_only_implement_an_identifier_Slashqualified_name_with_optional_type_arguments);
}
checkTypeReferenceNode(typeRefNode);
if (produceDiagnostics) {
const t = getTypeFromTypeNode(typeRefNode);
if (t !== unknownType) {
if (isValidBaseType(t)) {
checkTypeAssignableTo(typeWithThis, getTypeWithThisArgument(t, type.thisType), node.name || node, Diagnostics.Class_0_incorrectly_implements_interface_1);
}
else {
error(typeRefNode, Diagnostics.A_class_may_only_implement_another_class_or_interface);
}
}
}
}
}
if (produceDiagnostics) {
checkIndexConstraints(type);
checkTypeForDuplicateIndexSignatures(node);
}
}
function checkBaseTypeAccessibility(type: Type, node: ExpressionWithTypeArguments) {
const signatures = getSignaturesOfType(type, SignatureKind.Construct);
if (signatures.length) {
const declaration = signatures[0].declaration;
if (declaration && getModifierFlags(declaration) & ModifierFlags.Private) {
const typeClassDeclaration = <ClassLikeDeclaration>getClassLikeDeclarationOfSymbol(type.symbol);
if (!isNodeWithinClass(node, typeClassDeclaration)) {
error(node, Diagnostics.Cannot_extend_a_class_0_Class_constructor_is_marked_as_private, getFullyQualifiedName(type.symbol));
}
}
}
}
function getTargetSymbol(s: Symbol) {
// if symbol is instantiated its flags are not copied from the 'target'
// so we'll need to get back original 'target' symbol to work with correct set of flags
return getCheckFlags(s) & CheckFlags.Instantiated ? (<TransientSymbol>s).target : s;
}
function getClassLikeDeclarationOfSymbol(symbol: Symbol): Declaration {
return forEach(symbol.declarations, d => isClassLike(d) ? d : undefined);
}
function getClassOrInterfaceDeclarationsOfSymbol(symbol: Symbol) {
return filter(symbol.declarations, (d: Declaration): d is ClassDeclaration | InterfaceDeclaration =>
d.kind === SyntaxKind.ClassDeclaration || d.kind === SyntaxKind.InterfaceDeclaration);
}
function checkKindsOfPropertyMemberOverrides(type: InterfaceType, baseType: BaseType): 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
const baseProperties = getPropertiesOfType(baseType);
for (const baseProperty of baseProperties) {
const base = getTargetSymbol(baseProperty);
if (base.flags & SymbolFlags.Prototype) {
continue;
}
const derived = getTargetSymbol(getPropertyOfObjectType(type, base.name));
const baseDeclarationFlags = getDeclarationModifierFlagsFromSymbol(base);
Debug.assert(!!derived, "derived should point to something, even if it is the base class' declaration.");
if (derived) {
// In order to resolve whether the inherited method was overridden in the base class or not,
// we compare the Symbols obtained. Since getTargetSymbol returns the symbol on the *uninstantiated*
// type declaration, derived and base resolve to the same symbol even in the case of generic classes.
if (derived === base) {
// derived class inherits base without override/redeclaration
const derivedClassDecl = getClassLikeDeclarationOfSymbol(type.symbol);
// It is an error to inherit an abstract member without implementing it or being declared abstract.
// If there is no declaration for the derived class (as in the case of class expressions),
// then the class cannot be declared abstract.
if (baseDeclarationFlags & ModifierFlags.Abstract && (!derivedClassDecl || !(getModifierFlags(derivedClassDecl) & ModifierFlags.Abstract))) {
if (derivedClassDecl.kind === SyntaxKind.ClassExpression) {
error(derivedClassDecl, Diagnostics.Non_abstract_class_expression_does_not_implement_inherited_abstract_member_0_from_class_1,
symbolToString(baseProperty), typeToString(baseType));
}
else {
error(derivedClassDecl, Diagnostics.Non_abstract_class_0_does_not_implement_inherited_abstract_member_1_from_class_2,
typeToString(type), symbolToString(baseProperty), typeToString(baseType));
}
}
}
else {
// derived overrides base.
const derivedDeclarationFlags = getDeclarationModifierFlagsFromSymbol(derived);
if (baseDeclarationFlags & ModifierFlags.Private || derivedDeclarationFlags & ModifierFlags.Private) {
// either base or derived property is private - not override, skip it
continue;
}
if (isMethodLike(base) && isMethodLike(derived) || base.flags & SymbolFlags.PropertyOrAccessor && derived.flags & SymbolFlags.PropertyOrAccessor) {
// method is overridden with method or property/accessor is overridden with property/accessor - correct case
continue;
}
let errorMessage: DiagnosticMessage;
if (isMethodLike(base)) {
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 {
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) {
errorMessage = Diagnostics.Class_0_defines_instance_member_property_1_but_extended_class_2_defines_it_as_instance_member_function;
}
else {
errorMessage = Diagnostics.Class_0_defines_instance_member_accessor_1_but_extended_class_2_defines_it_as_instance_member_function;
}
error(getNameOfDeclaration(derived.valueDeclaration) || derived.valueDeclaration, errorMessage, typeToString(baseType), symbolToString(base), typeToString(type));
}
}
}
}
function isAccessor(kind: SyntaxKind): boolean {
return kind === SyntaxKind.GetAccessor || kind === SyntaxKind.SetAccessor;
}
function checkInheritedPropertiesAreIdentical(type: InterfaceType, typeNode: Node): boolean {
const baseTypes = getBaseTypes(type);
if (baseTypes.length < 2) {
return true;
}
const seen = createMap<{ prop: Symbol; containingType: Type }>();
forEach(resolveDeclaredMembers(type).declaredProperties, p => { seen.set(p.name, { prop: p, containingType: type }); });
let ok = true;
for (const base of baseTypes) {
const properties = getPropertiesOfType(getTypeWithThisArgument(base, type.thisType));
for (const prop of properties) {
const existing = seen.get(prop.name);
if (!existing) {
seen.set(prop.name, { prop: prop, containingType: base });
}
else {
const isInheritedProperty = existing.containingType !== type;
if (isInheritedProperty && !isPropertyIdenticalTo(existing.prop, prop)) {
ok = false;
const typeName1 = typeToString(existing.containingType);
const typeName2 = typeToString(base);
let errorInfo = chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Named_property_0_of_types_1_and_2_are_not_identical, symbolToString(prop), typeName1, typeName2);
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Interface_0_cannot_simultaneously_extend_types_1_and_2, typeToString(type), typeName1, typeName2);
diagnostics.add(createDiagnosticForNodeFromMessageChain(typeNode, errorInfo));
}
}
}
}
return ok;
}
function checkInterfaceDeclaration(node: InterfaceDeclaration) {
// Grammar checking
checkGrammarDecorators(node) || checkGrammarModifiers(node) || checkGrammarInterfaceDeclaration(node);
checkTypeParameters(node.typeParameters);
if (produceDiagnostics) {
checkTypeNameIsReserved(node.name, Diagnostics.Interface_name_cannot_be_0);
checkExportsOnMergedDeclarations(node);
const symbol = getSymbolOfNode(node);
checkTypeParameterListsIdentical(symbol);
// Only check this symbol once
const firstInterfaceDecl = getDeclarationOfKind<InterfaceDeclaration>(symbol, SyntaxKind.InterfaceDeclaration);
if (node === firstInterfaceDecl) {
const type = <InterfaceType>getDeclaredTypeOfSymbol(symbol);
const typeWithThis = getTypeWithThisArgument(type);
// run subsequent checks only if first set succeeded
if (checkInheritedPropertiesAreIdentical(type, node.name)) {
for (const baseType of getBaseTypes(type)) {
checkTypeAssignableTo(typeWithThis, getTypeWithThisArgument(baseType, type.thisType), node.name, Diagnostics.Interface_0_incorrectly_extends_interface_1);
}
checkIndexConstraints(type);
}
}
checkObjectTypeForDuplicateDeclarations(node);
}
forEach(getInterfaceBaseTypeNodes(node), heritageElement => {
if (!isEntityNameExpression(heritageElement.expression)) {
error(heritageElement.expression, Diagnostics.An_interface_can_only_extend_an_identifier_Slashqualified_name_with_optional_type_arguments);
}
checkTypeReferenceNode(heritageElement);
});
forEach(node.members, checkSourceElement);
if (produceDiagnostics) {
checkTypeForDuplicateIndexSignatures(node);
registerForUnusedIdentifiersCheck(node);
}
}
function checkTypeAliasDeclaration(node: TypeAliasDeclaration) {
// Grammar checking
checkGrammarDecorators(node) || checkGrammarModifiers(node);
checkTypeNameIsReserved(node.name, Diagnostics.Type_alias_name_cannot_be_0);
checkTypeParameters(node.typeParameters);
checkSourceElement(node.type);
}
function computeEnumMemberValues(node: EnumDeclaration) {
const nodeLinks = getNodeLinks(node);
if (!(nodeLinks.flags & NodeCheckFlags.EnumValuesComputed)) {
nodeLinks.flags |= NodeCheckFlags.EnumValuesComputed;
let autoValue = 0;
for (const member of node.members) {
const value = computeMemberValue(member, autoValue);
getNodeLinks(member).enumMemberValue = value;
autoValue = typeof value === "number" ? value + 1 : undefined;
}
}
}
function computeMemberValue(member: EnumMember, autoValue: number) {
if (isComputedNonLiteralName(<PropertyName>member.name)) {
error(member.name, Diagnostics.Computed_property_names_are_not_allowed_in_enums);
}
else {
const text = getTextOfPropertyName(<PropertyName>member.name);
if (isNumericLiteralName(text) && !isInfinityOrNaNString(text)) {
error(member.name, Diagnostics.An_enum_member_cannot_have_a_numeric_name);
}
}
if (member.initializer) {
return computeConstantValue(member);
}
// In ambient enum declarations that specify no const modifier, enum member declarations that omit
// a value are considered computed members (as opposed to having auto-incremented values).
if (isInAmbientContext(member.parent) && !isConst(member.parent)) {
return undefined;
}
// If the member declaration specifies no value, the member is considered a constant enum member.
// If the member is the first member in the enum declaration, it is assigned the value zero.
// Otherwise, it is assigned the value of the immediately preceding member plus one, and an error
// occurs if the immediately preceding member is not a constant enum member.
if (autoValue !== undefined) {
return autoValue;
}
error(member.name, Diagnostics.Enum_member_must_have_initializer);
return undefined;
}
function computeConstantValue(member: EnumMember): string | number {
const enumKind = getEnumKind(getSymbolOfNode(member.parent));
const isConstEnum = isConst(member.parent);
const initializer = member.initializer;
const value = enumKind === EnumKind.Literal && !isLiteralEnumMember(member) ? undefined : evaluate(initializer);
if (value !== undefined) {
if (isConstEnum && typeof value === "number" && !isFinite(value)) {
error(initializer, isNaN(value) ?
Diagnostics.const_enum_member_initializer_was_evaluated_to_disallowed_value_NaN :
Diagnostics.const_enum_member_initializer_was_evaluated_to_a_non_finite_value);
}
}
else if (enumKind === EnumKind.Literal) {
error(initializer, Diagnostics.Computed_values_are_not_permitted_in_an_enum_with_string_valued_members);
return 0;
}
else if (isConstEnum) {
error(initializer, Diagnostics.In_const_enum_declarations_member_initializer_must_be_constant_expression);
}
else if (isInAmbientContext(member.parent)) {
error(initializer, Diagnostics.In_ambient_enum_declarations_member_initializer_must_be_constant_expression);
}
else {
// Only here do we need to check that the initializer is assignable to the enum type.
checkTypeAssignableTo(checkExpression(initializer), getDeclaredTypeOfSymbol(getSymbolOfNode(member.parent)), initializer, /*headMessage*/ undefined);
}
return value;
function evaluate(expr: Expression): string | number {
switch (expr.kind) {
case SyntaxKind.PrefixUnaryExpression:
const value = evaluate((<PrefixUnaryExpression>expr).operand);
if (typeof value === "number") {
switch ((<PrefixUnaryExpression>expr).operator) {
case SyntaxKind.PlusToken: return value;
case SyntaxKind.MinusToken: return -value;
case SyntaxKind.TildeToken: return ~value;
}
}
break;
case SyntaxKind.BinaryExpression:
const left = evaluate((<BinaryExpression>expr).left);
const right = evaluate((<BinaryExpression>expr).right);
if (typeof left === "number" && typeof right === "number") {
switch ((<BinaryExpression>expr).operatorToken.kind) {
case SyntaxKind.BarToken: return left | right;
case SyntaxKind.AmpersandToken: return left & right;
case SyntaxKind.GreaterThanGreaterThanToken: return left >> right;
case SyntaxKind.GreaterThanGreaterThanGreaterThanToken: return left >>> right;
case SyntaxKind.LessThanLessThanToken: return left << right;
case SyntaxKind.CaretToken: return left ^ right;
case SyntaxKind.AsteriskToken: return left * right;
case SyntaxKind.SlashToken: return left / right;
case SyntaxKind.PlusToken: return left + right;
case SyntaxKind.MinusToken: return left - right;
case SyntaxKind.PercentToken: return left % right;
}
}
break;
case SyntaxKind.StringLiteral:
return (<StringLiteral>expr).text;
case SyntaxKind.NumericLiteral:
checkGrammarNumericLiteral(<NumericLiteral>expr);
return +(<NumericLiteral>expr).text;
case SyntaxKind.ParenthesizedExpression:
return evaluate((<ParenthesizedExpression>expr).expression);
case SyntaxKind.Identifier:
return nodeIsMissing(expr) ? 0 : evaluateEnumMember(expr, getSymbolOfNode(member.parent), (<Identifier>expr).text);
case SyntaxKind.ElementAccessExpression:
case SyntaxKind.PropertyAccessExpression:
if (isConstantMemberAccess(expr)) {
const type = getTypeOfExpression((<PropertyAccessExpression | ElementAccessExpression>expr).expression);
if (type.symbol && type.symbol.flags & SymbolFlags.Enum) {
const name = expr.kind === SyntaxKind.PropertyAccessExpression ?
(<PropertyAccessExpression>expr).name.text :
(<LiteralExpression>(<ElementAccessExpression>expr).argumentExpression).text;
return evaluateEnumMember(expr, type.symbol, name);
}
}
break;
}
return undefined;
}
function evaluateEnumMember(expr: Expression, enumSymbol: Symbol, name: string) {
const memberSymbol = enumSymbol.exports.get(name);
if (memberSymbol) {
const declaration = memberSymbol.valueDeclaration;
if (declaration !== member) {
if (isBlockScopedNameDeclaredBeforeUse(declaration, member)) {
return getNodeLinks(declaration).enumMemberValue;
}
error(expr, Diagnostics.A_member_initializer_in_a_enum_declaration_cannot_reference_members_declared_after_it_including_members_defined_in_other_enums);
return 0;
}
}
return undefined;
}
}
function isConstantMemberAccess(node: Expression): boolean {
return node.kind === SyntaxKind.Identifier ||
node.kind === SyntaxKind.PropertyAccessExpression && isConstantMemberAccess((<PropertyAccessExpression>node).expression) ||
node.kind === SyntaxKind.ElementAccessExpression && isConstantMemberAccess((<ElementAccessExpression>node).expression) &&
(<ElementAccessExpression>node).argumentExpression.kind === SyntaxKind.StringLiteral;
}
function checkEnumDeclaration(node: EnumDeclaration) {
if (!produceDiagnostics) {
return;
}
// Grammar checking
checkGrammarDecorators(node) || checkGrammarModifiers(node);
checkTypeNameIsReserved(node.name, Diagnostics.Enum_name_cannot_be_0);
checkCollisionWithCapturedThisVariable(node, node.name);
checkCollisionWithCapturedNewTargetVariable(node, node.name);
checkCollisionWithRequireExportsInGeneratedCode(node, node.name);
checkCollisionWithGlobalPromiseInGeneratedCode(node, node.name);
checkExportsOnMergedDeclarations(node);
computeEnumMemberValues(node);
const enumIsConst = isConst(node);
if (compilerOptions.isolatedModules && enumIsConst && isInAmbientContext(node)) {
error(node.name, Diagnostics.Ambient_const_enums_are_not_allowed_when_the_isolatedModules_flag_is_provided);
}
// Spec 2014 - Section 9.3:
// It isn't possible for one enum declaration to continue the automatic numbering sequence of another,
// and when an enum type has multiple declarations, only one declaration is permitted to omit a value
// for the first member.
//
// Only perform this check once per symbol
const enumSymbol = getSymbolOfNode(node);
const firstDeclaration = getDeclarationOfKind(enumSymbol, node.kind);
if (node === firstDeclaration) {
if (enumSymbol.declarations.length > 1) {
// check that const is placed\omitted on all enum declarations
forEach(enumSymbol.declarations, decl => {
if (isConstEnumDeclaration(decl) !== enumIsConst) {
error(getNameOfDeclaration(decl), Diagnostics.Enum_declarations_must_all_be_const_or_non_const);
}
});
}
let seenEnumMissingInitialInitializer = false;
forEach(enumSymbol.declarations, declaration => {
// return true if we hit a violation of the rule, false otherwise
if (declaration.kind !== SyntaxKind.EnumDeclaration) {
return false;
}
const enumDeclaration = <EnumDeclaration>declaration;
if (!enumDeclaration.members.length) {
return false;
}
const firstEnumMember = enumDeclaration.members[0];
if (!firstEnumMember.initializer) {
if (seenEnumMissingInitialInitializer) {
error(firstEnumMember.name, Diagnostics.In_an_enum_with_multiple_declarations_only_one_declaration_can_omit_an_initializer_for_its_first_enum_element);
}
else {
seenEnumMissingInitialInitializer = true;
}
}
});
}
}
function getFirstNonAmbientClassOrFunctionDeclaration(symbol: Symbol): Declaration {
const declarations = symbol.declarations;
for (const declaration of declarations) {
if ((declaration.kind === SyntaxKind.ClassDeclaration ||
(declaration.kind === SyntaxKind.FunctionDeclaration && nodeIsPresent((<FunctionLikeDeclaration>declaration).body))) &&
!isInAmbientContext(declaration)) {
return declaration;
}
}
return undefined;
}
function inSameLexicalScope(node1: Node, node2: Node) {
const container1 = getEnclosingBlockScopeContainer(node1);
const container2 = getEnclosingBlockScopeContainer(node2);
if (isGlobalSourceFile(container1)) {
return isGlobalSourceFile(container2);
}
else if (isGlobalSourceFile(container2)) {
return false;
}
else {
return container1 === container2;
}
}
function checkModuleDeclaration(node: ModuleDeclaration) {
if (produceDiagnostics) {
// Grammar checking
const isGlobalAugmentation = isGlobalScopeAugmentation(node);
const inAmbientContext = isInAmbientContext(node);
if (isGlobalAugmentation && !inAmbientContext) {
error(node.name, Diagnostics.Augmentations_for_the_global_scope_should_have_declare_modifier_unless_they_appear_in_already_ambient_context);
}
const isAmbientExternalModule = isAmbientModule(node);
const contextErrorMessage = isAmbientExternalModule
? Diagnostics.An_ambient_module_declaration_is_only_allowed_at_the_top_level_in_a_file
: Diagnostics.A_namespace_declaration_is_only_allowed_in_a_namespace_or_module;
if (checkGrammarModuleElementContext(node, contextErrorMessage)) {
// If we hit a module declaration in an illegal context, just bail out to avoid cascading errors.
return;
}
if (!checkGrammarDecorators(node) && !checkGrammarModifiers(node)) {
if (!inAmbientContext && node.name.kind === SyntaxKind.StringLiteral) {
grammarErrorOnNode(node.name, Diagnostics.Only_ambient_modules_can_use_quoted_names);
}
}
if (isIdentifier(node.name)) {
checkCollisionWithCapturedThisVariable(node, node.name);
checkCollisionWithRequireExportsInGeneratedCode(node, node.name);
checkCollisionWithGlobalPromiseInGeneratedCode(node, node.name);
}
checkExportsOnMergedDeclarations(node);
const symbol = getSymbolOfNode(node);
// The following checks only apply on a non-ambient instantiated module declaration.
if (symbol.flags & SymbolFlags.ValueModule
&& symbol.declarations.length > 1
&& !inAmbientContext
&& isInstantiatedModule(node, compilerOptions.preserveConstEnums || compilerOptions.isolatedModules)) {
const firstNonAmbientClassOrFunc = getFirstNonAmbientClassOrFunctionDeclaration(symbol);
if (firstNonAmbientClassOrFunc) {
if (getSourceFileOfNode(node) !== getSourceFileOfNode(firstNonAmbientClassOrFunc)) {
error(node.name, Diagnostics.A_namespace_declaration_cannot_be_in_a_different_file_from_a_class_or_function_with_which_it_is_merged);
}
else if (node.pos < firstNonAmbientClassOrFunc.pos) {
error(node.name, Diagnostics.A_namespace_declaration_cannot_be_located_prior_to_a_class_or_function_with_which_it_is_merged);
}
}
// if the module merges with a class declaration in the same lexical scope,
// we need to track this to ensure the correct emit.
const mergedClass = getDeclarationOfKind(symbol, SyntaxKind.ClassDeclaration);
if (mergedClass &&
inSameLexicalScope(node, mergedClass)) {
getNodeLinks(node).flags |= NodeCheckFlags.LexicalModuleMergesWithClass;
}
}
if (isAmbientExternalModule) {
if (isExternalModuleAugmentation(node)) {
// body of the augmentation should be checked for consistency only if augmentation was applied to its target (either global scope or module)
// otherwise we'll be swamped in cascading errors.
// We can detect if augmentation was applied using following rules:
// - augmentation for a global scope is always applied
// - augmentation for some external module is applied if symbol for augmentation is merged (it was combined with target module).
const checkBody = isGlobalAugmentation || (getSymbolOfNode(node).flags & SymbolFlags.Transient);
if (checkBody && node.body) {
// body of ambient external module is always a module block
for (const statement of (<ModuleBlock>node.body).statements) {
checkModuleAugmentationElement(statement, isGlobalAugmentation);
}
}
}
else if (isGlobalSourceFile(node.parent)) {
if (isGlobalAugmentation) {
error(node.name, Diagnostics.Augmentations_for_the_global_scope_can_only_be_directly_nested_in_external_modules_or_ambient_module_declarations);
}
else if (isExternalModuleNameRelative(node.name.text)) {
error(node.name, Diagnostics.Ambient_module_declaration_cannot_specify_relative_module_name);
}
}
else {
if (isGlobalAugmentation) {
error(node.name, Diagnostics.Augmentations_for_the_global_scope_can_only_be_directly_nested_in_external_modules_or_ambient_module_declarations);
}
else {
// Node is not an augmentation and is not located on the script level.
// This means that this is declaration of ambient module that is located in other module or namespace which is prohibited.
error(node.name, Diagnostics.Ambient_modules_cannot_be_nested_in_other_modules_or_namespaces);
}
}
}
}
if (node.body) {
checkSourceElement(node.body);
if (!isGlobalScopeAugmentation(node)) {
registerForUnusedIdentifiersCheck(node);
}
}
}
function checkModuleAugmentationElement(node: Node, isGlobalAugmentation: boolean): void {
switch (node.kind) {
case SyntaxKind.VariableStatement:
// error each individual name in variable statement instead of marking the entire variable statement
for (const decl of (<VariableStatement>node).declarationList.declarations) {
checkModuleAugmentationElement(decl, isGlobalAugmentation);
}
break;
case SyntaxKind.ExportAssignment:
case SyntaxKind.ExportDeclaration:
grammarErrorOnFirstToken(node, Diagnostics.Exports_and_export_assignments_are_not_permitted_in_module_augmentations);
break;
case SyntaxKind.ImportEqualsDeclaration:
case SyntaxKind.ImportDeclaration:
grammarErrorOnFirstToken(node, Diagnostics.Imports_are_not_permitted_in_module_augmentations_Consider_moving_them_to_the_enclosing_external_module);
break;
case SyntaxKind.BindingElement:
case SyntaxKind.VariableDeclaration:
const name = (<VariableDeclaration | BindingElement>node).name;
if (isBindingPattern(name)) {
for (const el of name.elements) {
// mark individual names in binding pattern
checkModuleAugmentationElement(el, isGlobalAugmentation);
}
break;
}
// falls through
case SyntaxKind.ClassDeclaration:
case SyntaxKind.EnumDeclaration:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.ModuleDeclaration:
case SyntaxKind.TypeAliasDeclaration:
if (isGlobalAugmentation) {
return;
}
const symbol = getSymbolOfNode(node);
if (symbol) {
// module augmentations cannot introduce new names on the top level scope of the module
// this is done it two steps
// 1. quick check - if symbol for node is not merged - this is local symbol to this augmentation - report error
// 2. main check - report error if value declaration of the parent symbol is module augmentation)
let reportError = !(symbol.flags & SymbolFlags.Transient);
if (!reportError) {
// symbol should not originate in augmentation
reportError = isExternalModuleAugmentation(symbol.parent.declarations[0]);
}
}
break;
}
}
function getFirstIdentifier(node: EntityNameOrEntityNameExpression): Identifier {
switch (node.kind) {
case SyntaxKind.Identifier:
return <Identifier>node;
case SyntaxKind.QualifiedName:
do {
node = (<QualifiedName>node).left;
} while (node.kind !== SyntaxKind.Identifier);
return <Identifier>node;
case SyntaxKind.PropertyAccessExpression:
do {
node = (<PropertyAccessEntityNameExpression>node).expression;
} while (node.kind !== SyntaxKind.Identifier);
return <Identifier>node;
}
}
function checkExternalImportOrExportDeclaration(node: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration): boolean {
const moduleName = getExternalModuleName(node);
if (!nodeIsMissing(moduleName) && moduleName.kind !== SyntaxKind.StringLiteral) {
error(moduleName, Diagnostics.String_literal_expected);
return false;
}
const inAmbientExternalModule = node.parent.kind === SyntaxKind.ModuleBlock && isAmbientModule(<ModuleDeclaration>node.parent.parent);
if (node.parent.kind !== SyntaxKind.SourceFile && !inAmbientExternalModule) {
error(moduleName, node.kind === SyntaxKind.ExportDeclaration ?
Diagnostics.Export_declarations_are_not_permitted_in_a_namespace :
Diagnostics.Import_declarations_in_a_namespace_cannot_reference_a_module);
return false;
}
if (inAmbientExternalModule && isExternalModuleNameRelative((<LiteralExpression>moduleName).text)) {
// we have already reported errors on top level imports\exports in external module augmentations in checkModuleDeclaration
// no need to do this again.
if (!isTopLevelInExternalModuleAugmentation(node)) {
// TypeScript 1.0 spec (April 2013): 12.1.6
// An ExternalImportDeclaration in an AmbientExternalModuleDeclaration may reference
// other external modules only through top - level external module names.
// Relative external module names are not permitted.
error(node, Diagnostics.Import_or_export_declaration_in_an_ambient_module_declaration_cannot_reference_module_through_relative_module_name);
return false;
}
}
return true;
}
function checkAliasSymbol(node: ImportEqualsDeclaration | ImportClause | NamespaceImport | ImportSpecifier | ExportSpecifier) {
const symbol = getSymbolOfNode(node);
const target = resolveAlias(symbol);
if (target !== unknownSymbol) {
// For external modules symbol represent local symbol for an alias.
// This local symbol will merge any other local declarations (excluding other aliases)
// and symbol.flags will contains combined representation for all merged declaration.
// Based on symbol.flags we can compute a set of excluded meanings (meaning that resolved alias should not have,
// otherwise it will conflict with some local declaration). Note that in addition to normal flags we include matching SymbolFlags.Export*
// in order to prevent collisions with declarations that were exported from the current module (they still contribute to local names).
const excludedMeanings =
(symbol.flags & (SymbolFlags.Value | SymbolFlags.ExportValue) ? SymbolFlags.Value : 0) |
(symbol.flags & SymbolFlags.Type ? SymbolFlags.Type : 0) |
(symbol.flags & SymbolFlags.Namespace ? SymbolFlags.Namespace : 0);
if (target.flags & excludedMeanings) {
const message = node.kind === SyntaxKind.ExportSpecifier ?
Diagnostics.Export_declaration_conflicts_with_exported_declaration_of_0 :
Diagnostics.Import_declaration_conflicts_with_local_declaration_of_0;
error(node, message, symbolToString(symbol));
}
// Don't allow to re-export something with no value side when `--isolatedModules` is set.
if (node.kind === SyntaxKind.ExportSpecifier && compilerOptions.isolatedModules && !(target.flags & SymbolFlags.Value)) {
error(node, Diagnostics.Cannot_re_export_a_type_when_the_isolatedModules_flag_is_provided);
}
}
}
function checkImportBinding(node: ImportEqualsDeclaration | ImportClause | NamespaceImport | ImportSpecifier) {
checkCollisionWithCapturedThisVariable(node, node.name);
checkCollisionWithRequireExportsInGeneratedCode(node, node.name);
checkCollisionWithGlobalPromiseInGeneratedCode(node, node.name);
checkAliasSymbol(node);
}
function checkImportDeclaration(node: ImportDeclaration) {
if (checkGrammarModuleElementContext(node, Diagnostics.An_import_declaration_can_only_be_used_in_a_namespace_or_module)) {
// If we hit an import declaration in an illegal context, just bail out to avoid cascading errors.
return;
}
if (!checkGrammarDecorators(node) && !checkGrammarModifiers(node) && getModifierFlags(node) !== 0) {
grammarErrorOnFirstToken(node, Diagnostics.An_import_declaration_cannot_have_modifiers);
}
if (checkExternalImportOrExportDeclaration(node)) {
const importClause = node.importClause;
if (importClause) {
if (importClause.name) {
checkImportBinding(importClause);
}
if (importClause.namedBindings) {
if (importClause.namedBindings.kind === SyntaxKind.NamespaceImport) {
checkImportBinding(<NamespaceImport>importClause.namedBindings);
}
else {
forEach((<NamedImports>importClause.namedBindings).elements, checkImportBinding);
}
}
}
}
}
function checkImportEqualsDeclaration(node: ImportEqualsDeclaration) {
if (checkGrammarModuleElementContext(node, Diagnostics.An_import_declaration_can_only_be_used_in_a_namespace_or_module)) {
// If we hit an import declaration in an illegal context, just bail out to avoid cascading errors.
return;
}
checkGrammarDecorators(node) || checkGrammarModifiers(node);
if (isInternalModuleImportEqualsDeclaration(node) || checkExternalImportOrExportDeclaration(node)) {
checkImportBinding(node);
if (getModifierFlags(node) & ModifierFlags.Export) {
markExportAsReferenced(node);
}
if (isInternalModuleImportEqualsDeclaration(node)) {
const target = resolveAlias(getSymbolOfNode(node));
if (target !== unknownSymbol) {
if (target.flags & SymbolFlags.Value) {
// Target is a value symbol, check that it is not hidden by a local declaration with the same name
const moduleName = getFirstIdentifier(<EntityName>node.moduleReference);
if (!(resolveEntityName(moduleName, SymbolFlags.Value | SymbolFlags.Namespace).flags & SymbolFlags.Namespace)) {
error(moduleName, Diagnostics.Module_0_is_hidden_by_a_local_declaration_with_the_same_name, declarationNameToString(moduleName));
}
}
if (target.flags & SymbolFlags.Type) {
checkTypeNameIsReserved(node.name, Diagnostics.Import_name_cannot_be_0);
}
}
}
else {
if (modulekind === ModuleKind.ES2015 && !isInAmbientContext(node)) {
// Import equals declaration is deprecated in es6 or above
grammarErrorOnNode(node, Diagnostics.Import_assignment_cannot_be_used_when_targeting_ECMAScript_2015_modules_Consider_using_import_Asterisk_as_ns_from_mod_import_a_from_mod_import_d_from_mod_or_another_module_format_instead);
}
}
}
}
function checkExportDeclaration(node: ExportDeclaration) {
if (checkGrammarModuleElementContext(node, Diagnostics.An_export_declaration_can_only_be_used_in_a_module)) {
// If we hit an export in an illegal context, just bail out to avoid cascading errors.
return;
}
if (!checkGrammarDecorators(node) && !checkGrammarModifiers(node) && getModifierFlags(node) !== 0) {
grammarErrorOnFirstToken(node, Diagnostics.An_export_declaration_cannot_have_modifiers);
}
if (!node.moduleSpecifier || checkExternalImportOrExportDeclaration(node)) {
if (node.exportClause) {
// export { x, y }
// export { x, y } from "foo"
forEach(node.exportClause.elements, checkExportSpecifier);
const inAmbientExternalModule = node.parent.kind === SyntaxKind.ModuleBlock && isAmbientModule(node.parent.parent);
const inAmbientNamespaceDeclaration = !inAmbientExternalModule && node.parent.kind === SyntaxKind.ModuleBlock &&
!node.moduleSpecifier && isInAmbientContext(node);
if (node.parent.kind !== SyntaxKind.SourceFile && !inAmbientExternalModule && !inAmbientNamespaceDeclaration) {
error(node, Diagnostics.Export_declarations_are_not_permitted_in_a_namespace);
}
}
else {
// export * from "foo"
const moduleSymbol = resolveExternalModuleName(node, node.moduleSpecifier);
if (moduleSymbol && hasExportAssignmentSymbol(moduleSymbol)) {
error(node.moduleSpecifier, Diagnostics.Module_0_uses_export_and_cannot_be_used_with_export_Asterisk, symbolToString(moduleSymbol));
}
}
}
}
function checkGrammarModuleElementContext(node: Statement, errorMessage: DiagnosticMessage): boolean {
const isInAppropriateContext = node.parent.kind === SyntaxKind.SourceFile || node.parent.kind === SyntaxKind.ModuleBlock || node.parent.kind === SyntaxKind.ModuleDeclaration;
if (!isInAppropriateContext) {
grammarErrorOnFirstToken(node, errorMessage);
}
return !isInAppropriateContext;
}
function checkExportSpecifier(node: ExportSpecifier) {
checkAliasSymbol(node);
if (!(<ExportDeclaration>node.parent.parent).moduleSpecifier) {
const exportedName = node.propertyName || node.name;
// find immediate value referenced by exported name (SymbolFlags.Alias is set so we don't chase down aliases)
const symbol = resolveName(exportedName, exportedName.text, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias,
/*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined);
if (symbol && (symbol === undefinedSymbol || isGlobalSourceFile(getDeclarationContainer(symbol.declarations[0])))) {
error(exportedName, Diagnostics.Cannot_export_0_Only_local_declarations_can_be_exported_from_a_module, exportedName.text);
}
else {
markExportAsReferenced(node);
}
}
}
function checkExportAssignment(node: ExportAssignment) {
if (checkGrammarModuleElementContext(node, Diagnostics.An_export_assignment_can_only_be_used_in_a_module)) {
// If we hit an export assignment in an illegal context, just bail out to avoid cascading errors.
return;
}
const container = node.parent.kind === SyntaxKind.SourceFile ? <SourceFile>node.parent : <ModuleDeclaration>node.parent.parent;
if (container.kind === SyntaxKind.ModuleDeclaration && !isAmbientModule(container)) {
if (node.isExportEquals) {
error(node, Diagnostics.An_export_assignment_cannot_be_used_in_a_namespace);
}
else {
error(node, Diagnostics.A_default_export_can_only_be_used_in_an_ECMAScript_style_module);
}
return;
}
// Grammar checking
if (!checkGrammarDecorators(node) && !checkGrammarModifiers(node) && getModifierFlags(node) !== 0) {
grammarErrorOnFirstToken(node, Diagnostics.An_export_assignment_cannot_have_modifiers);
}
if (node.expression.kind === SyntaxKind.Identifier) {
markExportAsReferenced(node);
}
else {
checkExpressionCached(node.expression);
}
checkExternalModuleExports(container);
if (node.isExportEquals && !isInAmbientContext(node)) {
if (modulekind === ModuleKind.ES2015) {
// export assignment is not supported in es6 modules
grammarErrorOnNode(node, Diagnostics.Export_assignment_cannot_be_used_when_targeting_ECMAScript_2015_modules_Consider_using_export_default_or_another_module_format_instead);
}
else if (modulekind === ModuleKind.System) {
// system modules does not support export assignment
grammarErrorOnNode(node, Diagnostics.Export_assignment_is_not_supported_when_module_flag_is_system);
}
}
}
function hasExportedMembers(moduleSymbol: Symbol) {
return forEachEntry(moduleSymbol.exports, (_, id) => id !== "export=");
}
function checkExternalModuleExports(node: SourceFile | ModuleDeclaration) {
const moduleSymbol = getSymbolOfNode(node);
const links = getSymbolLinks(moduleSymbol);
if (!links.exportsChecked) {
const exportEqualsSymbol = moduleSymbol.exports.get("export=");
if (exportEqualsSymbol && hasExportedMembers(moduleSymbol)) {
const declaration = getDeclarationOfAliasSymbol(exportEqualsSymbol) || exportEqualsSymbol.valueDeclaration;
if (!isTopLevelInExternalModuleAugmentation(declaration)) {
error(declaration, Diagnostics.An_export_assignment_cannot_be_used_in_a_module_with_other_exported_elements);
}
}
// Checks for export * conflicts
const exports = getExportsOfModule(moduleSymbol);
exports && exports.forEach(({ declarations, flags }, id) => {
if (id === "__export") {
return;
}
// ECMA262: 15.2.1.1 It is a Syntax Error if the ExportedNames of ModuleItemList contains any duplicate entries.
// (TS Exceptions: namespaces, function overloads, enums, and interfaces)
if (flags & (SymbolFlags.Namespace | SymbolFlags.Interface | SymbolFlags.Enum)) {
return;
}
const exportedDeclarationsCount = countWhere(declarations, isNotOverload);
if (flags & SymbolFlags.TypeAlias && exportedDeclarationsCount <= 2) {
// it is legal to merge type alias with other values
// so count should be either 1 (just type alias) or 2 (type alias + merged value)
return;
}
if (exportedDeclarationsCount > 1) {
for (const declaration of declarations) {
if (isNotOverload(declaration)) {
diagnostics.add(createDiagnosticForNode(declaration, Diagnostics.Cannot_redeclare_exported_variable_0, id));
}
}
}
});
links.exportsChecked = true;
}
function isNotOverload(declaration: Declaration): boolean {
return (declaration.kind !== SyntaxKind.FunctionDeclaration && declaration.kind !== SyntaxKind.MethodDeclaration) ||
!!(declaration as FunctionDeclaration).body;
}
}
function checkSourceElement(node: Node): void {
if (!node) {
return;
}
const kind = node.kind;
if (cancellationToken) {
// Only bother checking on a few construct kinds. We don't want to be excessively
// hitting the cancellation token on every node we check.
switch (kind) {
case SyntaxKind.ModuleDeclaration:
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.FunctionDeclaration:
cancellationToken.throwIfCancellationRequested();
}
}
switch (kind) {
case SyntaxKind.TypeParameter:
return checkTypeParameter(<TypeParameterDeclaration>node);
case SyntaxKind.Parameter:
return checkParameter(<ParameterDeclaration>node);
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.PropertySignature:
return checkPropertyDeclaration(<PropertyDeclaration>node);
case SyntaxKind.FunctionType:
case SyntaxKind.ConstructorType:
case SyntaxKind.CallSignature:
case SyntaxKind.ConstructSignature:
return checkSignatureDeclaration(<SignatureDeclaration>node);
case SyntaxKind.IndexSignature:
return checkSignatureDeclaration(<SignatureDeclaration>node);
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
return checkMethodDeclaration(<MethodDeclaration>node);
case SyntaxKind.Constructor:
return checkConstructorDeclaration(<ConstructorDeclaration>node);
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
return checkAccessorDeclaration(<AccessorDeclaration>node);
case SyntaxKind.TypeReference:
return checkTypeReferenceNode(<TypeReferenceNode>node);
case SyntaxKind.TypePredicate:
return checkTypePredicate(<TypePredicateNode>node);
case SyntaxKind.TypeQuery:
return checkTypeQuery(<TypeQueryNode>node);
case SyntaxKind.TypeLiteral:
return checkTypeLiteral(<TypeLiteralNode>node);
case SyntaxKind.ArrayType:
return checkArrayType(<ArrayTypeNode>node);
case SyntaxKind.TupleType:
return checkTupleType(<TupleTypeNode>node);
case SyntaxKind.UnionType:
case SyntaxKind.IntersectionType:
return checkUnionOrIntersectionType(<UnionOrIntersectionTypeNode>node);
case SyntaxKind.ParenthesizedType:
case SyntaxKind.TypeOperator:
return checkSourceElement((<ParenthesizedTypeNode | TypeOperatorNode>node).type);
case SyntaxKind.IndexedAccessType:
return checkIndexedAccessType(<IndexedAccessTypeNode>node);
case SyntaxKind.MappedType:
return checkMappedType(<MappedTypeNode>node);
case SyntaxKind.FunctionDeclaration:
return checkFunctionDeclaration(<FunctionDeclaration>node);
case SyntaxKind.Block:
case SyntaxKind.ModuleBlock:
return checkBlock(<Block>node);
case SyntaxKind.VariableStatement:
return checkVariableStatement(<VariableStatement>node);
case SyntaxKind.ExpressionStatement:
return checkExpressionStatement(<ExpressionStatement>node);
case SyntaxKind.IfStatement:
return checkIfStatement(<IfStatement>node);
case SyntaxKind.DoStatement:
return checkDoStatement(<DoStatement>node);
case SyntaxKind.WhileStatement:
return checkWhileStatement(<WhileStatement>node);
case SyntaxKind.ForStatement:
return checkForStatement(<ForStatement>node);
case SyntaxKind.ForInStatement:
return checkForInStatement(<ForInStatement>node);
case SyntaxKind.ForOfStatement:
return checkForOfStatement(<ForOfStatement>node);
case SyntaxKind.ContinueStatement:
case SyntaxKind.BreakStatement:
return checkBreakOrContinueStatement(<BreakOrContinueStatement>node);
case SyntaxKind.ReturnStatement:
return checkReturnStatement(<ReturnStatement>node);
case SyntaxKind.WithStatement:
return checkWithStatement(<WithStatement>node);
case SyntaxKind.SwitchStatement:
return checkSwitchStatement(<SwitchStatement>node);
case SyntaxKind.LabeledStatement:
return checkLabeledStatement(<LabeledStatement>node);
case SyntaxKind.ThrowStatement:
return checkThrowStatement(<ThrowStatement>node);
case SyntaxKind.TryStatement:
return checkTryStatement(<TryStatement>node);
case SyntaxKind.VariableDeclaration:
return checkVariableDeclaration(<VariableDeclaration>node);
case SyntaxKind.BindingElement:
return checkBindingElement(<BindingElement>node);
case SyntaxKind.ClassDeclaration:
return checkClassDeclaration(<ClassDeclaration>node);
case SyntaxKind.InterfaceDeclaration:
return checkInterfaceDeclaration(<InterfaceDeclaration>node);
case SyntaxKind.TypeAliasDeclaration:
return checkTypeAliasDeclaration(<TypeAliasDeclaration>node);
case SyntaxKind.EnumDeclaration:
return checkEnumDeclaration(<EnumDeclaration>node);
case SyntaxKind.ModuleDeclaration:
return checkModuleDeclaration(<ModuleDeclaration>node);
case SyntaxKind.ImportDeclaration:
return checkImportDeclaration(<ImportDeclaration>node);
case SyntaxKind.ImportEqualsDeclaration:
return checkImportEqualsDeclaration(<ImportEqualsDeclaration>node);
case SyntaxKind.ExportDeclaration:
return checkExportDeclaration(<ExportDeclaration>node);
case SyntaxKind.ExportAssignment:
return checkExportAssignment(<ExportAssignment>node);
case SyntaxKind.EmptyStatement:
checkGrammarStatementInAmbientContext(node);
return;
case SyntaxKind.DebuggerStatement:
checkGrammarStatementInAmbientContext(node);
return;
case SyntaxKind.MissingDeclaration:
return checkMissingDeclaration(node);
}
}
// Function and class expression bodies are checked after all statements in the enclosing body. This is
// to ensure constructs like the following are permitted:
// const foo = function () {
// const s = foo();
// return "hello";
// }
// Here, performing a full type check of the body of the function expression whilst in the process of
// determining the type of foo would cause foo to be given type any because of the recursive reference.
// Delaying the type check of the body ensures foo has been assigned a type.
function checkNodeDeferred(node: Node) {
if (deferredNodes) {
deferredNodes.push(node);
}
}
function checkDeferredNodes() {
for (const node of deferredNodes) {
switch (node.kind) {
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
checkFunctionExpressionOrObjectLiteralMethodDeferred(<FunctionExpression>node);
break;
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
checkAccessorDeclaration(<AccessorDeclaration>node);
break;
case SyntaxKind.ClassExpression:
checkClassExpressionDeferred(<ClassExpression>node);
break;
}
}
}
function checkSourceFile(node: SourceFile) {
performance.mark("beforeCheck");
checkSourceFileWorker(node);
performance.mark("afterCheck");
performance.measure("Check", "beforeCheck", "afterCheck");
}
// Fully type check a source file and collect the relevant diagnostics.
function checkSourceFileWorker(node: SourceFile) {
const links = getNodeLinks(node);
if (!(links.flags & NodeCheckFlags.TypeChecked)) {
// If skipLibCheck is enabled, skip type checking if file is a declaration file.
// If skipDefaultLibCheck is enabled, skip type checking if file contains a
// '/// <reference no-default-lib="true"/>' directive.
if (compilerOptions.skipLibCheck && node.isDeclarationFile || compilerOptions.skipDefaultLibCheck && node.hasNoDefaultLib) {
return;
}
// Grammar checking
checkGrammarSourceFile(node);
potentialThisCollisions.length = 0;
potentialNewTargetCollisions.length = 0;
deferredNodes = [];
deferredUnusedIdentifierNodes = produceDiagnostics && noUnusedIdentifiers ? [] : undefined;
forEach(node.statements, checkSourceElement);
checkDeferredNodes();
if (isExternalModule(node)) {
registerForUnusedIdentifiersCheck(node);
}
if (!node.isDeclarationFile) {
checkUnusedIdentifiers();
}
deferredNodes = undefined;
deferredUnusedIdentifierNodes = undefined;
if (isExternalOrCommonJsModule(node)) {
checkExternalModuleExports(node);
}
if (potentialThisCollisions.length) {
forEach(potentialThisCollisions, checkIfThisIsCapturedInEnclosingScope);
potentialThisCollisions.length = 0;
}
if (potentialNewTargetCollisions.length) {
forEach(potentialNewTargetCollisions, checkIfNewTargetIsCapturedInEnclosingScope);
potentialNewTargetCollisions.length = 0;
}
links.flags |= NodeCheckFlags.TypeChecked;
}
}
function getDiagnostics(sourceFile: SourceFile, ct: CancellationToken): Diagnostic[] {
try {
// Record the cancellation token so it can be checked later on during checkSourceElement.
// Do this in a finally block so we can ensure that it gets reset back to nothing after
// this call is done.
cancellationToken = ct;
return getDiagnosticsWorker(sourceFile);
}
finally {
cancellationToken = undefined;
}
}
function getDiagnosticsWorker(sourceFile: SourceFile): Diagnostic[] {
throwIfNonDiagnosticsProducing();
if (sourceFile) {
// Some global diagnostics are deferred until they are needed and
// may not be reported in the firt call to getGlobalDiagnostics.
// We should catch these changes and report them.
const previousGlobalDiagnostics = diagnostics.getGlobalDiagnostics();
const previousGlobalDiagnosticsSize = previousGlobalDiagnostics.length;
checkSourceFile(sourceFile);
const semanticDiagnostics = diagnostics.getDiagnostics(sourceFile.fileName);
const currentGlobalDiagnostics = diagnostics.getGlobalDiagnostics();
if (currentGlobalDiagnostics !== previousGlobalDiagnostics) {
// If the arrays are not the same reference, new diagnostics were added.
const deferredGlobalDiagnostics = relativeComplement(previousGlobalDiagnostics, currentGlobalDiagnostics, compareDiagnostics);
return concatenate(deferredGlobalDiagnostics, semanticDiagnostics);
}
else if (previousGlobalDiagnosticsSize === 0 && currentGlobalDiagnostics.length > 0) {
// If the arrays are the same reference, but the length has changed, a single
// new diagnostic was added as DiagnosticCollection attempts to reuse the
// same array.
return concatenate(currentGlobalDiagnostics, semanticDiagnostics);
}
return semanticDiagnostics;
}
// Global diagnostics are always added when a file is not provided to
// getDiagnostics
forEach(host.getSourceFiles(), checkSourceFile);
return diagnostics.getDiagnostics();
}
function getGlobalDiagnostics(): Diagnostic[] {
throwIfNonDiagnosticsProducing();
return diagnostics.getGlobalDiagnostics();
}
function throwIfNonDiagnosticsProducing() {
if (!produceDiagnostics) {
throw new Error("Trying to get diagnostics from a type checker that does not produce them.");
}
}
// Language service support
function isInsideWithStatementBody(node: Node): boolean {
if (node) {
while (node.parent) {
if (node.parent.kind === SyntaxKind.WithStatement && (<WithStatement>node.parent).statement === node) {
return true;
}
node = node.parent;
}
}
return false;
}
function getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[] {
if (isInsideWithStatementBody(location)) {
// We cannot answer semantic questions within a with block, do not proceed any further
return [];
}
const symbols = createMap<Symbol>();
let memberFlags: ModifierFlags = ModifierFlags.None;
populateSymbols();
return symbolsToArray(symbols);
function populateSymbols() {
while (location) {
if (location.locals && !isGlobalSourceFile(location)) {
copySymbols(location.locals, meaning);
}
switch (location.kind) {
case SyntaxKind.SourceFile:
if (!isExternalOrCommonJsModule(<SourceFile>location)) {
break;
}
// falls through
case SyntaxKind.ModuleDeclaration:
copySymbols(getSymbolOfNode(location).exports, meaning & SymbolFlags.ModuleMember);
break;
case SyntaxKind.EnumDeclaration:
copySymbols(getSymbolOfNode(location).exports, meaning & SymbolFlags.EnumMember);
break;
case SyntaxKind.ClassExpression:
const className = (<ClassExpression>location).name;
if (className) {
copySymbol(location.symbol, meaning);
}
// falls through
// this fall-through is necessary because we would like to handle
// type parameter inside class expression similar to how we handle it in classDeclaration and interface Declaration
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
// If we didn't come from static member of class or interface,
// add the type parameters into the symbol table
// (type parameters of classDeclaration/classExpression and interface are in member property of the symbol.
// Note: that the memberFlags come from previous iteration.
if (!(memberFlags & ModifierFlags.Static)) {
copySymbols(getSymbolOfNode(location).members, meaning & SymbolFlags.Type);
}
break;
case SyntaxKind.FunctionExpression:
const funcName = (<FunctionExpression>location).name;
if (funcName) {
copySymbol(location.symbol, meaning);
}
break;
}
if (introducesArgumentsExoticObject(location)) {
copySymbol(argumentsSymbol, meaning);
}
memberFlags = getModifierFlags(location);
location = location.parent;
}
copySymbols(globals, meaning);
}
/**
* Copy the given symbol into symbol tables if the symbol has the given meaning
* and it doesn't already existed in the symbol table
* @param key a key for storing in symbol table; if undefined, use symbol.name
* @param symbol the symbol to be added into symbol table
* @param meaning meaning of symbol to filter by before adding to symbol table
*/
function copySymbol(symbol: Symbol, meaning: SymbolFlags): void {
if (symbol.flags & meaning) {
const id = symbol.name;
// We will copy all symbol regardless of its reserved name because
// symbolsToArray will check whether the key is a reserved name and
// it will not copy symbol with reserved name to the array
if (!symbols.has(id)) {
symbols.set(id, symbol);
}
}
}
function copySymbols(source: SymbolTable, meaning: SymbolFlags): void {
if (meaning) {
source.forEach(symbol => {
copySymbol(symbol, meaning);
});
}
}
}
function isTypeDeclarationName(name: Node): boolean {
return name.kind === SyntaxKind.Identifier &&
isTypeDeclaration(name.parent) &&
(<NamedDeclaration>name.parent).name === name;
}
function isTypeDeclaration(node: Node): boolean {
switch (node.kind) {
case SyntaxKind.TypeParameter:
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.TypeAliasDeclaration:
case SyntaxKind.EnumDeclaration:
return true;
}
}
// True if the given identifier is part of a type reference
function isTypeReferenceIdentifier(entityName: EntityName): boolean {
let node: Node = entityName;
while (node.parent && node.parent.kind === SyntaxKind.QualifiedName) {
node = node.parent;
}
return node.parent && (node.parent.kind === SyntaxKind.TypeReference || node.parent.kind === SyntaxKind.JSDocTypeReference) ;
}
function isHeritageClauseElementIdentifier(entityName: Node): boolean {
let node = entityName;
while (node.parent && node.parent.kind === SyntaxKind.PropertyAccessExpression) {
node = node.parent;
}
return node.parent && node.parent.kind === SyntaxKind.ExpressionWithTypeArguments;
}
function forEachEnclosingClass<T>(node: Node, callback: (node: Node) => T): T {
let result: T;
while (true) {
node = getContainingClass(node);
if (!node) break;
if (result = callback(node)) break;
}
return result;
}
function isNodeWithinClass(node: Node, classDeclaration: ClassLikeDeclaration) {
return !!forEachEnclosingClass(node, n => n === classDeclaration);
}
function getLeftSideOfImportEqualsOrExportAssignment(nodeOnRightSide: EntityName): ImportEqualsDeclaration | ExportAssignment {
while (nodeOnRightSide.parent.kind === SyntaxKind.QualifiedName) {
nodeOnRightSide = <QualifiedName>nodeOnRightSide.parent;
}
if (nodeOnRightSide.parent.kind === SyntaxKind.ImportEqualsDeclaration) {
return (<ImportEqualsDeclaration>nodeOnRightSide.parent).moduleReference === nodeOnRightSide && <ImportEqualsDeclaration>nodeOnRightSide.parent;
}
if (nodeOnRightSide.parent.kind === SyntaxKind.ExportAssignment) {
return (<ExportAssignment>nodeOnRightSide.parent).expression === <Node>nodeOnRightSide && <ExportAssignment>nodeOnRightSide.parent;
}
return undefined;
}
function isInRightSideOfImportOrExportAssignment(node: EntityName) {
return getLeftSideOfImportEqualsOrExportAssignment(node) !== undefined;
}
function getSpecialPropertyAssignmentSymbolFromEntityName(entityName: EntityName | PropertyAccessExpression) {
const specialPropertyAssignmentKind = getSpecialPropertyAssignmentKind(entityName.parent.parent as BinaryExpression);
switch (specialPropertyAssignmentKind) {
case SpecialPropertyAssignmentKind.ExportsProperty:
case SpecialPropertyAssignmentKind.PrototypeProperty:
return getSymbolOfNode(entityName.parent);
case SpecialPropertyAssignmentKind.ThisProperty:
case SpecialPropertyAssignmentKind.ModuleExports:
case SpecialPropertyAssignmentKind.Property:
return getSymbolOfNode(entityName.parent.parent);
}
}
function getSymbolOfEntityNameOrPropertyAccessExpression(entityName: EntityName | PropertyAccessExpression): Symbol | undefined {
if (isDeclarationName(entityName)) {
return getSymbolOfNode(entityName.parent);
}
if (isInJavaScriptFile(entityName) &&
entityName.parent.kind === SyntaxKind.PropertyAccessExpression &&
entityName.parent === (entityName.parent.parent as BinaryExpression).left) {
// Check if this is a special property assignment
const specialPropertyAssignmentSymbol = getSpecialPropertyAssignmentSymbolFromEntityName(entityName);
if (specialPropertyAssignmentSymbol) {
return specialPropertyAssignmentSymbol;
}
}
if (entityName.parent.kind === SyntaxKind.ExportAssignment && isEntityNameExpression(<Identifier | PropertyAccessExpression>entityName)) {
return resolveEntityName(<EntityNameExpression>entityName,
/*all meanings*/ SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias);
}
if (entityName.kind !== SyntaxKind.PropertyAccessExpression && isInRightSideOfImportOrExportAssignment(<EntityName>entityName)) {
// Since we already checked for ExportAssignment, this really could only be an Import
const importEqualsDeclaration = <ImportEqualsDeclaration>getAncestor(entityName, SyntaxKind.ImportEqualsDeclaration);
Debug.assert(importEqualsDeclaration !== undefined);
return getSymbolOfPartOfRightHandSideOfImportEquals(<EntityName>entityName, /*dontResolveAlias*/ true);
}
if (isRightSideOfQualifiedNameOrPropertyAccess(entityName)) {
entityName = <QualifiedName | PropertyAccessEntityNameExpression>entityName.parent;
}
if (isHeritageClauseElementIdentifier(<EntityName>entityName)) {
let meaning = SymbolFlags.None;
// In an interface or class, we're definitely interested in a type.
if (entityName.parent.kind === SyntaxKind.ExpressionWithTypeArguments) {
meaning = SymbolFlags.Type;
// In a class 'extends' clause we are also looking for a value.
if (isExpressionWithTypeArgumentsInClassExtendsClause(entityName.parent)) {
meaning |= SymbolFlags.Value;
}
}
else {
meaning = SymbolFlags.Namespace;
}
meaning |= SymbolFlags.Alias;
const entityNameSymbol = resolveEntityName(<EntityName>entityName, meaning);
if (entityNameSymbol) {
return entityNameSymbol;
}
}
if (entityName.parent!.kind === SyntaxKind.JSDocParameterTag) {
const parameter = ts.getParameterFromJSDoc(entityName.parent as JSDocParameterTag);
return parameter && parameter.symbol;
}
if (isPartOfExpression(entityName)) {
if (nodeIsMissing(entityName)) {
// Missing entity name.
return undefined;
}
if (entityName.kind === SyntaxKind.Identifier) {
if (isJSXTagName(entityName) && isJsxIntrinsicIdentifier(<Identifier>entityName)) {
return getIntrinsicTagSymbol(<JsxOpeningLikeElement>entityName.parent);
}
return resolveEntityName(<Identifier>entityName, SymbolFlags.Value, /*ignoreErrors*/ false, /*dontResolveAlias*/ true);
}
else if (entityName.kind === SyntaxKind.PropertyAccessExpression) {
const symbol = getNodeLinks(entityName).resolvedSymbol;
if (!symbol) {
checkPropertyAccessExpression(<PropertyAccessExpression>entityName);
}
return getNodeLinks(entityName).resolvedSymbol;
}
else if (entityName.kind === SyntaxKind.QualifiedName) {
const symbol = getNodeLinks(entityName).resolvedSymbol;
if (!symbol) {
checkQualifiedName(<QualifiedName>entityName);
}
return getNodeLinks(entityName).resolvedSymbol;
}
}
else if (isTypeReferenceIdentifier(<EntityName>entityName)) {
const meaning = (entityName.parent.kind === SyntaxKind.TypeReference || entityName.parent.kind === SyntaxKind.JSDocTypeReference) ? SymbolFlags.Type : SymbolFlags.Namespace;
return resolveEntityName(<EntityName>entityName, meaning, /*ignoreErrors*/ false, /*dontResolveAlias*/ true);
}
else if (entityName.parent.kind === SyntaxKind.JsxAttribute) {
return getJsxAttributePropertySymbol(<JsxAttribute>entityName.parent);
}
if (entityName.parent.kind === SyntaxKind.TypePredicate) {
return resolveEntityName(<Identifier>entityName, /*meaning*/ SymbolFlags.FunctionScopedVariable);
}
// Do we want to return undefined here?
return undefined;
}
function getSymbolAtLocation(node: Node) {
if (node.kind === SyntaxKind.SourceFile) {
return isExternalModule(<SourceFile>node) ? getMergedSymbol(node.symbol) : undefined;
}
if (isInsideWithStatementBody(node)) {
// We cannot answer semantic questions within a with block, do not proceed any further
return undefined;
}
if (isDeclarationNameOrImportPropertyName(node)) {
// This is a declaration, call getSymbolOfNode
return getSymbolOfNode(node.parent);
}
else if (isLiteralComputedPropertyDeclarationName(node)) {
return getSymbolOfNode(node.parent.parent);
}
if (node.kind === SyntaxKind.Identifier) {
if (isInRightSideOfImportOrExportAssignment(<Identifier>node)) {
return getSymbolOfEntityNameOrPropertyAccessExpression(<Identifier>node);
}
else if (node.parent.kind === SyntaxKind.BindingElement &&
node.parent.parent.kind === SyntaxKind.ObjectBindingPattern &&
node === (<BindingElement>node.parent).propertyName) {
const typeOfPattern = getTypeOfNode(node.parent.parent);
const propertyDeclaration = typeOfPattern && getPropertyOfType(typeOfPattern, (<Identifier>node).text);
if (propertyDeclaration) {
return propertyDeclaration;
}
}
}
switch (node.kind) {
case SyntaxKind.Identifier:
case SyntaxKind.PropertyAccessExpression:
case SyntaxKind.QualifiedName:
return getSymbolOfEntityNameOrPropertyAccessExpression(<EntityName | PropertyAccessExpression>node);
case SyntaxKind.ThisKeyword:
const container = getThisContainer(node, /*includeArrowFunctions*/ false);
if (isFunctionLike(container)) {
const sig = getSignatureFromDeclaration(container);
if (sig.thisParameter) {
return sig.thisParameter;
}
}
// falls through
case SyntaxKind.SuperKeyword:
const type = isPartOfExpression(node) ? getTypeOfExpression(<Expression>node) : getTypeFromTypeNode(<TypeNode>node);
return type.symbol;
case SyntaxKind.ThisType:
return getTypeFromTypeNode(<TypeNode>node).symbol;
case SyntaxKind.ConstructorKeyword:
// constructor keyword for an overload, should take us to the definition if it exist
const constructorDeclaration = node.parent;
if (constructorDeclaration && constructorDeclaration.kind === SyntaxKind.Constructor) {
return (<ClassDeclaration>constructorDeclaration.parent).symbol;
}
return undefined;
case SyntaxKind.StringLiteral:
// External module name in an import declaration
if ((isExternalModuleImportEqualsDeclaration(node.parent.parent) &&
getExternalModuleImportEqualsDeclarationExpression(node.parent.parent) === node) ||
((node.parent.kind === SyntaxKind.ImportDeclaration || node.parent.kind === SyntaxKind.ExportDeclaration) &&
(<ImportDeclaration>node.parent).moduleSpecifier === node)) {
return resolveExternalModuleName(node, <LiteralExpression>node);
}
if (isInJavaScriptFile(node) && isRequireCall(node.parent, /*checkArgumentIsStringLiteral*/ false)) {
return resolveExternalModuleName(node, <LiteralExpression>node);
}
// falls through
case SyntaxKind.NumericLiteral:
// index access
if (node.parent.kind === SyntaxKind.ElementAccessExpression && (<ElementAccessExpression>node.parent).argumentExpression === node) {
const objectType = getTypeOfExpression((<ElementAccessExpression>node.parent).expression);
if (objectType === unknownType) return undefined;
const apparentType = getApparentType(objectType);
if (apparentType === unknownType) return undefined;
return getPropertyOfType(apparentType, (<NumericLiteral>node).text);
}
break;
}
return undefined;
}
function getShorthandAssignmentValueSymbol(location: Node): Symbol {
// The function returns a value symbol of an identifier in the short-hand property assignment.
// This is necessary as an identifier in short-hand property assignment can contains two meaning:
// property name and property value.
if (location && location.kind === SyntaxKind.ShorthandPropertyAssignment) {
return resolveEntityName((<ShorthandPropertyAssignment>location).name, SymbolFlags.Value | SymbolFlags.Alias);
}
return undefined;
}
/** Returns the target of an export specifier without following aliases */
function getExportSpecifierLocalTargetSymbol(node: ExportSpecifier): Symbol {
return (<ExportDeclaration>node.parent.parent).moduleSpecifier ?
getExternalModuleMember(<ExportDeclaration>node.parent.parent, node) :
resolveEntityName(node.propertyName || node.name, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias);
}
function getTypeOfNode(node: Node): Type {
if (isInsideWithStatementBody(node)) {
// We cannot answer semantic questions within a with block, do not proceed any further
return unknownType;
}
if (isPartOfTypeNode(node)) {
let typeFromTypeNode = getTypeFromTypeNode(<TypeNode>node);
if (typeFromTypeNode && isExpressionWithTypeArgumentsInClassImplementsClause(node)) {
const containingClass = getContainingClass(node);
const classType = getTypeOfNode(containingClass) as InterfaceType;
typeFromTypeNode = getTypeWithThisArgument(typeFromTypeNode, classType.thisType);
}
return typeFromTypeNode;
}
if (isPartOfExpression(node)) {
return getRegularTypeOfExpression(<Expression>node);
}
if (isExpressionWithTypeArgumentsInClassExtendsClause(node)) {
// A SyntaxKind.ExpressionWithTypeArguments is considered a type node, except when it occurs in the
// extends clause of a class. We handle that case here.
const classNode = getContainingClass(node);
const classType = getDeclaredTypeOfSymbol(getSymbolOfNode(classNode)) as InterfaceType;
const baseType = getBaseTypes(classType)[0];
return baseType && getTypeWithThisArgument(baseType, classType.thisType);
}
if (isTypeDeclaration(node)) {
// In this case, we call getSymbolOfNode instead of getSymbolAtLocation because it is a declaration
const symbol = getSymbolOfNode(node);
return getDeclaredTypeOfSymbol(symbol);
}
if (isTypeDeclarationName(node)) {
const symbol = getSymbolAtLocation(node);
return symbol && getDeclaredTypeOfSymbol(symbol);
}
if (isDeclaration(node)) {
// In this case, we call getSymbolOfNode instead of getSymbolAtLocation because it is a declaration
const symbol = getSymbolOfNode(node);
return getTypeOfSymbol(symbol);
}
if (isDeclarationNameOrImportPropertyName(node)) {
const symbol = getSymbolAtLocation(node);
return symbol && getTypeOfSymbol(symbol);
}
if (isBindingPattern(node)) {
return getTypeForVariableLikeDeclaration(<VariableLikeDeclaration>node.parent, /*includeOptionality*/ true);
}
if (isInRightSideOfImportOrExportAssignment(<Identifier>node)) {
const symbol = getSymbolAtLocation(node);
const declaredType = symbol && getDeclaredTypeOfSymbol(symbol);
return declaredType !== unknownType ? declaredType : getTypeOfSymbol(symbol);
}
return unknownType;
}
// Gets the type of object literal or array literal of destructuring assignment.
// { a } from
// for ( { a } of elems) {
// }
// [ a ] from
// [a] = [ some array ...]
function getTypeOfArrayLiteralOrObjectLiteralDestructuringAssignment(expr: Expression): Type {
Debug.assert(expr.kind === SyntaxKind.ObjectLiteralExpression || expr.kind === SyntaxKind.ArrayLiteralExpression);
// If this is from "for of"
// for ( { a } of elems) {
// }
if (expr.parent.kind === SyntaxKind.ForOfStatement) {
const iteratedType = checkRightHandSideOfForOf((<ForOfStatement>expr.parent).expression, (<ForOfStatement>expr.parent).awaitModifier);
return checkDestructuringAssignment(expr, iteratedType || unknownType);
}
// If this is from "for" initializer
// for ({a } = elems[0];.....) { }
if (expr.parent.kind === SyntaxKind.BinaryExpression) {
const iteratedType = getTypeOfExpression((<BinaryExpression>expr.parent).right);
return checkDestructuringAssignment(expr, iteratedType || unknownType);
}
// If this is from nested object binding pattern
// for ({ skills: { primary, secondary } } = multiRobot, i = 0; i < 1; i++) {
if (expr.parent.kind === SyntaxKind.PropertyAssignment) {
const typeOfParentObjectLiteral = getTypeOfArrayLiteralOrObjectLiteralDestructuringAssignment(<Expression>expr.parent.parent);
return checkObjectLiteralDestructuringPropertyAssignment(typeOfParentObjectLiteral || unknownType, <ObjectLiteralElementLike>expr.parent);
}
// Array literal assignment - array destructuring pattern
Debug.assert(expr.parent.kind === SyntaxKind.ArrayLiteralExpression);
// [{ property1: p1, property2 }] = elems;
const typeOfArrayLiteral = getTypeOfArrayLiteralOrObjectLiteralDestructuringAssignment(<Expression>expr.parent);
const elementType = checkIteratedTypeOrElementType(typeOfArrayLiteral || unknownType, expr.parent, /*allowStringInput*/ false, /*allowAsyncIterable*/ false) || unknownType;
return checkArrayLiteralDestructuringElementAssignment(<ArrayLiteralExpression>expr.parent, typeOfArrayLiteral,
indexOf((<ArrayLiteralExpression>expr.parent).elements, expr), elementType || unknownType);
}
// Gets the property symbol corresponding to the property in destructuring assignment
// 'property1' from
// for ( { property1: a } of elems) {
// }
// 'property1' at location 'a' from:
// [a] = [ property1, property2 ]
function getPropertySymbolOfDestructuringAssignment(location: Identifier) {
// Get the type of the object or array literal and then look for property of given name in the type
const typeOfObjectLiteral = getTypeOfArrayLiteralOrObjectLiteralDestructuringAssignment(<Expression>location.parent.parent);
return typeOfObjectLiteral && getPropertyOfType(typeOfObjectLiteral, location.text);
}
function getRegularTypeOfExpression(expr: Expression): Type {
if (isRightSideOfQualifiedNameOrPropertyAccess(expr)) {
expr = <Expression>expr.parent;
}
return getRegularTypeOfLiteralType(getTypeOfExpression(expr));
}
/**
* Gets either the static or instance type of a class element, based on
* whether the element is declared as "static".
*/
function getParentTypeOfClassElement(node: ClassElement) {
const classSymbol = getSymbolOfNode(node.parent);
return getModifierFlags(node) & ModifierFlags.Static
? getTypeOfSymbol(classSymbol)
: getDeclaredTypeOfSymbol(classSymbol);
}
// Return the list of properties of the given type, augmented with properties from Function
// if the type has call or construct signatures
function getAugmentedPropertiesOfType(type: Type): Symbol[] {
type = getApparentType(type);
const propsByName = createSymbolTable(getPropertiesOfType(type));
if (getSignaturesOfType(type, SignatureKind.Call).length || getSignaturesOfType(type, SignatureKind.Construct).length) {
forEach(getPropertiesOfType(globalFunctionType), p => {
if (!propsByName.has(p.name)) {
propsByName.set(p.name, p);
}
});
}
return getNamedMembers(propsByName);
}
function getRootSymbols(symbol: Symbol): Symbol[] {
if (getCheckFlags(symbol) & CheckFlags.Synthetic) {
const symbols: Symbol[] = [];
const name = symbol.name;
forEach(getSymbolLinks(symbol).containingType.types, t => {
const symbol = getPropertyOfType(t, name);
if (symbol) {
symbols.push(symbol);
}
});
return symbols;
}
else if (symbol.flags & SymbolFlags.Transient) {
const transient = symbol as TransientSymbol;
if (transient.leftSpread) {
return [...getRootSymbols(transient.leftSpread), ...getRootSymbols(transient.rightSpread)];
}
if (transient.syntheticOrigin) {
return getRootSymbols(transient.syntheticOrigin);
}
let target: Symbol;
let next = symbol;
while (next = getSymbolLinks(next).target) {
target = next;
}
if (target) {
return [target];
}
}
return [symbol];
}
// Emitter support
function isArgumentsLocalBinding(node: Identifier): boolean {
if (!isGeneratedIdentifier(node)) {
node = getParseTreeNode(node, isIdentifier);
if (node) {
const isPropertyName = node.parent.kind === SyntaxKind.PropertyAccessExpression && (<PropertyAccessExpression>node.parent).name === node;
return !isPropertyName && getReferencedValueSymbol(node) === argumentsSymbol;
}
}
return false;
}
function moduleExportsSomeValue(moduleReferenceExpression: Expression): boolean {
let moduleSymbol = resolveExternalModuleName(moduleReferenceExpression.parent, moduleReferenceExpression);
if (!moduleSymbol || isShorthandAmbientModuleSymbol(moduleSymbol)) {
// If the module is not found or is shorthand, assume that it may export a value.
return true;
}
const hasExportAssignment = hasExportAssignmentSymbol(moduleSymbol);
// if module has export assignment then 'resolveExternalModuleSymbol' will return resolved symbol for export assignment
// otherwise it will return moduleSymbol itself
moduleSymbol = resolveExternalModuleSymbol(moduleSymbol);
const symbolLinks = getSymbolLinks(moduleSymbol);
if (symbolLinks.exportsSomeValue === undefined) {
// for export assignments - check if resolved symbol for RHS is itself a value
// otherwise - check if at least one export is value
symbolLinks.exportsSomeValue = hasExportAssignment
? !!(moduleSymbol.flags & SymbolFlags.Value)
: forEachEntry(getExportsOfModule(moduleSymbol), isValue);
}
return symbolLinks.exportsSomeValue;
function isValue(s: Symbol): boolean {
s = resolveSymbol(s);
return s && !!(s.flags & SymbolFlags.Value);
}
}
function isNameOfModuleOrEnumDeclaration(node: Identifier) {
const parent = node.parent;
return parent && isModuleOrEnumDeclaration(parent) && node === parent.name;
}
// When resolved as an expression identifier, if the given node references an exported entity, return the declaration
// node of the exported entity's container. Otherwise, return undefined.
function getReferencedExportContainer(node: Identifier, prefixLocals?: boolean): SourceFile | ModuleDeclaration | EnumDeclaration | undefined {
node = getParseTreeNode(node, isIdentifier);
if (node) {
// When resolving the export container for the name of a module or enum
// declaration, we need to start resolution at the declaration's container.
// Otherwise, we could incorrectly resolve the export container as the
// declaration if it contains an exported member with the same name.
let symbol = getReferencedValueSymbol(node, /*startInDeclarationContainer*/ isNameOfModuleOrEnumDeclaration(node));
if (symbol) {
if (symbol.flags & SymbolFlags.ExportValue) {
// 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.
const exportSymbol = getMergedSymbol(symbol.exportSymbol);
if (!prefixLocals && exportSymbol.flags & SymbolFlags.ExportHasLocal) {
return undefined;
}
symbol = exportSymbol;
}
const parentSymbol = getParentOfSymbol(symbol);
if (parentSymbol) {
if (parentSymbol.flags & SymbolFlags.ValueModule && parentSymbol.valueDeclaration.kind === SyntaxKind.SourceFile) {
const symbolFile = <SourceFile>parentSymbol.valueDeclaration;
const referenceFile = getSourceFileOfNode(node);
// If `node` accesses an export and that export isn't in the same file, then symbol is a namespace export, so return undefined.
const symbolIsUmdExport = symbolFile !== referenceFile;
return symbolIsUmdExport ? undefined : symbolFile;
}
return findAncestor(node.parent, (n): n is ModuleDeclaration | EnumDeclaration => isModuleOrEnumDeclaration(n) && getSymbolOfNode(n) === parentSymbol);
}
}
}
}
// When resolved as an expression identifier, if the given node references an import, return the declaration of
// that import. Otherwise, return undefined.
function getReferencedImportDeclaration(node: Identifier): Declaration {
node = getParseTreeNode(node, isIdentifier);
if (node) {
const symbol = getReferencedValueSymbol(node);
if (symbol && symbol.flags & SymbolFlags.Alias) {
return getDeclarationOfAliasSymbol(symbol);
}
}
return undefined;
}
function isSymbolOfDeclarationWithCollidingName(symbol: Symbol): boolean {
if (symbol.flags & SymbolFlags.BlockScoped) {
const links = getSymbolLinks(symbol);
if (links.isDeclarationWithCollidingName === undefined) {
const container = getEnclosingBlockScopeContainer(symbol.valueDeclaration);
if (isStatementWithLocals(container)) {
const nodeLinks = getNodeLinks(symbol.valueDeclaration);
if (!!resolveName(container.parent, symbol.name, SymbolFlags.Value, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined)) {
// redeclaration - always should be renamed
links.isDeclarationWithCollidingName = true;
}
else if (nodeLinks.flags & NodeCheckFlags.CapturedBlockScopedBinding) {
// binding is captured in the function
// should be renamed if:
// - binding is not top level - top level bindings never collide with anything
// AND
// - binding is not declared in loop, should be renamed to avoid name reuse across siblings
// let a, b
// { let x = 1; a = () => x; }
// { let x = 100; b = () => x; }
// console.log(a()); // should print '1'
// console.log(b()); // should print '100'
// OR
// - binding is declared inside loop but not in inside initializer of iteration statement or directly inside loop body
// * variables from initializer are passed to rewritten loop body as parameters so they are not captured directly
// * variables that are declared immediately in loop body will become top level variable after loop is rewritten and thus
// they will not collide with anything
const isDeclaredInLoop = nodeLinks.flags & NodeCheckFlags.BlockScopedBindingInLoop;
const inLoopInitializer = isIterationStatement(container, /*lookInLabeledStatements*/ false);
const inLoopBodyBlock = container.kind === SyntaxKind.Block && isIterationStatement(container.parent, /*lookInLabeledStatements*/ false);
links.isDeclarationWithCollidingName = !isBlockScopedContainerTopLevel(container) && (!isDeclaredInLoop || (!inLoopInitializer && !inLoopBodyBlock));
}
else {
links.isDeclarationWithCollidingName = false;
}
}
}
return links.isDeclarationWithCollidingName;
}
return false;
}
// When resolved as an expression identifier, if the given node references a nested block scoped entity with
// a name that either hides an existing name or might hide it when compiled downlevel,
// return the declaration of that entity. Otherwise, return undefined.
function getReferencedDeclarationWithCollidingName(node: Identifier): Declaration {
if (!isGeneratedIdentifier(node)) {
node = getParseTreeNode(node, isIdentifier);
if (node) {
const symbol = getReferencedValueSymbol(node);
if (symbol && isSymbolOfDeclarationWithCollidingName(symbol)) {
return symbol.valueDeclaration;
}
}
}
return undefined;
}
// Return true if the given node is a declaration of a nested block scoped entity with a name that either hides an
// existing name or might hide a name when compiled downlevel
function isDeclarationWithCollidingName(node: Declaration): boolean {
node = getParseTreeNode(node, isDeclaration);
if (node) {
const symbol = getSymbolOfNode(node);
if (symbol) {
return isSymbolOfDeclarationWithCollidingName(symbol);
}
}
return false;
}
function isValueAliasDeclaration(node: Node): boolean {
switch (node.kind) {
case SyntaxKind.ImportEqualsDeclaration:
case SyntaxKind.ImportClause:
case SyntaxKind.NamespaceImport:
case SyntaxKind.ImportSpecifier:
case SyntaxKind.ExportSpecifier:
return isAliasResolvedToValue(getSymbolOfNode(node) || unknownSymbol);
case SyntaxKind.ExportDeclaration:
const exportClause = (<ExportDeclaration>node).exportClause;
return exportClause && forEach(exportClause.elements, isValueAliasDeclaration);
case SyntaxKind.ExportAssignment:
return (<ExportAssignment>node).expression
&& (<ExportAssignment>node).expression.kind === SyntaxKind.Identifier
? isAliasResolvedToValue(getSymbolOfNode(node) || unknownSymbol)
: true;
}
return false;
}
function isTopLevelValueImportEqualsWithEntityName(node: ImportEqualsDeclaration): boolean {
node = getParseTreeNode(node, isImportEqualsDeclaration);
if (node === undefined || node.parent.kind !== SyntaxKind.SourceFile || !isInternalModuleImportEqualsDeclaration(node)) {
// parent is not source file or it is not reference to internal module
return false;
}
const isValue = isAliasResolvedToValue(getSymbolOfNode(node));
return isValue && node.moduleReference && !nodeIsMissing(node.moduleReference);
}
function isAliasResolvedToValue(symbol: Symbol): boolean {
const target = resolveAlias(symbol);
if (target === unknownSymbol) {
return true;
}
// const enums and modules that contain only const enums are not considered values from the emit perspective
// unless 'preserveConstEnums' option is set to true
return target.flags & SymbolFlags.Value &&
(compilerOptions.preserveConstEnums || !isConstEnumOrConstEnumOnlyModule(target));
}
function isConstEnumOrConstEnumOnlyModule(s: Symbol): boolean {
return isConstEnumSymbol(s) || s.constEnumOnlyModule;
}
function isReferencedAliasDeclaration(node: Node, checkChildren?: boolean): boolean {
if (isAliasSymbolDeclaration(node)) {
const symbol = getSymbolOfNode(node);
if (symbol && getSymbolLinks(symbol).referenced) {
return true;
}
}
if (checkChildren) {
return forEachChild(node, node => isReferencedAliasDeclaration(node, checkChildren));
}
return false;
}
function isImplementationOfOverload(node: FunctionLikeDeclaration) {
if (nodeIsPresent(node.body)) {
const symbol = getSymbolOfNode(node);
const signaturesOfSymbol = getSignaturesOfSymbol(symbol);
// If this function body corresponds to function with multiple signature, it is implementation of overload
// e.g.: function foo(a: string): string;
// function foo(a: number): number;
// function foo(a: any) { // This is implementation of the overloads
// return a;
// }
return signaturesOfSymbol.length > 1 ||
// If there is single signature for the symbol, it is overload if that signature isn't coming from the node
// e.g.: function foo(a: string): string;
// function foo(a: any) { // This is implementation of the overloads
// return a;
// }
(signaturesOfSymbol.length === 1 && signaturesOfSymbol[0].declaration !== node);
}
return false;
}
function isRequiredInitializedParameter(parameter: ParameterDeclaration) {
return strictNullChecks &&
!isOptionalParameter(parameter) &&
parameter.initializer &&
!(getModifierFlags(parameter) & ModifierFlags.ParameterPropertyModifier);
}
function getNodeCheckFlags(node: Node): NodeCheckFlags {
return getNodeLinks(node).flags;
}
function getEnumMemberValue(node: EnumMember): string | number {
computeEnumMemberValues(<EnumDeclaration>node.parent);
return getNodeLinks(node).enumMemberValue;
}
function canHaveConstantValue(node: Node): node is EnumMember | PropertyAccessExpression | ElementAccessExpression {
switch (node.kind) {
case SyntaxKind.EnumMember:
case SyntaxKind.PropertyAccessExpression:
case SyntaxKind.ElementAccessExpression:
return true;
}
return false;
}
function getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): string | number {
if (node.kind === SyntaxKind.EnumMember) {
return getEnumMemberValue(<EnumMember>node);
}
const symbol = getNodeLinks(node).resolvedSymbol;
if (symbol && (symbol.flags & SymbolFlags.EnumMember)) {
// inline property\index accesses only for const enums
if (isConstEnumDeclaration(symbol.valueDeclaration.parent)) {
return getEnumMemberValue(<EnumMember>symbol.valueDeclaration);
}
}
return undefined;
}
function isFunctionType(type: Type): boolean {
return type.flags & TypeFlags.Object && getSignaturesOfType(type, SignatureKind.Call).length > 0;
}
function getTypeReferenceSerializationKind(typeName: EntityName, location?: Node): TypeReferenceSerializationKind {
// Resolve the symbol as a value to ensure the type can be reached at runtime during emit.
const valueSymbol = resolveEntityName(typeName, SymbolFlags.Value, /*ignoreErrors*/ true, /*dontResolveAlias*/ false, location);
// Resolve the symbol as a type so that we can provide a more useful hint for the type serializer.
const typeSymbol = resolveEntityName(typeName, SymbolFlags.Type, /*ignoreErrors*/ true, /*dontResolveAlias*/ false, location);
if (valueSymbol && valueSymbol === typeSymbol) {
const globalPromiseSymbol = getGlobalPromiseConstructorSymbol(/*reportErrors*/ false);
if (globalPromiseSymbol && valueSymbol === globalPromiseSymbol) {
return TypeReferenceSerializationKind.Promise;
}
const constructorType = getTypeOfSymbol(valueSymbol);
if (constructorType && isConstructorType(constructorType)) {
return TypeReferenceSerializationKind.TypeWithConstructSignatureAndValue;
}
}
// We might not be able to resolve type symbol so use unknown type in that case (eg error case)
if (!typeSymbol) {
return TypeReferenceSerializationKind.ObjectType;
}
const type = getDeclaredTypeOfSymbol(typeSymbol);
if (type === unknownType) {
return TypeReferenceSerializationKind.Unknown;
}
else if (type.flags & TypeFlags.Any) {
return TypeReferenceSerializationKind.ObjectType;
}
else if (isTypeOfKind(type, TypeFlags.Void | TypeFlags.Nullable | TypeFlags.Never)) {
return TypeReferenceSerializationKind.VoidNullableOrNeverType;
}
else if (isTypeOfKind(type, TypeFlags.BooleanLike)) {
return TypeReferenceSerializationKind.BooleanType;
}
else if (isTypeOfKind(type, TypeFlags.NumberLike)) {
return TypeReferenceSerializationKind.NumberLikeType;
}
else if (isTypeOfKind(type, TypeFlags.StringLike)) {
return TypeReferenceSerializationKind.StringLikeType;
}
else if (isTupleType(type)) {
return TypeReferenceSerializationKind.ArrayLikeType;
}
else if (isTypeOfKind(type, TypeFlags.ESSymbol)) {
return TypeReferenceSerializationKind.ESSymbolType;
}
else if (isFunctionType(type)) {
return TypeReferenceSerializationKind.TypeWithCallSignature;
}
else if (isArrayType(type)) {
return TypeReferenceSerializationKind.ArrayLikeType;
}
else {
return TypeReferenceSerializationKind.ObjectType;
}
}
function writeTypeOfDeclaration(declaration: AccessorDeclaration | VariableLikeDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter) {
// Get type of the symbol if this is the valid symbol otherwise get type at location
const symbol = getSymbolOfNode(declaration);
let type = symbol && !(symbol.flags & (SymbolFlags.TypeLiteral | SymbolFlags.Signature))
? getWidenedLiteralType(getTypeOfSymbol(symbol))
: unknownType;
if (flags & TypeFormatFlags.AddUndefined) {
type = getNullableType(type, TypeFlags.Undefined);
}
getSymbolDisplayBuilder().buildTypeDisplay(type, writer, enclosingDeclaration, flags);
}
function writeReturnTypeOfSignatureDeclaration(signatureDeclaration: SignatureDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter) {
const signature = getSignatureFromDeclaration(signatureDeclaration);
getSymbolDisplayBuilder().buildTypeDisplay(getReturnTypeOfSignature(signature), writer, enclosingDeclaration, flags);
}
function writeTypeOfExpression(expr: Expression, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter) {
const type = getWidenedType(getRegularTypeOfExpression(expr));
getSymbolDisplayBuilder().buildTypeDisplay(type, writer, enclosingDeclaration, flags);
}
function hasGlobalName(name: string): boolean {
return globals.has(name);
}
function getReferencedValueSymbol(reference: Identifier, startInDeclarationContainer?: boolean): Symbol {
const resolvedSymbol = getNodeLinks(reference).resolvedSymbol;
if (resolvedSymbol) {
return resolvedSymbol;
}
let location: Node = reference;
if (startInDeclarationContainer) {
// When resolving the name of a declaration as a value, we need to start resolution
// at a point outside of the declaration.
const parent = reference.parent;
if (isDeclaration(parent) && reference === parent.name) {
location = getDeclarationContainer(parent);
}
}
return resolveName(location, reference.text, SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias, /*nodeNotFoundMessage*/ undefined, /*nameArg*/ undefined);
}
function getReferencedValueDeclaration(reference: Identifier): Declaration {
if (!isGeneratedIdentifier(reference)) {
reference = getParseTreeNode(reference, isIdentifier);
if (reference) {
const symbol = getReferencedValueSymbol(reference);
if (symbol) {
return getExportSymbolOfValueSymbolIfExported(symbol).valueDeclaration;
}
}
}
return undefined;
}
function isLiteralConstDeclaration(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration): boolean {
if (isConst(node)) {
const type = getTypeOfSymbol(getSymbolOfNode(node));
return !!(type.flags & TypeFlags.StringOrNumberLiteral && type.flags & TypeFlags.FreshLiteral);
}
return false;
}
function writeLiteralConstValue(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration, writer: SymbolWriter) {
const type = getTypeOfSymbol(getSymbolOfNode(node));
writer.writeStringLiteral(literalTypeToString(<LiteralType>type));
}
function createResolver(): EmitResolver {
// this variable and functions that use it are deliberately moved here from the outer scope
// to avoid scope pollution
const resolvedTypeReferenceDirectives = host.getResolvedTypeReferenceDirectives();
let fileToDirective: FileMap<string>;
if (resolvedTypeReferenceDirectives) {
// populate reverse mapping: file path -> type reference directive that was resolved to this file
fileToDirective = createFileMap<string>();
resolvedTypeReferenceDirectives.forEach((resolvedDirective, key) => {
if (!resolvedDirective) {
return;
}
const file = host.getSourceFile(resolvedDirective.resolvedFileName);
fileToDirective.set(file.path, key);
});
}
return {
getReferencedExportContainer,
getReferencedImportDeclaration,
getReferencedDeclarationWithCollidingName,
isDeclarationWithCollidingName,
isValueAliasDeclaration: node => {
node = getParseTreeNode(node);
// Synthesized nodes are always treated like values.
return node ? isValueAliasDeclaration(node) : true;
},
hasGlobalName,
isReferencedAliasDeclaration: (node, checkChildren?) => {
node = getParseTreeNode(node);
// Synthesized nodes are always treated as referenced.
return node ? isReferencedAliasDeclaration(node, checkChildren) : true;
},
getNodeCheckFlags: node => {
node = getParseTreeNode(node);
return node ? getNodeCheckFlags(node) : undefined;
},
isTopLevelValueImportEqualsWithEntityName,
isDeclarationVisible,
isImplementationOfOverload,
isRequiredInitializedParameter,
writeTypeOfDeclaration,
writeReturnTypeOfSignatureDeclaration,
writeTypeOfExpression,
isSymbolAccessible,
isEntityNameVisible,
getConstantValue: node => {
node = getParseTreeNode(node, canHaveConstantValue);
return node ? getConstantValue(node) : undefined;
},
collectLinkedAliases,
getReferencedValueDeclaration,
getTypeReferenceSerializationKind,
isOptionalParameter,
moduleExportsSomeValue,
isArgumentsLocalBinding,
getExternalModuleFileFromDeclaration,
getTypeReferenceDirectivesForEntityName,
getTypeReferenceDirectivesForSymbol,
isLiteralConstDeclaration,
writeLiteralConstValue,
getJsxFactoryEntity: () => _jsxFactoryEntity
};
// defined here to avoid outer scope pollution
function getTypeReferenceDirectivesForEntityName(node: EntityNameOrEntityNameExpression): string[] {
// program does not have any files with type reference directives - bail out
if (!fileToDirective) {
return undefined;
}
// property access can only be used as values
// qualified names can only be used as types\namespaces
// identifiers are treated as values only if they appear in type queries
const meaning = (node.kind === SyntaxKind.PropertyAccessExpression) || (node.kind === SyntaxKind.Identifier && isInTypeQuery(node))
? SymbolFlags.Value | SymbolFlags.ExportValue
: SymbolFlags.Type | SymbolFlags.Namespace;
const symbol = resolveEntityName(node, meaning, /*ignoreErrors*/ true);
return symbol && symbol !== unknownSymbol ? getTypeReferenceDirectivesForSymbol(symbol, meaning) : undefined;
}
// defined here to avoid outer scope pollution
function getTypeReferenceDirectivesForSymbol(symbol: Symbol, meaning?: SymbolFlags): string[] {
// program does not have any files with type reference directives - bail out
if (!fileToDirective) {
return undefined;
}
if (!isSymbolFromTypeDeclarationFile(symbol)) {
return undefined;
}
// check what declarations in the symbol can contribute to the target meaning
let typeReferenceDirectives: string[];
for (const decl of symbol.declarations) {
// check meaning of the local symbol to see if declaration needs to be analyzed further
if (decl.symbol && decl.symbol.flags & meaning) {
const file = getSourceFileOfNode(decl);
const typeReferenceDirective = fileToDirective.get(file.path);
if (typeReferenceDirective) {
(typeReferenceDirectives || (typeReferenceDirectives = [])).push(typeReferenceDirective);
}
else {
// found at least one entry that does not originate from type reference directive
return undefined;
}
}
}
return typeReferenceDirectives;
}
function isSymbolFromTypeDeclarationFile(symbol: Symbol): boolean {
// bail out if symbol does not have associated declarations (i.e. this is transient symbol created for property in binding pattern)
if (!symbol.declarations) {
return false;
}
// walk the parent chain for symbols to make sure that top level parent symbol is in the global scope
// external modules cannot define or contribute to type declaration files
let current = symbol;
while (true) {
const parent = getParentOfSymbol(current);
if (parent) {
current = parent;
}
else {
break;
}
}
if (current.valueDeclaration && current.valueDeclaration.kind === SyntaxKind.SourceFile && current.flags & SymbolFlags.ValueModule) {
return false;
}
// check that at least one declaration of top level symbol originates from type declaration file
for (const decl of symbol.declarations) {
const file = getSourceFileOfNode(decl);
if (fileToDirective.contains(file.path)) {
return true;
}
}
return false;
}
}
function getExternalModuleFileFromDeclaration(declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration): SourceFile {
const specifier = getExternalModuleName(declaration);
const moduleSymbol = resolveExternalModuleNameWorker(specifier, specifier, /*moduleNotFoundError*/ undefined);
if (!moduleSymbol) {
return undefined;
}
return getDeclarationOfKind(moduleSymbol, SyntaxKind.SourceFile) as SourceFile;
}
function initializeTypeChecker() {
// Bind all source files and propagate errors
for (const file of host.getSourceFiles()) {
bindSourceFile(file, compilerOptions);
}
// Initialize global symbol table
let augmentations: LiteralExpression[][];
for (const file of host.getSourceFiles()) {
if (!isExternalOrCommonJsModule(file)) {
mergeSymbolTable(globals, file.locals);
}
if (file.patternAmbientModules && file.patternAmbientModules.length) {
patternAmbientModules = concatenate(patternAmbientModules, file.patternAmbientModules);
}
if (file.moduleAugmentations.length) {
(augmentations || (augmentations = [])).push(file.moduleAugmentations);
}
if (file.symbol && file.symbol.globalExports) {
// Merge in UMD exports with first-in-wins semantics (see #9771)
const source = file.symbol.globalExports;
source.forEach((sourceSymbol, id) => {
if (!globals.has(id)) {
globals.set(id, sourceSymbol);
}
});
}
}
if (augmentations) {
// merge module augmentations.
// this needs to be done after global symbol table is initialized to make sure that all ambient modules are indexed
for (const list of augmentations) {
for (const augmentation of list) {
mergeModuleAugmentation(augmentation);
}
}
}
// Setup global builtins
addToSymbolTable(globals, builtinGlobals, Diagnostics.Declaration_name_conflicts_with_built_in_global_identifier_0);
getSymbolLinks(undefinedSymbol).type = undefinedWideningType;
getSymbolLinks(argumentsSymbol).type = getGlobalType("IArguments", /*arity*/ 0, /*reportErrors*/ true);
getSymbolLinks(unknownSymbol).type = unknownType;
// Initialize special types
globalArrayType = getGlobalType("Array", /*arity*/ 1, /*reportErrors*/ true);
globalObjectType = getGlobalType("Object", /*arity*/ 0, /*reportErrors*/ true);
globalFunctionType = getGlobalType("Function", /*arity*/ 0, /*reportErrors*/ true);
globalStringType = getGlobalType("String", /*arity*/ 0, /*reportErrors*/ true);
globalNumberType = getGlobalType("Number", /*arity*/ 0, /*reportErrors*/ true);
globalBooleanType = getGlobalType("Boolean", /*arity*/ 0, /*reportErrors*/ true);
globalRegExpType = getGlobalType("RegExp", /*arity*/ 0, /*reportErrors*/ true);
anyArrayType = createArrayType(anyType);
autoArrayType = createArrayType(autoType);
globalReadonlyArrayType = <GenericType>getGlobalTypeOrUndefined("ReadonlyArray", /*arity*/ 1);
anyReadonlyArrayType = globalReadonlyArrayType ? createTypeFromGenericGlobalType(globalReadonlyArrayType, [anyType]) : anyArrayType;
globalThisType = <GenericType>getGlobalTypeOrUndefined("ThisType", /*arity*/ 1);
}
function checkExternalEmitHelpers(location: Node, helpers: ExternalEmitHelpers) {
if ((requestedExternalEmitHelpers & helpers) !== helpers && compilerOptions.importHelpers) {
const sourceFile = getSourceFileOfNode(location);
if (isEffectiveExternalModule(sourceFile, compilerOptions) && !isInAmbientContext(location)) {
const helpersModule = resolveHelpersModule(sourceFile, location);
if (helpersModule !== unknownSymbol) {
const uncheckedHelpers = helpers & ~requestedExternalEmitHelpers;
for (let helper = ExternalEmitHelpers.FirstEmitHelper; helper <= ExternalEmitHelpers.LastEmitHelper; helper <<= 1) {
if (uncheckedHelpers & helper) {
const name = getHelperName(helper);
const symbol = getSymbol(helpersModule.exports, escapeIdentifier(name), SymbolFlags.Value);
if (!symbol) {
error(location, Diagnostics.This_syntax_requires_an_imported_helper_named_1_but_module_0_has_no_exported_member_1, externalHelpersModuleNameText, name);
}
}
}
}
requestedExternalEmitHelpers |= helpers;
}
}
}
function getHelperName(helper: ExternalEmitHelpers) {
switch (helper) {
case ExternalEmitHelpers.Extends: return "__extends";
case ExternalEmitHelpers.Assign: return "__assign";
case ExternalEmitHelpers.Rest: return "__rest";
case ExternalEmitHelpers.Decorate: return "__decorate";
case ExternalEmitHelpers.Metadata: return "__metadata";
case ExternalEmitHelpers.Param: return "__param";
case ExternalEmitHelpers.Awaiter: return "__awaiter";
case ExternalEmitHelpers.Generator: return "__generator";
case ExternalEmitHelpers.Values: return "__values";
case ExternalEmitHelpers.Read: return "__read";
case ExternalEmitHelpers.Spread: return "__spread";
case ExternalEmitHelpers.Await: return "__await";
case ExternalEmitHelpers.AsyncGenerator: return "__asyncGenerator";
case ExternalEmitHelpers.AsyncDelegator: return "__asyncDelegator";
case ExternalEmitHelpers.AsyncValues: return "__asyncValues";
default: Debug.fail("Unrecognized helper.");
}
}
function resolveHelpersModule(node: SourceFile, errorNode: Node) {
if (!externalHelpersModule) {
externalHelpersModule = resolveExternalModule(node, externalHelpersModuleNameText, Diagnostics.This_syntax_requires_an_imported_helper_but_module_0_cannot_be_found, errorNode) || unknownSymbol;
}
return externalHelpersModule;
}
// GRAMMAR CHECKING
function checkGrammarDecorators(node: Node): boolean {
if (!node.decorators) {
return false;
}
if (!nodeCanBeDecorated(node)) {
if (node.kind === SyntaxKind.MethodDeclaration && !ts.nodeIsPresent((<MethodDeclaration>node).body)) {
return grammarErrorOnFirstToken(node, Diagnostics.A_decorator_can_only_decorate_a_method_implementation_not_an_overload);
}
else {
return grammarErrorOnFirstToken(node, Diagnostics.Decorators_are_not_valid_here);
}
}
else if (node.kind === SyntaxKind.GetAccessor || node.kind === SyntaxKind.SetAccessor) {
const accessors = getAllAccessorDeclarations((<ClassDeclaration>node.parent).members, <AccessorDeclaration>node);
if (accessors.firstAccessor.decorators && node === accessors.secondAccessor) {
return grammarErrorOnFirstToken(node, Diagnostics.Decorators_cannot_be_applied_to_multiple_get_Slashset_accessors_of_the_same_name);
}
}
return false;
}
function checkGrammarModifiers(node: Node): boolean {
const quickResult = reportObviousModifierErrors(node);
if (quickResult !== undefined) {
return quickResult;
}
let lastStatic: Node, lastPrivate: Node, lastProtected: Node, lastDeclare: Node, lastAsync: Node, lastReadonly: Node;
let flags = ModifierFlags.None;
for (const modifier of node.modifiers) {
if (modifier.kind !== SyntaxKind.ReadonlyKeyword) {
if (node.kind === SyntaxKind.PropertySignature || node.kind === SyntaxKind.MethodSignature) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_type_member, tokenToString(modifier.kind));
}
if (node.kind === SyntaxKind.IndexSignature) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_an_index_signature, tokenToString(modifier.kind));
}
}
switch (modifier.kind) {
case SyntaxKind.ConstKeyword:
if (node.kind !== SyntaxKind.EnumDeclaration && node.parent.kind === SyntaxKind.ClassDeclaration) {
return grammarErrorOnNode(node, Diagnostics.A_class_member_cannot_have_the_0_keyword, tokenToString(SyntaxKind.ConstKeyword));
}
break;
case SyntaxKind.PublicKeyword:
case SyntaxKind.ProtectedKeyword:
case SyntaxKind.PrivateKeyword:
const text = visibilityToString(modifierToFlag(modifier.kind));
if (modifier.kind === SyntaxKind.ProtectedKeyword) {
lastProtected = modifier;
}
else if (modifier.kind === SyntaxKind.PrivateKeyword) {
lastPrivate = modifier;
}
if (flags & ModifierFlags.AccessibilityModifier) {
return grammarErrorOnNode(modifier, Diagnostics.Accessibility_modifier_already_seen);
}
else if (flags & ModifierFlags.Static) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, text, "static");
}
else if (flags & ModifierFlags.Readonly) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, text, "readonly");
}
else if (flags & ModifierFlags.Async) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, text, "async");
}
else if (node.parent.kind === SyntaxKind.ModuleBlock || node.parent.kind === SyntaxKind.SourceFile) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_module_or_namespace_element, text);
}
else if (flags & ModifierFlags.Abstract) {
if (modifier.kind === SyntaxKind.PrivateKeyword) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_be_used_with_1_modifier, text, "abstract");
}
else {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, text, "abstract");
}
}
flags |= modifierToFlag(modifier.kind);
break;
case SyntaxKind.StaticKeyword:
if (flags & ModifierFlags.Static) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "static");
}
else if (flags & ModifierFlags.Readonly) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "static", "readonly");
}
else if (flags & ModifierFlags.Async) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "static", "async");
}
else if (node.parent.kind === SyntaxKind.ModuleBlock || node.parent.kind === SyntaxKind.SourceFile) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_module_or_namespace_element, "static");
}
else if (node.kind === SyntaxKind.Parameter) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_parameter, "static");
}
else if (flags & ModifierFlags.Abstract) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_be_used_with_1_modifier, "static", "abstract");
}
flags |= ModifierFlags.Static;
lastStatic = modifier;
break;
case SyntaxKind.ReadonlyKeyword:
if (flags & ModifierFlags.Readonly) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "readonly");
}
else if (node.kind !== SyntaxKind.PropertyDeclaration && node.kind !== SyntaxKind.PropertySignature && node.kind !== SyntaxKind.IndexSignature && node.kind !== SyntaxKind.Parameter) {
// If node.kind === SyntaxKind.Parameter, checkParameter report an error if it's not a parameter property.
return grammarErrorOnNode(modifier, Diagnostics.readonly_modifier_can_only_appear_on_a_property_declaration_or_index_signature);
}
flags |= ModifierFlags.Readonly;
lastReadonly = modifier;
break;
case SyntaxKind.ExportKeyword:
if (flags & ModifierFlags.Export) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "export");
}
else if (flags & ModifierFlags.Ambient) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "export", "declare");
}
else if (flags & ModifierFlags.Abstract) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "export", "abstract");
}
else if (flags & ModifierFlags.Async) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "export", "async");
}
else if (node.parent.kind === SyntaxKind.ClassDeclaration) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_class_element, "export");
}
else if (node.kind === SyntaxKind.Parameter) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_parameter, "export");
}
flags |= ModifierFlags.Export;
break;
case SyntaxKind.DeclareKeyword:
if (flags & ModifierFlags.Ambient) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "declare");
}
else if (flags & ModifierFlags.Async) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_be_used_in_an_ambient_context, "async");
}
else if (node.parent.kind === SyntaxKind.ClassDeclaration) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_class_element, "declare");
}
else if (node.kind === SyntaxKind.Parameter) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_parameter, "declare");
}
else if (isInAmbientContext(node.parent) && node.parent.kind === SyntaxKind.ModuleBlock) {
return grammarErrorOnNode(modifier, Diagnostics.A_declare_modifier_cannot_be_used_in_an_already_ambient_context);
}
flags |= ModifierFlags.Ambient;
lastDeclare = modifier;
break;
case SyntaxKind.AbstractKeyword:
if (flags & ModifierFlags.Abstract) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "abstract");
}
if (node.kind !== SyntaxKind.ClassDeclaration) {
if (node.kind !== SyntaxKind.MethodDeclaration &&
node.kind !== SyntaxKind.PropertyDeclaration &&
node.kind !== SyntaxKind.GetAccessor &&
node.kind !== SyntaxKind.SetAccessor) {
return grammarErrorOnNode(modifier, Diagnostics.abstract_modifier_can_only_appear_on_a_class_method_or_property_declaration);
}
if (!(node.parent.kind === SyntaxKind.ClassDeclaration && getModifierFlags(node.parent) & ModifierFlags.Abstract)) {
return grammarErrorOnNode(modifier, Diagnostics.Abstract_methods_can_only_appear_within_an_abstract_class);
}
if (flags & ModifierFlags.Static) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_be_used_with_1_modifier, "static", "abstract");
}
if (flags & ModifierFlags.Private) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_be_used_with_1_modifier, "private", "abstract");
}
}
flags |= ModifierFlags.Abstract;
break;
case SyntaxKind.AsyncKeyword:
if (flags & ModifierFlags.Async) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "async");
}
else if (flags & ModifierFlags.Ambient || isInAmbientContext(node.parent)) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_be_used_in_an_ambient_context, "async");
}
else if (node.kind === SyntaxKind.Parameter) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_parameter, "async");
}
flags |= ModifierFlags.Async;
lastAsync = modifier;
break;
}
}
if (node.kind === SyntaxKind.Constructor) {
if (flags & ModifierFlags.Static) {
return grammarErrorOnNode(lastStatic, Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration, "static");
}
if (flags & ModifierFlags.Abstract) {
return grammarErrorOnNode(lastStatic, Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration, "abstract");
}
else if (flags & ModifierFlags.Async) {
return grammarErrorOnNode(lastAsync, Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration, "async");
}
else if (flags & ModifierFlags.Readonly) {
return grammarErrorOnNode(lastReadonly, Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration, "readonly");
}
return;
}
else if ((node.kind === SyntaxKind.ImportDeclaration || node.kind === SyntaxKind.ImportEqualsDeclaration) && flags & ModifierFlags.Ambient) {
return grammarErrorOnNode(lastDeclare, Diagnostics.A_0_modifier_cannot_be_used_with_an_import_declaration, "declare");
}
else if (node.kind === SyntaxKind.Parameter && (flags & ModifierFlags.ParameterPropertyModifier) && isBindingPattern((<ParameterDeclaration>node).name)) {
return grammarErrorOnNode(node, Diagnostics.A_parameter_property_may_not_be_declared_using_a_binding_pattern);
}
else if (node.kind === SyntaxKind.Parameter && (flags & ModifierFlags.ParameterPropertyModifier) && (<ParameterDeclaration>node).dotDotDotToken) {
return grammarErrorOnNode(node, Diagnostics.A_parameter_property_cannot_be_declared_using_a_rest_parameter);
}
if (flags & ModifierFlags.Async) {
return checkGrammarAsyncModifier(node, lastAsync);
}
}
/**
* true | false: Early return this value from checkGrammarModifiers.
* undefined: Need to do full checking on the modifiers.
*/
function reportObviousModifierErrors(node: Node): boolean | undefined {
return !node.modifiers
? false
: shouldReportBadModifier(node)
? grammarErrorOnFirstToken(node, Diagnostics.Modifiers_cannot_appear_here)
: undefined;
}
function shouldReportBadModifier(node: Node): boolean {
switch (node.kind) {
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.Constructor:
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.PropertySignature:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
case SyntaxKind.IndexSignature:
case SyntaxKind.ModuleDeclaration:
case SyntaxKind.ImportDeclaration:
case SyntaxKind.ImportEqualsDeclaration:
case SyntaxKind.ExportDeclaration:
case SyntaxKind.ExportAssignment:
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
case SyntaxKind.Parameter:
return false;
default:
if (node.parent.kind === SyntaxKind.ModuleBlock || node.parent.kind === SyntaxKind.SourceFile) {
return false;
}
switch (node.kind) {
case SyntaxKind.FunctionDeclaration:
return nodeHasAnyModifiersExcept(node, SyntaxKind.AsyncKeyword);
case SyntaxKind.ClassDeclaration:
return nodeHasAnyModifiersExcept(node, SyntaxKind.AbstractKeyword);
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.VariableStatement:
case SyntaxKind.TypeAliasDeclaration:
return true;
case SyntaxKind.EnumDeclaration:
return nodeHasAnyModifiersExcept(node, SyntaxKind.ConstKeyword);
default:
Debug.fail();
return false;
}
}
}
function nodeHasAnyModifiersExcept(node: Node, allowedModifier: SyntaxKind): boolean {
return node.modifiers.length > 1 || node.modifiers[0].kind !== allowedModifier;
}
function checkGrammarAsyncModifier(node: Node, asyncModifier: Node): boolean {
switch (node.kind) {
case SyntaxKind.MethodDeclaration:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
return false;
}
return grammarErrorOnNode(asyncModifier, Diagnostics._0_modifier_cannot_be_used_here, "async");
}
function checkGrammarForDisallowedTrailingComma(list: NodeArray<Node>): boolean {
if (list && list.hasTrailingComma) {
const start = list.end - ",".length;
const end = list.end;
const sourceFile = getSourceFileOfNode(list[0]);
return grammarErrorAtPos(sourceFile, start, end - start, Diagnostics.Trailing_comma_not_allowed);
}
}
function checkGrammarTypeParameterList(typeParameters: NodeArray<TypeParameterDeclaration>, file: SourceFile): boolean {
if (checkGrammarForDisallowedTrailingComma(typeParameters)) {
return true;
}
if (typeParameters && typeParameters.length === 0) {
const start = typeParameters.pos - "<".length;
const end = skipTrivia(file.text, typeParameters.end) + ">".length;
return grammarErrorAtPos(file, start, end - start, Diagnostics.Type_parameter_list_cannot_be_empty);
}
}
function checkGrammarParameterList(parameters: NodeArray<ParameterDeclaration>) {
let seenOptionalParameter = false;
const parameterCount = parameters.length;
for (let i = 0; i < parameterCount; i++) {
const parameter = parameters[i];
if (parameter.dotDotDotToken) {
if (i !== (parameterCount - 1)) {
return grammarErrorOnNode(parameter.dotDotDotToken, Diagnostics.A_rest_parameter_must_be_last_in_a_parameter_list);
}
if (isBindingPattern(parameter.name)) {
return grammarErrorOnNode(parameter.name, Diagnostics.A_rest_element_cannot_contain_a_binding_pattern);
}
if (parameter.questionToken) {
return grammarErrorOnNode(parameter.questionToken, Diagnostics.A_rest_parameter_cannot_be_optional);
}
if (parameter.initializer) {
return grammarErrorOnNode(parameter.name, Diagnostics.A_rest_parameter_cannot_have_an_initializer);
}
}
else if (parameter.questionToken) {
seenOptionalParameter = true;
if (parameter.initializer) {
return grammarErrorOnNode(parameter.name, Diagnostics.Parameter_cannot_have_question_mark_and_initializer);
}
}
else if (seenOptionalParameter && !parameter.initializer) {
return grammarErrorOnNode(parameter.name, Diagnostics.A_required_parameter_cannot_follow_an_optional_parameter);
}
}
}
function checkGrammarFunctionLikeDeclaration(node: FunctionLikeDeclaration): boolean {
// Prevent cascading error by short-circuit
const file = getSourceFileOfNode(node);
return checkGrammarDecorators(node) || checkGrammarModifiers(node) || checkGrammarTypeParameterList(node.typeParameters, file) ||
checkGrammarParameterList(node.parameters) || checkGrammarArrowFunction(node, file);
}
function checkGrammarClassLikeDeclaration(node: ClassLikeDeclaration): boolean {
const file = getSourceFileOfNode(node);
return checkGrammarClassDeclarationHeritageClauses(node) || checkGrammarTypeParameterList(node.typeParameters, file);
}
function checkGrammarArrowFunction(node: FunctionLikeDeclaration, file: SourceFile): boolean {
if (node.kind === SyntaxKind.ArrowFunction) {
const arrowFunction = <ArrowFunction>node;
const startLine = getLineAndCharacterOfPosition(file, arrowFunction.equalsGreaterThanToken.pos).line;
const endLine = getLineAndCharacterOfPosition(file, arrowFunction.equalsGreaterThanToken.end).line;
if (startLine !== endLine) {
return grammarErrorOnNode(arrowFunction.equalsGreaterThanToken, Diagnostics.Line_terminator_not_permitted_before_arrow);
}
}
return false;
}
function checkGrammarIndexSignatureParameters(node: SignatureDeclaration): boolean {
const parameter = node.parameters[0];
if (node.parameters.length !== 1) {
if (parameter) {
return grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_must_have_exactly_one_parameter);
}
else {
return grammarErrorOnNode(node, Diagnostics.An_index_signature_must_have_exactly_one_parameter);
}
}
if (parameter.dotDotDotToken) {
return grammarErrorOnNode(parameter.dotDotDotToken, Diagnostics.An_index_signature_cannot_have_a_rest_parameter);
}
if (getModifierFlags(parameter) !== 0) {
return grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_parameter_cannot_have_an_accessibility_modifier);
}
if (parameter.questionToken) {
return grammarErrorOnNode(parameter.questionToken, Diagnostics.An_index_signature_parameter_cannot_have_a_question_mark);
}
if (parameter.initializer) {
return grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_parameter_cannot_have_an_initializer);
}
if (!parameter.type) {
return grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_parameter_must_have_a_type_annotation);
}
if (parameter.type.kind !== SyntaxKind.StringKeyword && parameter.type.kind !== SyntaxKind.NumberKeyword) {
return grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_parameter_type_must_be_string_or_number);
}
if (!node.type) {
return grammarErrorOnNode(node, Diagnostics.An_index_signature_must_have_a_type_annotation);
}
}
function checkGrammarIndexSignature(node: SignatureDeclaration) {
// Prevent cascading error by short-circuit
return checkGrammarDecorators(node) || checkGrammarModifiers(node) || checkGrammarIndexSignatureParameters(node);
}
function checkGrammarForAtLeastOneTypeArgument(node: Node, typeArguments: NodeArray<TypeNode>): boolean {
if (typeArguments && typeArguments.length === 0) {
const sourceFile = getSourceFileOfNode(node);
const start = typeArguments.pos - "<".length;
const end = skipTrivia(sourceFile.text, typeArguments.end) + ">".length;
return grammarErrorAtPos(sourceFile, start, end - start, Diagnostics.Type_argument_list_cannot_be_empty);
}
}
function checkGrammarTypeArguments(node: Node, typeArguments: NodeArray<TypeNode>): boolean {
return checkGrammarForDisallowedTrailingComma(typeArguments) ||
checkGrammarForAtLeastOneTypeArgument(node, typeArguments);
}
function checkGrammarForOmittedArgument(node: CallExpression | NewExpression, args: NodeArray<Expression>): boolean {
if (args) {
const sourceFile = getSourceFileOfNode(node);
for (const arg of args) {
if (arg.kind === SyntaxKind.OmittedExpression) {
return grammarErrorAtPos(sourceFile, arg.pos, 0, Diagnostics.Argument_expression_expected);
}
}
}
}
function checkGrammarArguments(node: CallExpression | NewExpression, args: NodeArray<Expression>): boolean {
return checkGrammarForOmittedArgument(node, args);
}
function checkGrammarHeritageClause(node: HeritageClause): boolean {
const types = node.types;
if (checkGrammarForDisallowedTrailingComma(types)) {
return true;
}
if (types && types.length === 0) {
const listType = tokenToString(node.token);
const sourceFile = getSourceFileOfNode(node);
return grammarErrorAtPos(sourceFile, types.pos, 0, Diagnostics._0_list_cannot_be_empty, listType);
}
}
function checkGrammarClassDeclarationHeritageClauses(node: ClassLikeDeclaration) {
let seenExtendsClause = false;
let seenImplementsClause = false;
if (!checkGrammarDecorators(node) && !checkGrammarModifiers(node) && node.heritageClauses) {
for (const heritageClause of node.heritageClauses) {
if (heritageClause.token === SyntaxKind.ExtendsKeyword) {
if (seenExtendsClause) {
return grammarErrorOnFirstToken(heritageClause, Diagnostics.extends_clause_already_seen);
}
if (seenImplementsClause) {
return grammarErrorOnFirstToken(heritageClause, Diagnostics.extends_clause_must_precede_implements_clause);
}
if (heritageClause.types.length > 1) {
return grammarErrorOnFirstToken(heritageClause.types[1], Diagnostics.Classes_can_only_extend_a_single_class);
}
seenExtendsClause = true;
}
else {
Debug.assert(heritageClause.token === SyntaxKind.ImplementsKeyword);
if (seenImplementsClause) {
return grammarErrorOnFirstToken(heritageClause, Diagnostics.implements_clause_already_seen);
}
seenImplementsClause = true;
}
// Grammar checking heritageClause inside class declaration
checkGrammarHeritageClause(heritageClause);
}
}
}
function checkGrammarInterfaceDeclaration(node: InterfaceDeclaration) {
let seenExtendsClause = false;
if (node.heritageClauses) {
for (const heritageClause of node.heritageClauses) {
if (heritageClause.token === SyntaxKind.ExtendsKeyword) {
if (seenExtendsClause) {
return grammarErrorOnFirstToken(heritageClause, Diagnostics.extends_clause_already_seen);
}
seenExtendsClause = true;
}
else {
Debug.assert(heritageClause.token === SyntaxKind.ImplementsKeyword);
return grammarErrorOnFirstToken(heritageClause, Diagnostics.Interface_declaration_cannot_have_implements_clause);
}
// Grammar checking heritageClause inside class declaration
checkGrammarHeritageClause(heritageClause);
}
}
return false;
}
function checkGrammarComputedPropertyName(node: Node): boolean {
// If node is not a computedPropertyName, just skip the grammar checking
if (node.kind !== SyntaxKind.ComputedPropertyName) {
return false;
}
const computedPropertyName = <ComputedPropertyName>node;
if (computedPropertyName.expression.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>computedPropertyName.expression).operatorToken.kind === SyntaxKind.CommaToken) {
return grammarErrorOnNode(computedPropertyName.expression, Diagnostics.A_comma_expression_is_not_allowed_in_a_computed_property_name);
}
}
function checkGrammarForGenerator(node: FunctionLikeDeclaration) {
if (node.asteriskToken) {
Debug.assert(
node.kind === SyntaxKind.FunctionDeclaration ||
node.kind === SyntaxKind.FunctionExpression ||
node.kind === SyntaxKind.MethodDeclaration);
if (isInAmbientContext(node)) {
return grammarErrorOnNode(node.asteriskToken, Diagnostics.Generators_are_not_allowed_in_an_ambient_context);
}
if (!node.body) {
return grammarErrorOnNode(node.asteriskToken, Diagnostics.An_overload_signature_cannot_be_declared_as_a_generator);
}
}
}
function checkGrammarForInvalidQuestionMark(questionToken: Node, message: DiagnosticMessage): boolean {
if (questionToken) {
return grammarErrorOnNode(questionToken, message);
}
}
function checkGrammarObjectLiteralExpression(node: ObjectLiteralExpression, inDestructuring: boolean) {
const seen = createMap<SymbolFlags>();
const Property = 1;
const GetAccessor = 2;
const SetAccessor = 4;
const GetOrSetAccessor = GetAccessor | SetAccessor;
for (const prop of node.properties) {
if (prop.kind === SyntaxKind.SpreadAssignment) {
continue;
}
const name = prop.name;
if (name.kind === SyntaxKind.ComputedPropertyName) {
// If the name is not a ComputedPropertyName, the grammar checking will skip it
checkGrammarComputedPropertyName(<ComputedPropertyName>name);
}
if (prop.kind === SyntaxKind.ShorthandPropertyAssignment && !inDestructuring && (<ShorthandPropertyAssignment>prop).objectAssignmentInitializer) {
// having objectAssignmentInitializer is only valid in ObjectAssignmentPattern
// outside of destructuring it is a syntax error
return grammarErrorOnNode((<ShorthandPropertyAssignment>prop).equalsToken, Diagnostics.can_only_be_used_in_an_object_literal_property_inside_a_destructuring_assignment);
}
// Modifiers are never allowed on properties except for 'async' on a method declaration
if (prop.modifiers) {
for (const mod of prop.modifiers) {
if (mod.kind !== SyntaxKind.AsyncKeyword || prop.kind !== SyntaxKind.MethodDeclaration) {
grammarErrorOnNode(mod, Diagnostics._0_modifier_cannot_be_used_here, getTextOfNode(mod));
}
}
}
// ECMA-262 11.1.5 Object Initializer
// If previous is not undefined then throw a SyntaxError exception if any of the following conditions are true
// a.This production is contained in strict code and IsDataDescriptor(previous) is true and
// IsDataDescriptor(propId.descriptor) is true.
// b.IsDataDescriptor(previous) is true and IsAccessorDescriptor(propId.descriptor) is true.
// c.IsAccessorDescriptor(previous) is true and IsDataDescriptor(propId.descriptor) is true.
// d.IsAccessorDescriptor(previous) is true and IsAccessorDescriptor(propId.descriptor) is true
// and either both previous and propId.descriptor have[[Get]] fields or both previous and propId.descriptor have[[Set]] fields
let currentKind: number;
if (prop.kind === SyntaxKind.PropertyAssignment || prop.kind === SyntaxKind.ShorthandPropertyAssignment) {
// Grammar checking for computedPropertyName and shorthandPropertyAssignment
checkGrammarForInvalidQuestionMark((<PropertyAssignment>prop).questionToken, Diagnostics.An_object_member_cannot_be_declared_optional);
if (name.kind === SyntaxKind.NumericLiteral) {
checkGrammarNumericLiteral(<NumericLiteral>name);
}
currentKind = Property;
}
else if (prop.kind === SyntaxKind.MethodDeclaration) {
currentKind = Property;
}
else if (prop.kind === SyntaxKind.GetAccessor) {
currentKind = GetAccessor;
}
else if (prop.kind === SyntaxKind.SetAccessor) {
currentKind = SetAccessor;
}
else {
Debug.fail("Unexpected syntax kind:" + (<Node>prop).kind);
}
const effectiveName = getPropertyNameForPropertyNameNode(name);
if (effectiveName === undefined) {
continue;
}
const existingKind = seen.get(effectiveName);
if (!existingKind) {
seen.set(effectiveName, currentKind);
}
else {
if (currentKind === Property && existingKind === Property) {
grammarErrorOnNode(name, Diagnostics.Duplicate_identifier_0, getTextOfNode(name));
}
else if ((currentKind & GetOrSetAccessor) && (existingKind & GetOrSetAccessor)) {
if (existingKind !== GetOrSetAccessor && currentKind !== existingKind) {
seen.set(effectiveName, currentKind | existingKind);
}
else {
return grammarErrorOnNode(name, Diagnostics.An_object_literal_cannot_have_multiple_get_Slashset_accessors_with_the_same_name);
}
}
else {
return grammarErrorOnNode(name, Diagnostics.An_object_literal_cannot_have_property_and_accessor_with_the_same_name);
}
}
}
}
function checkGrammarJsxElement(node: JsxOpeningLikeElement) {
const seen = createMap<boolean>();
for (const attr of node.attributes.properties) {
if (attr.kind === SyntaxKind.JsxSpreadAttribute) {
continue;
}
const jsxAttr = (<JsxAttribute>attr);
const name = jsxAttr.name;
if (!seen.get(name.text)) {
seen.set(name.text, true);
}
else {
return grammarErrorOnNode(name, Diagnostics.JSX_elements_cannot_have_multiple_attributes_with_the_same_name);
}
const initializer = jsxAttr.initializer;
if (initializer && initializer.kind === SyntaxKind.JsxExpression && !(<JsxExpression>initializer).expression) {
return grammarErrorOnNode(jsxAttr.initializer, Diagnostics.JSX_attributes_must_only_be_assigned_a_non_empty_expression);
}
}
}
function checkGrammarForInOrForOfStatement(forInOrOfStatement: ForInStatement | ForOfStatement): boolean {
if (checkGrammarStatementInAmbientContext(forInOrOfStatement)) {
return true;
}
if (forInOrOfStatement.kind === SyntaxKind.ForOfStatement && forInOrOfStatement.awaitModifier) {
if ((forInOrOfStatement.flags & NodeFlags.AwaitContext) === NodeFlags.None) {
return grammarErrorOnNode(forInOrOfStatement.awaitModifier, Diagnostics.A_for_await_of_statement_is_only_allowed_within_an_async_function_or_async_generator);
}
}
if (forInOrOfStatement.initializer.kind === SyntaxKind.VariableDeclarationList) {
const variableList = <VariableDeclarationList>forInOrOfStatement.initializer;
if (!checkGrammarVariableDeclarationList(variableList)) {
const declarations = variableList.declarations;
// declarations.length can be zero if there is an error in variable declaration in for-of or for-in
// See http://www.ecma-international.org/ecma-262/6.0/#sec-for-in-and-for-of-statements for details
// For example:
// var let = 10;
// for (let of [1,2,3]) {} // this is invalid ES6 syntax
// for (let in [1,2,3]) {} // this is invalid ES6 syntax
// We will then want to skip on grammar checking on variableList declaration
if (!declarations.length) {
return false;
}
if (declarations.length > 1) {
const diagnostic = forInOrOfStatement.kind === SyntaxKind.ForInStatement
? Diagnostics.Only_a_single_variable_declaration_is_allowed_in_a_for_in_statement
: Diagnostics.Only_a_single_variable_declaration_is_allowed_in_a_for_of_statement;
return grammarErrorOnFirstToken(variableList.declarations[1], diagnostic);
}
const firstDeclaration = declarations[0];
if (firstDeclaration.initializer) {
const diagnostic = forInOrOfStatement.kind === SyntaxKind.ForInStatement
? Diagnostics.The_variable_declaration_of_a_for_in_statement_cannot_have_an_initializer
: Diagnostics.The_variable_declaration_of_a_for_of_statement_cannot_have_an_initializer;
return grammarErrorOnNode(firstDeclaration.name, diagnostic);
}
if (firstDeclaration.type) {
const diagnostic = forInOrOfStatement.kind === SyntaxKind.ForInStatement
? Diagnostics.The_left_hand_side_of_a_for_in_statement_cannot_use_a_type_annotation
: Diagnostics.The_left_hand_side_of_a_for_of_statement_cannot_use_a_type_annotation;
return grammarErrorOnNode(firstDeclaration, diagnostic);
}
}
}
return false;
}
function checkGrammarAccessor(accessor: AccessorDeclaration): boolean {
const kind = accessor.kind;
if (languageVersion < ScriptTarget.ES5) {
return grammarErrorOnNode(accessor.name, Diagnostics.Accessors_are_only_available_when_targeting_ECMAScript_5_and_higher);
}
else if (isInAmbientContext(accessor)) {
return grammarErrorOnNode(accessor.name, Diagnostics.An_accessor_cannot_be_declared_in_an_ambient_context);
}
else if (accessor.body === undefined && !(getModifierFlags(accessor) & ModifierFlags.Abstract)) {
return grammarErrorAtPos(getSourceFileOfNode(accessor), accessor.end - 1, ";".length, Diagnostics._0_expected, "{");
}
else if (accessor.body && getModifierFlags(accessor) & ModifierFlags.Abstract) {
return grammarErrorOnNode(accessor, Diagnostics.An_abstract_accessor_cannot_have_an_implementation);
}
else if (accessor.typeParameters) {
return grammarErrorOnNode(accessor.name, Diagnostics.An_accessor_cannot_have_type_parameters);
}
else if (!doesAccessorHaveCorrectParameterCount(accessor)) {
return grammarErrorOnNode(accessor.name,
kind === SyntaxKind.GetAccessor ?
Diagnostics.A_get_accessor_cannot_have_parameters :
Diagnostics.A_set_accessor_must_have_exactly_one_parameter);
}
else if (kind === SyntaxKind.SetAccessor) {
if (accessor.type) {
return grammarErrorOnNode(accessor.name, Diagnostics.A_set_accessor_cannot_have_a_return_type_annotation);
}
else {
const parameter = accessor.parameters[0];
if (parameter.dotDotDotToken) {
return grammarErrorOnNode(parameter.dotDotDotToken, Diagnostics.A_set_accessor_cannot_have_rest_parameter);
}
else if (parameter.questionToken) {
return grammarErrorOnNode(parameter.questionToken, Diagnostics.A_set_accessor_cannot_have_an_optional_parameter);
}
else if (parameter.initializer) {
return grammarErrorOnNode(accessor.name, Diagnostics.A_set_accessor_parameter_cannot_have_an_initializer);
}
}
}
}
/** Does the accessor have the right number of parameters?
* A get accessor has no parameters or a single `this` parameter.
* A set accessor has one parameter or a `this` parameter and one more parameter.
*/
function doesAccessorHaveCorrectParameterCount(accessor: AccessorDeclaration) {
return getAccessorThisParameter(accessor) || accessor.parameters.length === (accessor.kind === SyntaxKind.GetAccessor ? 0 : 1);
}
function getAccessorThisParameter(accessor: AccessorDeclaration): ParameterDeclaration {
if (accessor.parameters.length === (accessor.kind === SyntaxKind.GetAccessor ? 1 : 2)) {
return getThisParameter(accessor);
}
}
function checkGrammarForNonSymbolComputedProperty(node: DeclarationName, message: DiagnosticMessage) {
if (isDynamicName(node)) {
return grammarErrorOnNode(node, message);
}
}
function checkGrammarMethod(node: MethodDeclaration) {
if (checkGrammarDisallowedModifiersOnObjectLiteralExpressionMethod(node) ||
checkGrammarFunctionLikeDeclaration(node) ||
checkGrammarForGenerator(node)) {
return true;
}
if (node.parent.kind === SyntaxKind.ObjectLiteralExpression) {
if (checkGrammarForInvalidQuestionMark(node.questionToken, Diagnostics.An_object_member_cannot_be_declared_optional)) {
return true;
}
else if (node.body === undefined) {
return grammarErrorAtPos(getSourceFileOfNode(node), node.end - 1, ";".length, Diagnostics._0_expected, "{");
}
}
if (isClassLike(node.parent)) {
// Technically, computed properties in ambient contexts is disallowed
// for property declarations and accessors too, not just methods.
// However, property declarations disallow computed names in general,
// and accessors are not allowed in ambient contexts in general,
// so this error only really matters for methods.
if (isInAmbientContext(node)) {
return checkGrammarForNonSymbolComputedProperty(node.name, Diagnostics.A_computed_property_name_in_an_ambient_context_must_directly_refer_to_a_built_in_symbol);
}
else if (!node.body) {
return checkGrammarForNonSymbolComputedProperty(node.name, Diagnostics.A_computed_property_name_in_a_method_overload_must_directly_refer_to_a_built_in_symbol);
}
}
else if (node.parent.kind === SyntaxKind.InterfaceDeclaration) {
return checkGrammarForNonSymbolComputedProperty(node.name, Diagnostics.A_computed_property_name_in_an_interface_must_directly_refer_to_a_built_in_symbol);
}
else if (node.parent.kind === SyntaxKind.TypeLiteral) {
return checkGrammarForNonSymbolComputedProperty(node.name, Diagnostics.A_computed_property_name_in_a_type_literal_must_directly_refer_to_a_built_in_symbol);
}
}
function checkGrammarBreakOrContinueStatement(node: BreakOrContinueStatement): boolean {
let current: Node = node;
while (current) {
if (isFunctionLike(current)) {
return grammarErrorOnNode(node, Diagnostics.Jump_target_cannot_cross_function_boundary);
}
switch (current.kind) {
case SyntaxKind.LabeledStatement:
if (node.label && (<LabeledStatement>current).label.text === node.label.text) {
// found matching label - verify that label usage is correct
// continue can only target labels that are on iteration statements
const isMisplacedContinueLabel = node.kind === SyntaxKind.ContinueStatement
&& !isIterationStatement((<LabeledStatement>current).statement, /*lookInLabeledStatement*/ true);
if (isMisplacedContinueLabel) {
return grammarErrorOnNode(node, Diagnostics.A_continue_statement_can_only_jump_to_a_label_of_an_enclosing_iteration_statement);
}
return false;
}
break;
case SyntaxKind.SwitchStatement:
if (node.kind === SyntaxKind.BreakStatement && !node.label) {
// unlabeled break within switch statement - ok
return false;
}
break;
default:
if (isIterationStatement(current, /*lookInLabeledStatement*/ false) && !node.label) {
// unlabeled break or continue within iteration statement - ok
return false;
}
break;
}
current = current.parent;
}
if (node.label) {
const message = node.kind === SyntaxKind.BreakStatement
? Diagnostics.A_break_statement_can_only_jump_to_a_label_of_an_enclosing_statement
: Diagnostics.A_continue_statement_can_only_jump_to_a_label_of_an_enclosing_iteration_statement;
return grammarErrorOnNode(node, message);
}
else {
const message = node.kind === SyntaxKind.BreakStatement
? Diagnostics.A_break_statement_can_only_be_used_within_an_enclosing_iteration_or_switch_statement
: Diagnostics.A_continue_statement_can_only_be_used_within_an_enclosing_iteration_statement;
return grammarErrorOnNode(node, message);
}
}
function checkGrammarBindingElement(node: BindingElement) {
if (node.dotDotDotToken) {
const elements = (<BindingPattern>node.parent).elements;
if (node !== lastOrUndefined(elements)) {
return grammarErrorOnNode(node, Diagnostics.A_rest_element_must_be_last_in_a_destructuring_pattern);
}
if (node.name.kind === SyntaxKind.ArrayBindingPattern || node.name.kind === SyntaxKind.ObjectBindingPattern) {
return grammarErrorOnNode(node.name, Diagnostics.A_rest_element_cannot_contain_a_binding_pattern);
}
if (node.initializer) {
// Error on equals token which immediately precedes the initializer
return grammarErrorAtPos(getSourceFileOfNode(node), node.initializer.pos - 1, 1, Diagnostics.A_rest_element_cannot_have_an_initializer);
}
}
}
function isStringOrNumberLiteralExpression(expr: Expression) {
return expr.kind === SyntaxKind.StringLiteral || expr.kind === SyntaxKind.NumericLiteral ||
expr.kind === SyntaxKind.PrefixUnaryExpression && (<PrefixUnaryExpression>expr).operator === SyntaxKind.MinusToken &&
(<PrefixUnaryExpression>expr).operand.kind === SyntaxKind.NumericLiteral;
}
function checkGrammarVariableDeclaration(node: VariableDeclaration) {
if (node.parent.parent.kind !== SyntaxKind.ForInStatement && node.parent.parent.kind !== SyntaxKind.ForOfStatement) {
if (isInAmbientContext(node)) {
if (node.initializer) {
if (isConst(node) && !node.type) {
if (!isStringOrNumberLiteralExpression(node.initializer)) {
return grammarErrorOnNode(node.initializer, Diagnostics.A_const_initializer_in_an_ambient_context_must_be_a_string_or_numeric_literal);
}
}
else {
// Error on equals token which immediate precedes the initializer
const equalsTokenLength = "=".length;
return grammarErrorAtPos(getSourceFileOfNode(node), node.initializer.pos - equalsTokenLength,
equalsTokenLength, Diagnostics.Initializers_are_not_allowed_in_ambient_contexts);
}
}
if (node.initializer && !(isConst(node) && isStringOrNumberLiteralExpression(node.initializer))) {
// Error on equals token which immediate precedes the initializer
const equalsTokenLength = "=".length;
return grammarErrorAtPos(getSourceFileOfNode(node), node.initializer.pos - equalsTokenLength,
equalsTokenLength, Diagnostics.Initializers_are_not_allowed_in_ambient_contexts);
}
}
else if (!node.initializer) {
if (isBindingPattern(node.name) && !isBindingPattern(node.parent)) {
return grammarErrorOnNode(node, Diagnostics.A_destructuring_declaration_must_have_an_initializer);
}
if (isConst(node)) {
return grammarErrorOnNode(node, Diagnostics.const_declarations_must_be_initialized);
}
}
}
if (compilerOptions.module !== ModuleKind.ES2015 && compilerOptions.module !== ModuleKind.System && !compilerOptions.noEmit &&
!isInAmbientContext(node.parent.parent) && hasModifier(node.parent.parent, ModifierFlags.Export)) {
checkESModuleMarker(node.name);
}
const checkLetConstNames = (isLet(node) || isConst(node));
// 1. LexicalDeclaration : LetOrConst BindingList ;
// It is a Syntax Error if the BoundNames of BindingList contains "let".
// 2. ForDeclaration: ForDeclaration : LetOrConst ForBinding
// It is a Syntax Error if the BoundNames of ForDeclaration contains "let".
// It is a SyntaxError if a VariableDeclaration or VariableDeclarationNoIn occurs within strict code
// and its Identifier is eval or arguments
return checkLetConstNames && checkGrammarNameInLetOrConstDeclarations(node.name);
}
function checkESModuleMarker(name: Identifier | BindingPattern): boolean {
if (name.kind === SyntaxKind.Identifier) {
if (unescapeIdentifier(name.text) === "__esModule") {
return grammarErrorOnNode(name, Diagnostics.Identifier_expected_esModule_is_reserved_as_an_exported_marker_when_transforming_ECMAScript_modules);
}
}
else {
const elements = (<BindingPattern>name).elements;
for (const element of elements) {
if (!isOmittedExpression(element)) {
return checkESModuleMarker(element.name);
}
}
}
}
function checkGrammarNameInLetOrConstDeclarations(name: Identifier | BindingPattern): boolean {
if (name.kind === SyntaxKind.Identifier) {
if ((<Identifier>name).originalKeywordKind === SyntaxKind.LetKeyword) {
return grammarErrorOnNode(name, Diagnostics.let_is_not_allowed_to_be_used_as_a_name_in_let_or_const_declarations);
}
}
else {
const elements = (<BindingPattern>name).elements;
for (const element of elements) {
if (!isOmittedExpression(element)) {
checkGrammarNameInLetOrConstDeclarations(element.name);
}
}
}
}
function checkGrammarVariableDeclarationList(declarationList: VariableDeclarationList): boolean {
const declarations = declarationList.declarations;
if (checkGrammarForDisallowedTrailingComma(declarationList.declarations)) {
return true;
}
if (!declarationList.declarations.length) {
return grammarErrorAtPos(getSourceFileOfNode(declarationList), declarations.pos, declarations.end - declarations.pos, Diagnostics.Variable_declaration_list_cannot_be_empty);
}
}
function allowLetAndConstDeclarations(parent: Node): boolean {
switch (parent.kind) {
case SyntaxKind.IfStatement:
case SyntaxKind.DoStatement:
case SyntaxKind.WhileStatement:
case SyntaxKind.WithStatement:
case SyntaxKind.ForStatement:
case SyntaxKind.ForInStatement:
case SyntaxKind.ForOfStatement:
return false;
case SyntaxKind.LabeledStatement:
return allowLetAndConstDeclarations(parent.parent);
}
return true;
}
function checkGrammarForDisallowedLetOrConstStatement(node: VariableStatement) {
if (!allowLetAndConstDeclarations(node.parent)) {
if (isLet(node.declarationList)) {
return grammarErrorOnNode(node, Diagnostics.let_declarations_can_only_be_declared_inside_a_block);
}
else if (isConst(node.declarationList)) {
return grammarErrorOnNode(node, Diagnostics.const_declarations_can_only_be_declared_inside_a_block);
}
}
}
function checkGrammarMetaProperty(node: MetaProperty) {
if (node.keywordToken === SyntaxKind.NewKeyword) {
if (node.name.text !== "target") {
return grammarErrorOnNode(node.name, Diagnostics._0_is_not_a_valid_meta_property_for_keyword_1_Did_you_mean_2, node.name.text, tokenToString(node.keywordToken), "target");
}
}
}
function hasParseDiagnostics(sourceFile: SourceFile): boolean {
return sourceFile.parseDiagnostics.length > 0;
}
function grammarErrorOnFirstToken(node: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): boolean {
const sourceFile = getSourceFileOfNode(node);
if (!hasParseDiagnostics(sourceFile)) {
const span = getSpanOfTokenAtPosition(sourceFile, node.pos);
diagnostics.add(createFileDiagnostic(sourceFile, span.start, span.length, message, arg0, arg1, arg2));
return true;
}
}
function grammarErrorAtPos(sourceFile: SourceFile, start: number, length: number, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): boolean {
if (!hasParseDiagnostics(sourceFile)) {
diagnostics.add(createFileDiagnostic(sourceFile, start, length, message, arg0, arg1, arg2));
return true;
}
}
function grammarErrorOnNode(node: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): boolean {
const sourceFile = getSourceFileOfNode(node);
if (!hasParseDiagnostics(sourceFile)) {
diagnostics.add(createDiagnosticForNode(node, message, arg0, arg1, arg2));
return true;
}
}
function checkGrammarConstructorTypeParameters(node: ConstructorDeclaration) {
if (node.typeParameters) {
return grammarErrorAtPos(getSourceFileOfNode(node), node.typeParameters.pos, node.typeParameters.end - node.typeParameters.pos, Diagnostics.Type_parameters_cannot_appear_on_a_constructor_declaration);
}
}
function checkGrammarConstructorTypeAnnotation(node: ConstructorDeclaration) {
if (node.type) {
return grammarErrorOnNode(node.type, Diagnostics.Type_annotation_cannot_appear_on_a_constructor_declaration);
}
}
function checkGrammarProperty(node: PropertyDeclaration) {
if (isClassLike(node.parent)) {
if (checkGrammarForNonSymbolComputedProperty(node.name, Diagnostics.A_computed_property_name_in_a_class_property_declaration_must_directly_refer_to_a_built_in_symbol)) {
return true;
}
}
else if (node.parent.kind === SyntaxKind.InterfaceDeclaration) {
if (checkGrammarForNonSymbolComputedProperty(node.name, Diagnostics.A_computed_property_name_in_an_interface_must_directly_refer_to_a_built_in_symbol)) {
return true;
}
if (node.initializer) {
return grammarErrorOnNode(node.initializer, Diagnostics.An_interface_property_cannot_have_an_initializer);
}
}
else if (node.parent.kind === SyntaxKind.TypeLiteral) {
if (checkGrammarForNonSymbolComputedProperty(node.name, Diagnostics.A_computed_property_name_in_a_type_literal_must_directly_refer_to_a_built_in_symbol)) {
return true;
}
if (node.initializer) {
return grammarErrorOnNode(node.initializer, Diagnostics.A_type_literal_property_cannot_have_an_initializer);
}
}
if (isInAmbientContext(node) && node.initializer) {
return grammarErrorOnFirstToken(node.initializer, Diagnostics.Initializers_are_not_allowed_in_ambient_contexts);
}
}
function checkGrammarTopLevelElementForRequiredDeclareModifier(node: Node): boolean {
// A declare modifier is required for any top level .d.ts declaration except export=, export default, export as namespace
// interfaces and imports categories:
//
// DeclarationElement:
// ExportAssignment
// export_opt InterfaceDeclaration
// export_opt TypeAliasDeclaration
// export_opt ImportDeclaration
// export_opt ExternalImportDeclaration
// export_opt AmbientDeclaration
//
// TODO: The spec needs to be amended to reflect this grammar.
if (node.kind === SyntaxKind.InterfaceDeclaration ||
node.kind === SyntaxKind.TypeAliasDeclaration ||
node.kind === SyntaxKind.ImportDeclaration ||
node.kind === SyntaxKind.ImportEqualsDeclaration ||
node.kind === SyntaxKind.ExportDeclaration ||
node.kind === SyntaxKind.ExportAssignment ||
node.kind === SyntaxKind.NamespaceExportDeclaration ||
getModifierFlags(node) & (ModifierFlags.Ambient | ModifierFlags.Export | ModifierFlags.Default)) {
return false;
}
return grammarErrorOnFirstToken(node, Diagnostics.A_declare_modifier_is_required_for_a_top_level_declaration_in_a_d_ts_file);
}
function checkGrammarTopLevelElementsForRequiredDeclareModifier(file: SourceFile): boolean {
for (const decl of file.statements) {
if (isDeclaration(decl) || decl.kind === SyntaxKind.VariableStatement) {
if (checkGrammarTopLevelElementForRequiredDeclareModifier(decl)) {
return true;
}
}
}
}
function checkGrammarSourceFile(node: SourceFile): boolean {
return isInAmbientContext(node) && checkGrammarTopLevelElementsForRequiredDeclareModifier(node);
}
function checkGrammarStatementInAmbientContext(node: Node): boolean {
if (isInAmbientContext(node)) {
// An accessors is already reported about the ambient context
if (isAccessor(node.parent.kind)) {
return getNodeLinks(node).hasReportedStatementInAmbientContext = true;
}
// Find containing block which is either Block, ModuleBlock, SourceFile
const links = getNodeLinks(node);
if (!links.hasReportedStatementInAmbientContext && isFunctionLike(node.parent)) {
return getNodeLinks(node).hasReportedStatementInAmbientContext = grammarErrorOnFirstToken(node, Diagnostics.An_implementation_cannot_be_declared_in_ambient_contexts);
}
// We are either parented by another statement, or some sort of block.
// If we're in a block, we only want to really report an error once
// to prevent noisiness. So use a bit on the block to indicate if
// this has already been reported, and don't report if it has.
//
if (node.parent.kind === SyntaxKind.Block || node.parent.kind === SyntaxKind.ModuleBlock || node.parent.kind === SyntaxKind.SourceFile) {
const links = getNodeLinks(node.parent);
// Check if the containing block ever report this error
if (!links.hasReportedStatementInAmbientContext) {
return links.hasReportedStatementInAmbientContext = grammarErrorOnFirstToken(node, Diagnostics.Statements_are_not_allowed_in_ambient_contexts);
}
}
else {
// We must be parented by a statement. If so, there's no need
// to report the error as our parent will have already done it.
// Debug.assert(isStatement(node.parent));
}
}
}
function checkGrammarNumericLiteral(node: NumericLiteral): boolean {
// Grammar checking
if (node.numericLiteralFlags & NumericLiteralFlags.Octal) {
let diagnosticMessage: DiagnosticMessage | undefined;
if (languageVersion >= ScriptTarget.ES5) {
diagnosticMessage = Diagnostics.Octal_literals_are_not_available_when_targeting_ECMAScript_5_and_higher_Use_the_syntax_0;
}
else if (isChildOfNodeWithKind(node, SyntaxKind.LiteralType)) {
diagnosticMessage = Diagnostics.Octal_literal_types_must_use_ES2015_syntax_Use_the_syntax_0;
}
else if (isChildOfNodeWithKind(node, SyntaxKind.EnumMember)) {
diagnosticMessage = Diagnostics.Octal_literals_are_not_allowed_in_enums_members_initializer_Use_the_syntax_0;
}
if (diagnosticMessage) {
const withMinus = isPrefixUnaryExpression(node.parent) && node.parent.operator === SyntaxKind.MinusToken;
const literal = `${withMinus ? "-" : ""}0o${node.text}`;
return grammarErrorOnNode(withMinus ? node.parent : node, diagnosticMessage, literal);
}
}
}
function grammarErrorAfterFirstToken(node: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): boolean {
const sourceFile = getSourceFileOfNode(node);
if (!hasParseDiagnostics(sourceFile)) {
const span = getSpanOfTokenAtPosition(sourceFile, node.pos);
diagnostics.add(createFileDiagnostic(sourceFile, textSpanEnd(span), /*length*/ 0, message, arg0, arg1, arg2));
return true;
}
}
function getAmbientModules(): Symbol[] {
const result: Symbol[] = [];
globals.forEach((global, sym) => {
if (ambientModuleSymbolRegex.test(sym)) {
result.push(global);
}
});
return result;
}
}
/** Like 'isDeclarationName', but returns true for LHS of `import { x as y }` or `export { x as y }`. */
function isDeclarationNameOrImportPropertyName(name: Node): boolean {
switch (name.parent.kind) {
case SyntaxKind.ImportSpecifier:
case SyntaxKind.ExportSpecifier:
if ((name.parent as ImportOrExportSpecifier).propertyName) {
return true;
}
// falls through
default:
return isDeclarationName(name);
}
}
}