Skip to content

Commit 8cd6c5d

Browse files
committed
Introduce ThisType<T> marker interface
1 parent f6a3a3f commit 8cd6c5d

2 files changed

Lines changed: 46 additions & 12 deletions

File tree

src/compiler/checker.ts

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ namespace ts {
197197
let globalNumberType: ObjectType;
198198
let globalBooleanType: ObjectType;
199199
let globalRegExpType: ObjectType;
200+
let globalThisType: GenericType;
200201
let anyArrayType: Type;
201202
let autoArrayType: Type;
202203
let anyReadonlyArrayType: Type;
@@ -5802,6 +5803,11 @@ namespace ts {
58025803
return getTypeOfGlobalSymbol(getGlobalTypeSymbol(name), arity);
58035804
}
58045805

5806+
function getGlobalTypeOrUndefined(name: string, arity = 0): ObjectType {
5807+
const symbol = getGlobalSymbol(name, SymbolFlags.Type, /*diagnostic*/ undefined);
5808+
return symbol && <GenericType>getTypeOfGlobalSymbol(symbol, arity);
5809+
}
5810+
58055811
/**
58065812
* Returns a type that is inside a namespace at the global scope, e.g.
58075813
* getExportedTypeFromNamespace('JSX', 'Element') returns the JSX.Element type
@@ -11180,6 +11186,22 @@ namespace ts {
1118011186
}
1118111187
}
1118211188

11189+
function getContainingObjectLiteral(func: FunctionLikeDeclaration) {
11190+
return func.kind === SyntaxKind.MethodDeclaration && func.parent.kind === SyntaxKind.ObjectLiteralExpression ? <ObjectLiteralExpression>func.parent :
11191+
func.kind === SyntaxKind.FunctionExpression && func.parent.kind === SyntaxKind.PropertyAssignment ? <ObjectLiteralExpression>func.parent.parent :
11192+
undefined;
11193+
}
11194+
11195+
function getThisTypeArgument(type: Type): Type {
11196+
return getObjectFlags(type) & ObjectFlags.Reference && (<TypeReference>type).target === globalThisType ? (<TypeReference>type).typeArguments[0] : undefined;
11197+
}
11198+
11199+
function getThisTypeFromContextualType(type: Type): Type {
11200+
return applyToContextualType(type, t => {
11201+
return t.flags & TypeFlags.Intersection ? forEach((<IntersectionType>t).types, getThisTypeArgument) : getThisTypeArgument(t);
11202+
});
11203+
}
11204+
1118311205
function getContextualThisParameterType(func: FunctionLikeDeclaration): Type {
1118411206
if (isContextSensitiveFunctionOrObjectLiteralMethod(func) && func.kind !== SyntaxKind.ArrowFunction) {
1118511207
const contextualSignature = getContextualSignature(func);
@@ -11189,17 +11211,24 @@ namespace ts {
1118911211
return getTypeOfSymbol(thisParameter);
1119011212
}
1119111213
}
11192-
if (isObjectLiteralMethod(func)) {
11193-
// For methods in an object literal, look for a '__this__' property in the contextual
11194-
// type for the object literal. If one exists, remove 'undefined' from the type of that
11195-
// property and report it as the contextual type for 'this' in the method.
11196-
const objectLiteral = <ObjectLiteralExpression>func.parent;
11197-
const type = getApparentTypeOfContextualType(objectLiteral);
11198-
if (type) {
11199-
const propertyType = getTypeOfPropertyOfContextualType(type, "___this__");
11200-
if (propertyType) {
11201-
return getNonNullableType(propertyType);
11214+
const containingLiteral = getContainingObjectLiteral(func);
11215+
if (containingLiteral) {
11216+
// We have an object literal method. Check if the containing object literal has a contextual type
11217+
// and if that contextual type is or includes a ThisType<T>. If so, T is the contextual type for
11218+
// 'this'. We continue looking in any directly enclosing object literals.
11219+
let objectLiteral = containingLiteral;
11220+
while (true) {
11221+
const type = getApparentTypeOfContextualType(objectLiteral);
11222+
if (type) {
11223+
const thisType = getThisTypeFromContextualType(type);
11224+
if (thisType) {
11225+
return thisType;
11226+
}
11227+
}
11228+
if (objectLiteral.parent.kind !== SyntaxKind.PropertyAssignment) {
11229+
break;
1120211230
}
11231+
objectLiteral = <ObjectLiteralExpression>objectLiteral.parent.parent;
1120311232
}
1120411233
}
1120511234
}
@@ -21175,9 +21204,9 @@ namespace ts {
2117521204
anyArrayType = createArrayType(anyType);
2117621205
autoArrayType = createArrayType(autoType);
2117721206

21178-
const symbol = getGlobalSymbol("ReadonlyArray", SymbolFlags.Type, /*diagnostic*/ undefined);
21179-
globalReadonlyArrayType = symbol && <GenericType>getTypeOfGlobalSymbol(symbol, /*arity*/ 1);
21207+
globalReadonlyArrayType = <GenericType>getGlobalTypeOrUndefined("ReadonlyArray", /*arity*/ 1);
2118021208
anyReadonlyArrayType = globalReadonlyArrayType ? createTypeFromGenericGlobalType(globalReadonlyArrayType, [anyType]) : anyArrayType;
21209+
globalThisType = <GenericType>getGlobalTypeOrUndefined("ThisType", /*arity*/ 1);
2118121210
}
2118221211

2118321212
function checkExternalEmitHelpers(location: Node, helpers: ExternalEmitHelpers) {

src/lib/es5.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1384,6 +1384,11 @@ type Record<K extends string, T> = {
13841384
[P in K]: T;
13851385
}
13861386

1387+
/**
1388+
* Marker for contextual 'this' type
1389+
*/
1390+
interface ThisType<T> { }
1391+
13871392
/**
13881393
* Represents a raw buffer of binary data, which is used to store data for the
13891394
* different typed arrays. ArrayBuffers cannot be read from or written to directly,

0 commit comments

Comments
 (0)