Skip to content

Commit cbe2f3d

Browse files
committed
Merge pull request #4910 from Microsoft/polymorphicThisType
Polymorphic 'this' type
2 parents a4db4be + 82c010e commit cbe2f3d

File tree

235 files changed

+4259
-2160
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

235 files changed

+4259
-2160
lines changed

Diff for: src/compiler/binder.ts

+12-1
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ namespace ts {
8888
let container: Node;
8989
let blockScopeContainer: Node;
9090
let lastContainer: Node;
91+
let seenThisKeyword: boolean;
9192

9293
// If this file is an external module, then it is automatically in strict-mode according to
9394
// ES6. If it is not an external module, then we'll determine if it is in strict mode or
@@ -329,7 +330,14 @@ namespace ts {
329330
blockScopeContainer.locals = undefined;
330331
}
331332

332-
forEachChild(node, bind);
333+
if (node.kind === SyntaxKind.InterfaceDeclaration) {
334+
seenThisKeyword = false;
335+
forEachChild(node, bind);
336+
node.flags = seenThisKeyword ? node.flags | NodeFlags.ContainsThis : node.flags & ~NodeFlags.ContainsThis;
337+
}
338+
else {
339+
forEachChild(node, bind);
340+
}
333341

334342
container = saveContainer;
335343
parent = saveParent;
@@ -851,6 +859,9 @@ namespace ts {
851859
return checkStrictModePrefixUnaryExpression(<PrefixUnaryExpression>node);
852860
case SyntaxKind.WithStatement:
853861
return checkStrictModeWithStatement(<WithStatement>node);
862+
case SyntaxKind.ThisKeyword:
863+
seenThisKeyword = true;
864+
return;
854865

855866
case SyntaxKind.TypeParameter:
856867
return declareSymbolAndAddToSymbolTable(<Declaration>node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes);

Diff for: src/compiler/checker.ts

+259-90
Large diffs are not rendered by default.

Diff for: src/compiler/declarationEmitter.ts

+14
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ namespace ts {
5252
let enclosingDeclaration: Node;
5353
let currentSourceFile: SourceFile;
5454
let reportedDeclarationError = false;
55+
let errorNameNode: DeclarationName;
5556
let emitJsDocComments = compilerOptions.removeComments ? function (declaration: Node) { } : writeJsDocComments;
5657
let emit = compilerOptions.stripInternal ? stripInternal : emitNode;
5758

@@ -152,6 +153,7 @@ namespace ts {
152153
function createAndSetNewTextWriterWithSymbolWriter(): EmitTextWriterWithSymbolWriter {
153154
let writer = <EmitTextWriterWithSymbolWriter>createTextWriter(newLine);
154155
writer.trackSymbol = trackSymbol;
156+
writer.reportInaccessibleThisError = reportInaccessibleThisError;
155157
writer.writeKeyword = writer.write;
156158
writer.writeOperator = writer.write;
157159
writer.writePunctuation = writer.write;
@@ -257,6 +259,13 @@ namespace ts {
257259
handleSymbolAccessibilityError(resolver.isSymbolAccessible(symbol, enclosingDeclaration, meaning));
258260
}
259261

262+
function reportInaccessibleThisError() {
263+
if (errorNameNode) {
264+
diagnostics.push(createDiagnosticForNode(errorNameNode, Diagnostics.The_inferred_type_of_0_references_an_inaccessible_this_type_A_type_annotation_is_necessary,
265+
declarationNameToString(errorNameNode)));
266+
}
267+
}
268+
260269
function writeTypeOfDeclaration(declaration: AccessorDeclaration | VariableLikeDeclaration, type: TypeNode, getSymbolAccessibilityDiagnostic: GetSymbolAccessibilityDiagnostic) {
261270
writer.getSymbolAccessibilityDiagnostic = getSymbolAccessibilityDiagnostic;
262271
write(": ");
@@ -265,7 +274,9 @@ namespace ts {
265274
emitType(type);
266275
}
267276
else {
277+
errorNameNode = declaration.name;
268278
resolver.writeTypeOfDeclaration(declaration, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction, writer);
279+
errorNameNode = undefined;
269280
}
270281
}
271282

@@ -277,7 +288,9 @@ namespace ts {
277288
emitType(signature.type);
278289
}
279290
else {
291+
errorNameNode = signature.name;
280292
resolver.writeReturnTypeOfSignatureDeclaration(signature, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction, writer);
293+
errorNameNode = undefined;
281294
}
282295
}
283296

@@ -326,6 +339,7 @@ namespace ts {
326339
case SyntaxKind.BooleanKeyword:
327340
case SyntaxKind.SymbolKeyword:
328341
case SyntaxKind.VoidKeyword:
342+
case SyntaxKind.ThisKeyword:
329343
case SyntaxKind.StringLiteral:
330344
return writeTextOfNode(currentSourceFile, type);
331345
case SyntaxKind.ExpressionWithTypeArguments:

Diff for: src/compiler/diagnosticMessages.json

+8
Original file line numberDiff line numberDiff line change
@@ -1652,6 +1652,14 @@
16521652
"category": "Error",
16531653
"code": 2525
16541654
},
1655+
"A 'this' type is available only in a non-static member of a class or interface.": {
1656+
"category": "Error",
1657+
"code": 2526
1658+
},
1659+
"The inferred type of '{0}' references an inaccessible 'this' type. A type annotation is necessary.": {
1660+
"category": "Error",
1661+
"code": 2527
1662+
},
16551663
"JSX element attributes type '{0}' must be an object type.": {
16561664
"category": "Error",
16571665
"code": 2600

Diff for: src/compiler/parser.ts

+2
Original file line numberDiff line numberDiff line change
@@ -2360,6 +2360,7 @@ namespace ts {
23602360
let node = tryParse(parseKeywordAndNoDot);
23612361
return node || parseTypeReferenceOrTypePredicate();
23622362
case SyntaxKind.VoidKeyword:
2363+
case SyntaxKind.ThisKeyword:
23632364
return parseTokenNode<TypeNode>();
23642365
case SyntaxKind.TypeOfKeyword:
23652366
return parseTypeQuery();
@@ -2382,6 +2383,7 @@ namespace ts {
23822383
case SyntaxKind.BooleanKeyword:
23832384
case SyntaxKind.SymbolKeyword:
23842385
case SyntaxKind.VoidKeyword:
2386+
case SyntaxKind.ThisKeyword:
23852387
case SyntaxKind.TypeOfKeyword:
23862388
case SyntaxKind.OpenBraceToken:
23872389
case SyntaxKind.OpenBracketToken:

Diff for: src/compiler/types.ts

+13-2
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,7 @@ namespace ts {
376376
OctalLiteral = 0x00010000, // Octal numeric literal
377377
Namespace = 0x00020000, // Namespace declaration
378378
ExportContext = 0x00040000, // Export context (initialized by binding)
379+
ContainsThis = 0x00080000, // Interface contains references to "this"
379380

380381
Modifier = Export | Ambient | Public | Private | Protected | Static | Abstract | Default | Async,
381382
AccessibilityModifier = Public | Private | Protected,
@@ -1496,6 +1497,7 @@ namespace ts {
14961497
// declaration emitter to help determine if it should patch up the final declaration file
14971498
// with import statements it previously saw (but chose not to emit).
14981499
trackSymbol(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): void;
1500+
reportInaccessibleThisError(): void;
14991501
}
15001502

15011503
export const enum TypeFormatFlags {
@@ -1797,6 +1799,7 @@ namespace ts {
17971799
/* @internal */
17981800
ContainsAnyFunctionType = 0x00800000, // Type is or contains object literal type
17991801
ESSymbol = 0x01000000, // Type of symbol primitive introduced in ES6
1802+
ThisType = 0x02000000, // This type
18001803

18011804
/* @internal */
18021805
Intrinsic = Any | String | Number | Boolean | ESSymbol | Void | Undefined | Null,
@@ -1842,6 +1845,7 @@ namespace ts {
18421845
typeParameters: TypeParameter[]; // Type parameters (undefined if non-generic)
18431846
outerTypeParameters: TypeParameter[]; // Outer type parameters (undefined if none)
18441847
localTypeParameters: TypeParameter[]; // Local type parameters (undefined if none)
1848+
thisType: TypeParameter; // The "this" type (undefined if none)
18451849
/* @internal */
18461850
resolvedBaseConstructorType?: Type; // Resolved base constructor type of class
18471851
/* @internal */
@@ -1856,10 +1860,17 @@ namespace ts {
18561860
declaredNumberIndexType: Type; // Declared numeric index type
18571861
}
18581862

1859-
// Type references (TypeFlags.Reference)
1863+
// Type references (TypeFlags.Reference). When a class or interface has type parameters or
1864+
// a "this" type, references to the class or interface are made using type references. The
1865+
// typeArguments property specififes the types to substitute for the type parameters of the
1866+
// class or interface and optionally includes an extra element that specifies the type to
1867+
// substitute for "this" in the resulting instantiation. When no extra argument is present,
1868+
// the type reference itself is substituted for "this". The typeArguments property is undefined
1869+
// if the class or interface has no type parameters and the reference isn't specifying an
1870+
// explicit "this" argument.
18601871
export interface TypeReference extends ObjectType {
18611872
target: GenericType; // Type reference target
1862-
typeArguments: Type[]; // Type reference type arguments
1873+
typeArguments: Type[]; // Type reference type arguments (undefined if none)
18631874
}
18641875

18651876
// Generic class and interface types

Diff for: src/compiler/utilities.ts

+10-6
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ namespace ts {
6565
increaseIndent: () => { },
6666
decreaseIndent: () => { },
6767
clear: () => str = "",
68-
trackSymbol: () => { }
68+
trackSymbol: () => { },
69+
reportInaccessibleThisError: () => { }
6970
};
7071
}
7172

@@ -468,13 +469,12 @@ namespace ts {
468469
else if (node.parent.kind === SyntaxKind.PropertyAccessExpression && (<PropertyAccessExpression>node.parent).name === node) {
469470
node = node.parent;
470471
}
471-
// fall through
472-
case SyntaxKind.QualifiedName:
473-
case SyntaxKind.PropertyAccessExpression:
474472
// At this point, node is either a qualified name or an identifier
475473
Debug.assert(node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.QualifiedName || node.kind === SyntaxKind.PropertyAccessExpression,
476474
"'node' was expected to be a qualified name, identifier or property access in 'isTypeNode'.");
477-
475+
case SyntaxKind.QualifiedName:
476+
case SyntaxKind.PropertyAccessExpression:
477+
case SyntaxKind.ThisKeyword:
478478
let parent = node.parent;
479479
if (parent.kind === SyntaxKind.TypeQuery) {
480480
return false;
@@ -733,6 +733,9 @@ namespace ts {
733733
case SyntaxKind.Constructor:
734734
case SyntaxKind.GetAccessor:
735735
case SyntaxKind.SetAccessor:
736+
case SyntaxKind.CallSignature:
737+
case SyntaxKind.ConstructSignature:
738+
case SyntaxKind.IndexSignature:
736739
case SyntaxKind.EnumDeclaration:
737740
case SyntaxKind.SourceFile:
738741
return node;
@@ -895,7 +898,6 @@ namespace ts {
895898

896899
export function isExpression(node: Node): boolean {
897900
switch (node.kind) {
898-
case SyntaxKind.ThisKeyword:
899901
case SyntaxKind.SuperKeyword:
900902
case SyntaxKind.NullKeyword:
901903
case SyntaxKind.TrueKeyword:
@@ -928,6 +930,7 @@ namespace ts {
928930
case SyntaxKind.JsxElement:
929931
case SyntaxKind.JsxSelfClosingElement:
930932
case SyntaxKind.YieldExpression:
933+
case SyntaxKind.AwaitExpression:
931934
return true;
932935
case SyntaxKind.QualifiedName:
933936
while (node.parent.kind === SyntaxKind.QualifiedName) {
@@ -941,6 +944,7 @@ namespace ts {
941944
// fall through
942945
case SyntaxKind.NumericLiteral:
943946
case SyntaxKind.StringLiteral:
947+
case SyntaxKind.ThisKeyword:
944948
let parent = node.parent;
945949
switch (parent.kind) {
946950
case SyntaxKind.VariableDeclaration:

Diff for: src/services/services.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -725,7 +725,7 @@ namespace ts {
725725
}
726726
getBaseTypes(): ObjectType[] {
727727
return this.flags & (TypeFlags.Class | TypeFlags.Interface)
728-
? this.checker.getBaseTypes(<TypeObject & InterfaceType>this)
728+
? this.checker.getBaseTypes(<InterfaceType><Type>this)
729729
: undefined;
730730
}
731731
}
@@ -6252,7 +6252,8 @@ namespace ts {
62526252
}
62536253

62546254
return node.parent.kind === SyntaxKind.TypeReference ||
6255-
(node.parent.kind === SyntaxKind.ExpressionWithTypeArguments && !isExpressionWithTypeArgumentsInClassExtendsClause(<ExpressionWithTypeArguments>node.parent));
6255+
(node.parent.kind === SyntaxKind.ExpressionWithTypeArguments && !isExpressionWithTypeArgumentsInClassExtendsClause(<ExpressionWithTypeArguments>node.parent)) ||
6256+
node.kind === SyntaxKind.ThisKeyword && !isExpression(node);
62566257
}
62576258

62586259
function isNamespaceReference(node: Node): boolean {

Diff for: src/services/utilities.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -636,7 +636,8 @@ namespace ts {
636636
increaseIndent: () => { indent++; },
637637
decreaseIndent: () => { indent--; },
638638
clear: resetWriter,
639-
trackSymbol: () => { }
639+
trackSymbol: () => { },
640+
reportInaccessibleThisError: () => { }
640641
};
641642

642643
function writeIndent() {

Diff for: tests/baselines/reference/2dArrays.types

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class Board {
2828
>this.ships.every(function (val) { return val.isSunk; }) : boolean
2929
>this.ships.every : (callbackfn: (value: Ship, index: number, array: Ship[]) => boolean, thisArg?: any) => boolean
3030
>this.ships : Ship[]
31-
>this : Board
31+
>this : this
3232
>ships : Ship[]
3333
>every : (callbackfn: (value: Ship, index: number, array: Ship[]) => boolean, thisArg?: any) => boolean
3434
>function (val) { return val.isSunk; } : (val: Ship) => boolean

Diff for: tests/baselines/reference/accessOverriddenBaseClassMember1.types

+3-3
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ class Point {
1515
>"x=" + this.x : string
1616
>"x=" : string
1717
>this.x : number
18-
>this : Point
18+
>this : this
1919
>x : number
2020
>" y=" : string
2121
>this.y : number
22-
>this : Point
22+
>this : this
2323
>y : number
2424
}
2525
}
@@ -50,7 +50,7 @@ class ColoredPoint extends Point {
5050
>toString : () => string
5151
>" color=" : string
5252
>this.color : string
53-
>this : ColoredPoint
53+
>this : this
5454
>color : string
5555
}
5656
}

Diff for: tests/baselines/reference/aliasUsageInAccessorsOfClass.types

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class C2 {
2626

2727
return this.x;
2828
>this.x : IHasVisualizationModel
29-
>this : C2
29+
>this : this
3030
>x : IHasVisualizationModel
3131
}
3232
set A(x) {

Diff for: tests/baselines/reference/ambiguousCallsWhereReturnTypesAgree.types

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class TestClass {
3131
this.bar(x); // should not error
3232
>this.bar(x) : void
3333
>this.bar : { (x: string): void; (x: string[]): void; }
34-
>this : TestClass
34+
>this : this
3535
>bar : { (x: string): void; (x: string[]): void; }
3636
>x : any
3737
}
@@ -71,7 +71,7 @@ class TestClass2 {
7171
return this.bar(x); // should not error
7272
>this.bar(x) : number
7373
>this.bar : { (x: string): number; (x: string[]): number; }
74-
>this : TestClass2
74+
>this : this
7575
>bar : { (x: string): number; (x: string[]): number; }
7676
>x : any
7777
}

Diff for: tests/baselines/reference/amdModuleName1.types

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class Foo {
1010
this.x = 5;
1111
>this.x = 5 : number
1212
>this.x : number
13-
>this : Foo
13+
>this : this
1414
>x : number
1515
>5 : number
1616
}

Diff for: tests/baselines/reference/arityAndOrderCompatibility01.errors.txt

+4
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(25,5): error
3434
tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(26,5): error TS2322: Type 'StrNum' is not assignable to type '[string]'.
3535
Types of property 'pop' are incompatible.
3636
Type '() => string | number' is not assignable to type '() => string'.
37+
Type 'string | number' is not assignable to type 'string'.
38+
Type 'number' is not assignable to type 'string'.
3739
tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(27,5): error TS2322: Type '{ 0: string; 1: number; }' is not assignable to type '[string]'.
3840
Property 'length' is missing in type '{ 0: string; 1: number; }'.
3941
tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(28,5): error TS2322: Type '[string, number]' is not assignable to type '[number, string]'.
@@ -125,6 +127,8 @@ tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(30,5): error
125127
!!! error TS2322: Type 'StrNum' is not assignable to type '[string]'.
126128
!!! error TS2322: Types of property 'pop' are incompatible.
127129
!!! error TS2322: Type '() => string | number' is not assignable to type '() => string'.
130+
!!! error TS2322: Type 'string | number' is not assignable to type 'string'.
131+
!!! error TS2322: Type 'number' is not assignable to type 'string'.
128132
var m3: [string] = z;
129133
~~
130134
!!! error TS2322: Type '{ 0: string; 1: number; }' is not assignable to type '[string]'.

0 commit comments

Comments
 (0)