diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1f59d28cc7c..898b7b4d539 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -197,6 +197,7 @@ namespace ts { let globalNumberType: ObjectType; let globalBooleanType: ObjectType; let globalRegExpType: ObjectType; + let globalThisType: GenericType; let anyArrayType: Type; let autoArrayType: Type; let anyReadonlyArrayType: Type; @@ -5802,6 +5803,11 @@ namespace ts { return getTypeOfGlobalSymbol(getGlobalTypeSymbol(name), arity); } + function getGlobalTypeOrUndefined(name: string, arity = 0): ObjectType { + const symbol = getGlobalSymbol(name, SymbolFlags.Type, /*diagnostic*/ undefined); + return symbol && 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 @@ -11180,6 +11186,22 @@ namespace ts { } } + function getContainingObjectLiteral(func: FunctionLikeDeclaration) { + return func.kind === SyntaxKind.MethodDeclaration && func.parent.kind === SyntaxKind.ObjectLiteralExpression ? func.parent : + func.kind === SyntaxKind.FunctionExpression && func.parent.kind === SyntaxKind.PropertyAssignment ? func.parent.parent : + undefined; + } + + function getThisTypeArgument(type: Type): Type { + return getObjectFlags(type) & ObjectFlags.Reference && (type).target === globalThisType ? (type).typeArguments[0] : undefined; + } + + function getThisTypeFromContextualType(type: Type): Type { + return applyToContextualType(type, t => { + return t.flags & TypeFlags.Intersection ? forEach((t).types, getThisTypeArgument) : getThisTypeArgument(t); + }); + } + function getContextualThisParameterType(func: FunctionLikeDeclaration): Type { if (isContextSensitiveFunctionOrObjectLiteralMethod(func) && func.kind !== SyntaxKind.ArrowFunction) { const contextualSignature = getContextualSignature(func); @@ -11189,17 +11211,24 @@ namespace ts { return getTypeOfSymbol(thisParameter); } } - if (isObjectLiteralMethod(func)) { - // For methods in an object literal, look for a '__this__' property in the contextual - // type for the object literal. If one exists, remove 'undefined' from the type of that - // property and report it as the contextual type for 'this' in the method. - const objectLiteral = func.parent; - const type = getApparentTypeOfContextualType(objectLiteral); - if (type) { - const propertyType = getTypeOfPropertyOfContextualType(type, "___this__"); - if (propertyType) { - return getNonNullableType(propertyType); + const containingLiteral = getContainingObjectLiteral(func); + if (containingLiteral) { + // We have an object literal method. Check if the containing object literal has a contextual type + // and if that contextual type is or includes a ThisType. If so, T is the contextual type for + // 'this'. We continue looking in any directly enclosing object literals. + let objectLiteral = containingLiteral; + while (true) { + const type = getApparentTypeOfContextualType(objectLiteral); + if (type) { + const thisType = getThisTypeFromContextualType(type); + if (thisType) { + return thisType; + } } + if (objectLiteral.parent.kind !== SyntaxKind.PropertyAssignment) { + break; + } + objectLiteral = objectLiteral.parent.parent; } } } @@ -21175,9 +21204,9 @@ namespace ts { anyArrayType = createArrayType(anyType); autoArrayType = createArrayType(autoType); - const symbol = getGlobalSymbol("ReadonlyArray", SymbolFlags.Type, /*diagnostic*/ undefined); - globalReadonlyArrayType = symbol && getTypeOfGlobalSymbol(symbol, /*arity*/ 1); + globalReadonlyArrayType = getGlobalTypeOrUndefined("ReadonlyArray", /*arity*/ 1); anyReadonlyArrayType = globalReadonlyArrayType ? createTypeFromGenericGlobalType(globalReadonlyArrayType, [anyType]) : anyArrayType; + globalThisType = getGlobalTypeOrUndefined("ThisType", /*arity*/ 1); } function checkExternalEmitHelpers(location: Node, helpers: ExternalEmitHelpers) { diff --git a/src/lib/es5.d.ts b/src/lib/es5.d.ts index 038d0344fda..6ff9b8f533a 100644 --- a/src/lib/es5.d.ts +++ b/src/lib/es5.d.ts @@ -1384,6 +1384,11 @@ type Record = { [P in K]: T; } +/** + * Marker for contextual 'this' type + */ +interface ThisType { } + /** * 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,