Merge pull request #14141 from Microsoft/contextualThisType

Typed 'this' in object literal methods
This commit is contained in:
Anders Hejlsberg
2017-03-06 11:11:40 -10:00
committed by GitHub
47 changed files with 2574 additions and 394 deletions

View File

@@ -259,6 +259,7 @@ namespace ts {
let globalNumberType: ObjectType;
let globalBooleanType: ObjectType;
let globalRegExpType: ObjectType;
let globalThisType: GenericType;
let anyArrayType: Type;
let autoArrayType: Type;
let anyReadonlyArrayType: Type;
@@ -434,6 +435,12 @@ namespace ts {
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);
@@ -6083,6 +6090,11 @@ namespace ts {
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
@@ -9015,11 +9027,19 @@ namespace ts {
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 = transformTypeOfMembers(type, prop => {
const widened = getWidenedType(prop);
return prop === widened ? prop : widened;
});
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,
@@ -10065,8 +10085,31 @@ namespace ts {
return f(type) ? type : neverType;
}
function mapType(type: Type, f: (t: Type) => Type): Type {
return type.flags & TypeFlags.Union ? getUnionType(map((<UnionType>type).types, f)) : f(type);
// 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) {
@@ -11502,8 +11545,29 @@ namespace ts {
}
}
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 (isContextSensitiveFunctionOrObjectLiteralMethod(func) && func.kind !== SyntaxKind.ArrowFunction) {
if (func.kind === SyntaxKind.ArrowFunction) {
return undefined;
}
if (isContextSensitiveFunctionOrObjectLiteralMethod(func)) {
const contextualSignature = getContextualSignature(func);
if (contextualSignature) {
const thisParameter = contextualSignature.thisParameter;
@@ -11512,6 +11576,40 @@ namespace ts {
}
}
}
if (compilerOptions.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 contextual type for the containing object literal or the type of the object
// literal itself.
return 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;
}
@@ -11710,42 +11808,15 @@ namespace ts {
return undefined;
}
// Apply a mapping function to a contextual type and return the resulting type. If the contextual type
// is a union type, the mapping function is applied to each constituent type and a union of the resulting
// types is returned.
function applyToContextualType(type: Type, mapper: (t: Type) => Type): Type {
if (!(type.flags & TypeFlags.Union)) {
return mapper(type);
}
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 getTypeOfPropertyOfContextualType(type: Type, name: string) {
return applyToContextualType(type, t => {
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 applyToContextualType(type, t => getIndexTypeOfStructuredType(t, kind));
return mapType(type, t => getIndexTypeOfStructuredType(t, kind));
}
// Return true if the given contextual type is a tuple-like type
@@ -11904,6 +11975,16 @@ namespace ts {
return undefined;
}
function getContextualMapper(node: Node) {
while (node) {
if (node.contextualMapper) {
return node.contextualMapper;
}
node = node.parent;
}
return 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 {
@@ -11994,31 +12075,12 @@ namespace ts {
return result;
}
/**
* Detect if the mapper implies an inference context. Specifically, there are 4 possible values
* for a mapper. Let's go through each one of them:
*
* 1. undefined - this means we are not doing inferential typing, but we may do contextual typing,
* which could cause us to assign a parameter a type
* 2. identityMapper - means we want to avoid assigning a parameter a type, whether or not we are in
* inferential typing (context is undefined for the identityMapper)
* 3. a mapper created by createInferenceMapper - we are doing inferential typing, we want to assign
* types to parameters and fix type parameters (context is defined)
* 4. an instantiation mapper created by createTypeMapper or createTypeEraser - this should never be
* passed as the contextual mapper when checking an expression (context is undefined for these)
*
* isInferentialContext is detecting if we are in case 3
*/
function isInferentialContext(mapper: TypeMapper) {
return mapper && mapper.context;
}
function checkSpreadExpression(node: SpreadElement, contextualMapper?: TypeMapper): Type {
function checkSpreadExpression(node: SpreadElement, checkMode?: CheckMode): Type {
if (languageVersion < ScriptTarget.ES2015 && compilerOptions.downlevelIteration) {
checkExternalEmitHelpers(node, ExternalEmitHelpers.SpreadIncludes);
}
const arrayOrIterableType = checkExpression(node.expression, contextualMapper);
const arrayOrIterableType = checkExpression(node.expression, checkMode);
return checkIteratedTypeOrElementType(arrayOrIterableType, node.expression, /*allowStringInput*/ false, /*allowAsyncIterable*/ false);
}
@@ -12027,7 +12089,7 @@ namespace ts {
(node.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>node).operatorToken.kind === SyntaxKind.EqualsToken);
}
function checkArrayLiteral(node: ArrayLiteralExpression, contextualMapper?: TypeMapper): Type {
function checkArrayLiteral(node: ArrayLiteralExpression, checkMode?: CheckMode): Type {
const elements = node.elements;
let hasSpreadElement = false;
const elementTypes: Type[] = [];
@@ -12046,7 +12108,7 @@ namespace ts {
// 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, contextualMapper);
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) {
@@ -12054,7 +12116,7 @@ namespace ts {
}
}
else {
const type = checkExpressionForMutableLocation(e, contextualMapper);
const type = checkExpressionForMutableLocation(e, checkMode);
elementTypes.push(type);
}
hasSpreadElement = hasSpreadElement || e.kind === SyntaxKind.SpreadElement;
@@ -12169,7 +12231,7 @@ namespace ts {
return createIndexInfo(unionType, /*isReadonly*/ false);
}
function checkObjectLiteral(node: ObjectLiteralExpression, contextualMapper?: TypeMapper): Type {
function checkObjectLiteral(node: ObjectLiteralExpression, checkMode?: CheckMode): Type {
const inDestructuringPattern = isAssignmentTarget(node);
// Grammar checking
checkGrammarObjectLiteralExpression(node, inDestructuringPattern);
@@ -12196,14 +12258,14 @@ namespace ts {
isObjectLiteralMethod(memberDecl)) {
let type: Type;
if (memberDecl.kind === SyntaxKind.PropertyAssignment) {
type = checkPropertyAssignment(<PropertyAssignment>memberDecl, contextualMapper);
type = checkPropertyAssignment(<PropertyAssignment>memberDecl, checkMode);
}
else if (memberDecl.kind === SyntaxKind.MethodDeclaration) {
type = checkObjectLiteralMethod(<MethodDeclaration>memberDecl, contextualMapper);
type = checkObjectLiteralMethod(<MethodDeclaration>memberDecl, checkMode);
}
else {
Debug.assert(memberDecl.kind === SyntaxKind.ShorthandPropertyAssignment);
type = checkExpressionForMutableLocation((<ShorthandPropertyAssignment>memberDecl).name, contextualMapper);
type = checkExpressionForMutableLocation((<ShorthandPropertyAssignment>memberDecl).name, checkMode);
}
typeFlags |= type.flags;
@@ -12272,7 +12334,7 @@ namespace ts {
// A set accessor declaration is processed in the same manner
// as an ordinary function declaration with a single parameter and a Void return type.
Debug.assert(memberDecl.kind === SyntaxKind.GetAccessor || memberDecl.kind === SyntaxKind.SetAccessor);
checkAccessorDeclaration(<AccessorDeclaration>memberDecl);
checkNodeDeferred(memberDecl);
}
if (hasDynamicName(memberDecl)) {
@@ -12409,7 +12471,7 @@ namespace ts {
* @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, contextualMapper?: TypeMapper) {
function createJsxAttributesTypeFromAttributesProperty(openingLikeElement: JsxOpeningLikeElement, filter?: (symbol: Symbol) => boolean, checkMode?: CheckMode) {
const attributes = openingLikeElement.attributes;
let attributesTable = createMap<Symbol>();
let spread: Type = emptyObjectType;
@@ -12418,7 +12480,7 @@ namespace ts {
const member = attributeDecl.symbol;
if (isJsxAttribute(attributeDecl)) {
const exprType = attributeDecl.initializer ?
checkExpression(attributeDecl.initializer, contextualMapper) :
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);
@@ -12489,8 +12551,8 @@ namespace ts {
* (See "checkApplicableSignatureForJsxOpeningLikeElement" for how the function is used)
* @param node a JSXAttributes to be resolved of its type
*/
function checkJsxAttributes(node: JsxAttributes, contextualMapper?: TypeMapper) {
return createJsxAttributesTypeFromAttributesProperty(node.parent as JsxOpeningLikeElement, /*filter*/ undefined, contextualMapper);
function checkJsxAttributes(node: JsxAttributes, checkMode?: CheckMode) {
return createJsxAttributesTypeFromAttributesProperty(node.parent as JsxOpeningLikeElement, /*filter*/ undefined, checkMode);
}
function getJsxType(name: string) {
@@ -12983,9 +13045,9 @@ namespace ts {
}
}
function checkJsxExpression(node: JsxExpression, contextualMapper?: TypeMapper) {
function checkJsxExpression(node: JsxExpression, checkMode?: CheckMode) {
if (node.expression) {
const type = checkExpression(node.expression, contextualMapper);
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));
}
@@ -14846,9 +14908,9 @@ namespace ts {
return signature.parameters.length > 0 ? getTypeAtPosition(signature, 0) : neverType;
}
function assignContextualParameterTypes(signature: Signature, context: Signature, mapper: TypeMapper) {
function assignContextualParameterTypes(signature: Signature, context: Signature, mapper: TypeMapper, checkMode: CheckMode) {
const len = signature.parameters.length - (signature.hasRestParameter ? 1 : 0);
if (isInferentialContext(mapper)) {
if (checkMode === CheckMode.Inferential) {
for (let i = 0; i < len; i++) {
const declaration = <ParameterDeclaration>signature.parameters[i].valueDeclaration;
if (declaration.type) {
@@ -14862,21 +14924,21 @@ namespace ts {
if (!parameter) {
signature.thisParameter = createSymbolWithType(context.thisParameter, undefined);
}
assignTypeToParameterAndFixTypeParameters(signature.thisParameter, getTypeOfSymbol(context.thisParameter), mapper);
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);
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);
assignTypeToParameterAndFixTypeParameters(parameter, contextualParameterType, mapper, checkMode);
}
}
}
@@ -14896,7 +14958,7 @@ namespace ts {
}
}
function assignTypeToParameterAndFixTypeParameters(parameter: Symbol, contextualType: Type, mapper: TypeMapper) {
function assignTypeToParameterAndFixTypeParameters(parameter: Symbol, contextualType: Type, mapper: TypeMapper, checkMode: CheckMode) {
const links = getSymbolLinks(parameter);
if (!links.type) {
links.type = instantiateType(contextualType, mapper);
@@ -14908,7 +14970,7 @@ namespace ts {
}
assignBindingElementTypes(<ParameterDeclaration>parameter.valueDeclaration);
}
else if (isInferentialContext(mapper)) {
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,
@@ -14976,7 +15038,7 @@ namespace ts {
return promiseType;
}
function getReturnTypeFromBody(func: FunctionLikeDeclaration, contextualMapper?: TypeMapper): Type {
function getReturnTypeFromBody(func: FunctionLikeDeclaration, checkMode?: CheckMode): Type {
const contextualSignature = getContextualSignatureForFunctionLikeDeclaration(func);
if (!func.body) {
return unknownType;
@@ -14985,7 +15047,7 @@ namespace ts {
const functionFlags = getFunctionFlags(func);
let type: Type;
if (func.body.kind !== SyntaxKind.Block) {
type = checkExpressionCached(<Expression>func.body, contextualMapper);
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
@@ -14997,7 +15059,7 @@ namespace ts {
else {
let types: Type[];
if (functionFlags & FunctionFlags.Generator) { // Generator or AsyncGenerator function
types = checkAndAggregateYieldOperandTypes(func, contextualMapper);
types = checkAndAggregateYieldOperandTypes(func, checkMode);
if (types.length === 0) {
const iterableIteratorAny = functionFlags & FunctionFlags.Async
? createAsyncIterableIteratorType(anyType) // AsyncGenerator function
@@ -15010,7 +15072,7 @@ namespace ts {
}
}
else {
types = checkAndAggregateReturnExpressionTypes(func, contextualMapper);
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
@@ -15054,13 +15116,13 @@ namespace ts {
: widenedType; // Generator function, AsyncGenerator function, or normal function
}
function checkAndAggregateYieldOperandTypes(func: FunctionLikeDeclaration, contextualMapper: TypeMapper): Type[] {
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, contextualMapper);
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);
@@ -15100,7 +15162,7 @@ namespace ts {
return true;
}
function checkAndAggregateReturnExpressionTypes(func: FunctionLikeDeclaration, contextualMapper: TypeMapper): Type[] {
function checkAndAggregateReturnExpressionTypes(func: FunctionLikeDeclaration, checkMode: CheckMode): Type[] {
const functionFlags = getFunctionFlags(func);
const aggregatedTypes: Type[] = [];
let hasReturnWithNoExpression = functionHasImplicitReturn(func);
@@ -15108,7 +15170,7 @@ namespace ts {
forEachReturnStatement(<Block>func.body, returnStatement => {
const expr = returnStatement.expression;
if (expr) {
let type = checkExpressionCached(expr, contextualMapper);
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
@@ -15195,7 +15257,7 @@ namespace ts {
}
}
function checkFunctionExpressionOrObjectLiteralMethod(node: FunctionExpression | MethodDeclaration, contextualMapper?: TypeMapper): Type {
function checkFunctionExpressionOrObjectLiteralMethod(node: FunctionExpression | MethodDeclaration, checkMode?: CheckMode): Type {
Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isObjectLiteralMethod(node));
// Grammar checking
@@ -15205,7 +15267,7 @@ namespace ts {
}
// The identityMapper object is used to indicate that function expressions are wildcards
if (contextualMapper === identityMapper && isContextSensitive(node)) {
if (checkMode === CheckMode.SkipContextSensitive && isContextSensitive(node)) {
checkNodeDeferred(node);
return anyFunctionType;
}
@@ -15213,7 +15275,7 @@ namespace ts {
const links = getNodeLinks(node);
const type = getTypeOfSymbol(node.symbol);
const contextSensitive = isContextSensitive(node);
const mightFixTypeParameters = contextSensitive && isInferentialContext(contextualMapper);
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
@@ -15229,10 +15291,10 @@ namespace ts {
if (contextualSignature) {
const signature = getSignaturesOfType(type, SignatureKind.Call)[0];
if (contextSensitive) {
assignContextualParameterTypes(signature, contextualSignature, contextualMapper || identityMapper);
assignContextualParameterTypes(signature, contextualSignature, getContextualMapper(node), checkMode);
}
if (mightFixTypeParameters || !node.type && !signature.resolvedReturnType) {
const returnType = getReturnTypeFromBody(node, contextualMapper);
const returnType = getReturnTypeFromBody(node, checkMode);
if (!signature.resolvedReturnType) {
signature.resolvedReturnType = returnType;
}
@@ -15608,7 +15670,7 @@ namespace ts {
}
}
function checkArrayLiteralAssignment(node: ArrayLiteralExpression, sourceType: Type, contextualMapper?: TypeMapper): Type {
function checkArrayLiteralAssignment(node: ArrayLiteralExpression, sourceType: Type, checkMode?: CheckMode): Type {
if (languageVersion < ScriptTarget.ES2015 && compilerOptions.downlevelIteration) {
checkExternalEmitHelpers(node, ExternalEmitHelpers.Read);
}
@@ -15619,13 +15681,13 @@ namespace ts {
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, contextualMapper);
checkArrayLiteralDestructuringElementAssignment(node, sourceType, i, elementType, checkMode);
}
return sourceType;
}
function checkArrayLiteralDestructuringElementAssignment(node: ArrayLiteralExpression, sourceType: Type,
elementIndex: number, elementType: Type, contextualMapper?: TypeMapper) {
elementIndex: number, elementType: Type, checkMode?: CheckMode) {
const elements = node.elements;
const element = elements[elementIndex];
if (element.kind !== SyntaxKind.OmittedExpression) {
@@ -15637,7 +15699,7 @@ namespace ts {
? getTypeOfPropertyOfType(sourceType, propName)
: elementType;
if (type) {
return checkDestructuringAssignment(element, type, contextualMapper);
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
@@ -15661,7 +15723,7 @@ namespace ts {
error((<BinaryExpression>restExpression).operatorToken, Diagnostics.A_rest_element_cannot_have_an_initializer);
}
else {
return checkDestructuringAssignment(restExpression, createArrayType(elementType), contextualMapper);
return checkDestructuringAssignment(restExpression, createArrayType(elementType), checkMode);
}
}
}
@@ -15669,7 +15731,7 @@ namespace ts {
return undefined;
}
function checkDestructuringAssignment(exprOrAssignment: Expression | ShorthandPropertyAssignment, sourceType: Type, contextualMapper?: TypeMapper): Type {
function checkDestructuringAssignment(exprOrAssignment: Expression | ShorthandPropertyAssignment, sourceType: Type, checkMode?: CheckMode): Type {
let target: Expression;
if (exprOrAssignment.kind === SyntaxKind.ShorthandPropertyAssignment) {
const prop = <ShorthandPropertyAssignment>exprOrAssignment;
@@ -15680,7 +15742,7 @@ namespace ts {
!(getFalsyFlags(checkExpression(prop.objectAssignmentInitializer)) & TypeFlags.Undefined)) {
sourceType = getTypeWithFacts(sourceType, TypeFacts.NEUndefined);
}
checkBinaryLikeExpression(prop.name, prop.equalsToken, prop.objectAssignmentInitializer, contextualMapper);
checkBinaryLikeExpression(prop.name, prop.equalsToken, prop.objectAssignmentInitializer, checkMode);
}
target = (<ShorthandPropertyAssignment>exprOrAssignment).name;
}
@@ -15689,20 +15751,20 @@ namespace ts {
}
if (target.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>target).operatorToken.kind === SyntaxKind.EqualsToken) {
checkBinaryExpression(<BinaryExpression>target, contextualMapper);
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, contextualMapper);
return checkArrayLiteralAssignment(<ArrayLiteralExpression>target, sourceType, checkMode);
}
return checkReferenceAssignment(target, sourceType, contextualMapper);
return checkReferenceAssignment(target, sourceType, checkMode);
}
function checkReferenceAssignment(target: Expression, sourceType: Type, contextualMapper?: TypeMapper): Type {
const targetType = checkExpression(target, contextualMapper);
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;
@@ -15790,17 +15852,17 @@ namespace ts {
getUnionType([type1, type2], /*subtypeReduction*/ true);
}
function checkBinaryExpression(node: BinaryExpression, contextualMapper?: TypeMapper) {
return checkBinaryLikeExpression(node.left, node.operatorToken, node.right, contextualMapper, node);
function checkBinaryExpression(node: BinaryExpression, checkMode?: CheckMode) {
return checkBinaryLikeExpression(node.left, node.operatorToken, node.right, checkMode, node);
}
function checkBinaryLikeExpression(left: Expression, operatorToken: Node, right: Expression, contextualMapper?: TypeMapper, errorNode?: 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, contextualMapper), contextualMapper);
return checkDestructuringAssignment(left, checkExpression(right, checkMode), checkMode);
}
let leftType = checkExpression(left, contextualMapper);
let rightType = checkExpression(right, contextualMapper);
let leftType = checkExpression(left, checkMode);
let rightType = checkExpression(right, checkMode);
switch (operator) {
case SyntaxKind.AsteriskToken:
case SyntaxKind.AsteriskAsteriskToken:
@@ -16067,10 +16129,10 @@ namespace ts {
return anyType;
}
function checkConditionalExpression(node: ConditionalExpression, contextualMapper?: TypeMapper): Type {
function checkConditionalExpression(node: ConditionalExpression, checkMode?: CheckMode): Type {
checkExpression(node.condition);
const type1 = checkExpression(node.whenTrue, contextualMapper);
const type2 = checkExpression(node.whenFalse, contextualMapper);
const type1 = checkExpression(node.whenTrue, checkMode);
const type2 = checkExpression(node.whenFalse, checkMode);
return getBestChoiceType(type1, type2);
}
@@ -16103,15 +16165,20 @@ namespace ts {
return stringType;
}
function checkExpressionWithContextualType(node: Expression, contextualType: Type, contextualMapper?: TypeMapper): Type {
function checkExpressionWithContextualType(node: Expression, contextualType: Type, contextualMapper: TypeMapper): Type {
const saveContextualType = node.contextualType;
const saveContextualMapper = node.contextualMapper;
node.contextualType = contextualType;
const result = checkExpression(node, contextualMapper);
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, contextualMapper?: TypeMapper): Type {
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
@@ -16119,7 +16186,7 @@ namespace ts {
// 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, contextualMapper);
links.resolvedType = checkExpression(node, checkMode);
flowLoopStart = saveFlowLoopStart;
}
return links.resolvedType;
@@ -16154,12 +16221,12 @@ namespace ts {
return false;
}
function checkExpressionForMutableLocation(node: Expression, contextualMapper?: TypeMapper): Type {
const type = checkExpression(node, contextualMapper);
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, contextualMapper?: TypeMapper): 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.
@@ -16167,10 +16234,10 @@ namespace ts {
checkComputedPropertyName(<ComputedPropertyName>node.name);
}
return checkExpressionForMutableLocation((<PropertyAssignment>node).initializer, contextualMapper);
return checkExpressionForMutableLocation((<PropertyAssignment>node).initializer, checkMode);
}
function checkObjectLiteralMethod(node: MethodDeclaration, contextualMapper?: TypeMapper): Type {
function checkObjectLiteralMethod(node: MethodDeclaration, checkMode?: CheckMode): Type {
// Grammar checking
checkGrammarMethod(node);
@@ -16181,19 +16248,19 @@ namespace ts {
checkComputedPropertyName(<ComputedPropertyName>node.name);
}
const uninstantiatedType = checkFunctionExpressionOrObjectLiteralMethod(node, contextualMapper);
return instantiateTypeWithSingleGenericCallSignature(node, uninstantiatedType, contextualMapper);
const uninstantiatedType = checkFunctionExpressionOrObjectLiteralMethod(node, checkMode);
return instantiateTypeWithSingleGenericCallSignature(node, uninstantiatedType, checkMode);
}
function instantiateTypeWithSingleGenericCallSignature(node: Expression | MethodDeclaration, type: Type, contextualMapper?: TypeMapper) {
if (isInferentialContext(contextualMapper)) {
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, contextualMapper));
return getOrCreateTypeFromSignature(instantiateSignatureInContextOf(signature, contextualSignature, getContextualMapper(node)));
}
}
}
@@ -16229,14 +16296,14 @@ namespace ts {
// 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, contextualMapper?: TypeMapper): Type {
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, contextualMapper);
type = instantiateTypeWithSingleGenericCallSignature(<Expression>node, uninstantiatedType, contextualMapper);
const uninstantiatedType = checkExpressionWorker(<Expression>node, checkMode);
type = instantiateTypeWithSingleGenericCallSignature(<Expression>node, uninstantiatedType, checkMode);
}
if (isConstEnumObjectType(type)) {
@@ -16256,7 +16323,7 @@ namespace ts {
return type;
}
function checkExpressionWorker(node: Expression, contextualMapper: TypeMapper): Type {
function checkExpressionWorker(node: Expression, checkMode: CheckMode): Type {
switch (node.kind) {
case SyntaxKind.Identifier:
return checkIdentifier(<Identifier>node);
@@ -16278,9 +16345,9 @@ namespace ts {
case SyntaxKind.RegularExpressionLiteral:
return globalRegExpType;
case SyntaxKind.ArrayLiteralExpression:
return checkArrayLiteral(<ArrayLiteralExpression>node, contextualMapper);
return checkArrayLiteral(<ArrayLiteralExpression>node, checkMode);
case SyntaxKind.ObjectLiteralExpression:
return checkObjectLiteral(<ObjectLiteralExpression>node, contextualMapper);
return checkObjectLiteral(<ObjectLiteralExpression>node, checkMode);
case SyntaxKind.PropertyAccessExpression:
return checkPropertyAccessExpression(<PropertyAccessExpression>node);
case SyntaxKind.ElementAccessExpression:
@@ -16291,12 +16358,12 @@ namespace ts {
case SyntaxKind.TaggedTemplateExpression:
return checkTaggedTemplateExpression(<TaggedTemplateExpression>node);
case SyntaxKind.ParenthesizedExpression:
return checkExpression((<ParenthesizedExpression>node).expression, contextualMapper);
return checkExpression((<ParenthesizedExpression>node).expression, checkMode);
case SyntaxKind.ClassExpression:
return checkClassExpression(<ClassExpression>node);
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
return checkFunctionExpressionOrObjectLiteralMethod(<FunctionExpression>node, contextualMapper);
return checkFunctionExpressionOrObjectLiteralMethod(<FunctionExpression>node, checkMode);
case SyntaxKind.TypeOfExpression:
return checkTypeOfExpression(<TypeOfExpression>node);
case SyntaxKind.TypeAssertionExpression:
@@ -16317,23 +16384,23 @@ namespace ts {
case SyntaxKind.PostfixUnaryExpression:
return checkPostfixUnaryExpression(<PostfixUnaryExpression>node);
case SyntaxKind.BinaryExpression:
return checkBinaryExpression(<BinaryExpression>node, contextualMapper);
return checkBinaryExpression(<BinaryExpression>node, checkMode);
case SyntaxKind.ConditionalExpression:
return checkConditionalExpression(<ConditionalExpression>node, contextualMapper);
return checkConditionalExpression(<ConditionalExpression>node, checkMode);
case SyntaxKind.SpreadElement:
return checkSpreadExpression(<SpreadElement>node, contextualMapper);
return checkSpreadExpression(<SpreadElement>node, checkMode);
case SyntaxKind.OmittedExpression:
return undefinedWideningType;
case SyntaxKind.YieldExpression:
return checkYieldExpression(<YieldExpression>node);
case SyntaxKind.JsxExpression:
return checkJsxExpression(<JsxExpression>node, contextualMapper);
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, contextualMapper);
return checkJsxAttributes(<JsxAttributes>node, checkMode);
case SyntaxKind.JsxOpeningElement:
Debug.fail("Shouldn't ever directly check a JsxOpeningElement");
}
@@ -16927,13 +16994,8 @@ namespace ts {
checkAllCodePathsInNonVoidFunctionReturnOrThrow(node, returnType);
}
}
if (node.parent.kind !== SyntaxKind.ObjectLiteralExpression) {
checkSourceElement(node.body);
registerForUnusedIdentifiersCheck(node);
}
else {
checkNodeDeferred(node);
}
checkSourceElement(node.body);
registerForUnusedIdentifiersCheck(node);
}
function checkAccessorDeclarationTypesIdentical(first: AccessorDeclaration, second: AccessorDeclaration, getAnnotatedType: (a: AccessorDeclaration) => Type, message: DiagnosticMessage) {
@@ -16944,11 +17006,6 @@ namespace ts {
}
}
function checkAccessorDeferred(node: AccessorDeclaration) {
checkSourceElement(node.body);
registerForUnusedIdentifiersCheck(node);
}
function checkMissingDeclaration(node: Node) {
checkDecorators(node);
}
@@ -20615,7 +20672,7 @@ namespace ts {
break;
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
checkAccessorDeferred(<AccessorDeclaration>node);
checkAccessorDeclaration(<AccessorDeclaration>node);
break;
case SyntaxKind.ClassExpression:
checkClassExpressionDeferred(<ClassExpression>node);
@@ -21972,9 +22029,9 @@ namespace ts {
anyArrayType = createArrayType(anyType);
autoArrayType = createArrayType(autoType);
const symbol = getGlobalSymbol("ReadonlyArray", SymbolFlags.Type, /*diagnostic*/ undefined);
globalReadonlyArrayType = symbol && <GenericType>getTypeOfGlobalSymbol(symbol, /*arity*/ 1);
globalReadonlyArrayType = <GenericType>getGlobalTypeOrUndefined("ReadonlyArray", /*arity*/ 1);
anyReadonlyArrayType = globalReadonlyArrayType ? createTypeFromGenericGlobalType(globalReadonlyArrayType, [anyType]) : anyArrayType;
globalThisType = <GenericType>getGlobalTypeOrUndefined("ThisType", /*arity*/ 1);
}
function checkExternalEmitHelpers(location: Node, helpers: ExternalEmitHelpers) {

View File

@@ -519,6 +519,8 @@
/* @internal */ localSymbol?: Symbol; // Local symbol declared by node (initialized by binding only for exported nodes)
/* @internal */ flowNode?: FlowNode; // Associated FlowNode (initialized by binding)
/* @internal */ emitNode?: EmitNode; // Associated EmitNode (initialized by transforms)
/* @internal */ contextualType?: Type; // Used to temporarily assign a contextual type during overload resolution
/* @internal */ contextualMapper?: TypeMapper; // Mapper for contextual type
}
export interface NodeArray<T extends Node> extends Array<T>, TextRange {
@@ -963,7 +965,6 @@
export interface Expression extends Node {
_expressionBrand: any;
contextualType?: Type; // Used to temporarily assign a contextual type during overload resolution
}
export interface OmittedExpression extends Expression {

11
src/lib/es5.d.ts vendored
View File

@@ -147,7 +147,7 @@ interface ObjectConstructor {
* @param o Object to use as a prototype. May be null
* @param properties JavaScript object that contains one or more property descriptors.
*/
create(o: object | null, properties: PropertyDescriptorMap): any;
create(o: object | null, properties: PropertyDescriptorMap & ThisType<any>): any;
/**
* Adds a property to an object, or modifies attributes of an existing property.
@@ -155,14 +155,14 @@ interface ObjectConstructor {
* @param p The property name.
* @param attributes Descriptor for the property. It can be for a data property or an accessor property.
*/
defineProperty(o: any, p: string, attributes: PropertyDescriptor): any;
defineProperty(o: any, p: string, attributes: PropertyDescriptor & ThisType<any>): any;
/**
* Adds one or more properties to an object, and/or modifies attributes of existing properties.
* @param o Object on which to add or modify the properties. This can be a native JavaScript object or a DOM object.
* @param properties JavaScript object that contains one or more descriptor objects. Each descriptor object describes a data property or an accessor property.
*/
defineProperties(o: any, properties: PropertyDescriptorMap): any;
defineProperties(o: any, properties: PropertyDescriptorMap & ThisType<any>): any;
/**
* Prevents the modification of attributes of existing properties, and prevents the addition of new properties.
@@ -1366,6 +1366,11 @@ type Record<K extends string, T> = {
[P in K]: T;
}
/**
* Marker for contextual 'this' type
*/
interface ThisType<T> { }
/**
* Represents a raw buffer of binary data, which is used to store data for the
* different typed arrays. ArrayBuffers cannot be read from or written to directly,