Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Polymorphic 'this' type #4910

Merged
merged 21 commits into from
Sep 30, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
9438a4b
Polymorphic "this" type
ahejlsberg Sep 21, 2015
89ea067
Accepting new baselines
ahejlsberg Sep 21, 2015
06a250e
Commenting out broken fourslash tests
ahejlsberg Sep 21, 2015
285483d
Merge branch 'master' into polymorphicThisType
ahejlsberg Sep 22, 2015
7acb9dd
Adding comments and addressing CR feedback
ahejlsberg Sep 22, 2015
8fd2d7a
Properly emit "this" type in declaration files
ahejlsberg Sep 25, 2015
9dee875
Add additional "this" containers
ahejlsberg Sep 25, 2015
d79f5a6
Removing a few "this" containers
ahejlsberg Sep 25, 2015
41f8aad
Write "this" as "any" when "this" reference would be an error
ahejlsberg Sep 25, 2015
31eebbf
Rewrite inaccessible "this" to containing class/interface in declarat…
ahejlsberg Sep 25, 2015
f17875e
Properly classify "this" and "await" in isExpression
ahejlsberg Sep 26, 2015
9594835
Accepting new baselines
ahejlsberg Sep 26, 2015
47c9190
Proper handling of "this" in getSymbolAtLocation
ahejlsberg Sep 26, 2015
abd2a85
Adding tests
ahejlsberg Sep 26, 2015
29f6036
Fixing comment and error message per CR feedback
ahejlsberg Sep 26, 2015
19319b2
Adding test for declaration files
ahejlsberg Sep 26, 2015
5dc8402
Make tuple type itself the 'this' type of base array type
ahejlsberg Sep 27, 2015
1c9fae8
Add tuple type test
ahejlsberg Sep 27, 2015
24f906a
Error when emitted type in declaration file references inaccessible '…
ahejlsberg Sep 29, 2015
81934ab
Accepting new baselines
ahejlsberg Sep 29, 2015
82c010e
Adding contextual typing test
ahejlsberg Sep 29, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ namespace ts {
let container: Node;
let blockScopeContainer: Node;
let lastContainer: Node;
let seenThisKeyword: boolean;

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

forEachChild(node, bind);
if (node.kind === SyntaxKind.InterfaceDeclaration) {
seenThisKeyword = false;
forEachChild(node, bind);
node.flags = seenThisKeyword ? node.flags | NodeFlags.ContainsThis : node.flags & ~NodeFlags.ContainsThis;
}
else {
forEachChild(node, bind);
}

container = saveContainer;
parent = saveParent;
Expand Down Expand Up @@ -851,6 +859,9 @@ namespace ts {
return checkStrictModePrefixUnaryExpression(<PrefixUnaryExpression>node);
case SyntaxKind.WithStatement:
return checkStrictModeWithStatement(<WithStatement>node);
case SyntaxKind.ThisKeyword:
seenThisKeyword = true;
return;

case SyntaxKind.TypeParameter:
return declareSymbolAndAddToSymbolTable(<Declaration>node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes);
Expand Down
349 changes: 259 additions & 90 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

14 changes: 14 additions & 0 deletions src/compiler/declarationEmitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ namespace ts {
let enclosingDeclaration: Node;
let currentSourceFile: SourceFile;
let reportedDeclarationError = false;
let errorNameNode: DeclarationName;
let emitJsDocComments = compilerOptions.removeComments ? function (declaration: Node) { } : writeJsDocComments;
let emit = compilerOptions.stripInternal ? stripInternal : emitNode;

Expand Down Expand Up @@ -152,6 +153,7 @@ namespace ts {
function createAndSetNewTextWriterWithSymbolWriter(): EmitTextWriterWithSymbolWriter {
let writer = <EmitTextWriterWithSymbolWriter>createTextWriter(newLine);
writer.trackSymbol = trackSymbol;
writer.reportInaccessibleThisError = reportInaccessibleThisError;
writer.writeKeyword = writer.write;
writer.writeOperator = writer.write;
writer.writePunctuation = writer.write;
Expand Down Expand Up @@ -257,6 +259,13 @@ namespace ts {
handleSymbolAccessibilityError(resolver.isSymbolAccessible(symbol, enclosingDeclaration, meaning));
}

function reportInaccessibleThisError() {
if (errorNameNode) {
diagnostics.push(createDiagnosticForNode(errorNameNode, Diagnostics.The_inferred_type_of_0_references_an_inaccessible_this_type_A_type_annotation_is_necessary,
declarationNameToString(errorNameNode)));
}
}

function writeTypeOfDeclaration(declaration: AccessorDeclaration | VariableLikeDeclaration, type: TypeNode, getSymbolAccessibilityDiagnostic: GetSymbolAccessibilityDiagnostic) {
writer.getSymbolAccessibilityDiagnostic = getSymbolAccessibilityDiagnostic;
write(": ");
Expand All @@ -265,7 +274,9 @@ namespace ts {
emitType(type);
}
else {
errorNameNode = declaration.name;
resolver.writeTypeOfDeclaration(declaration, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction, writer);
errorNameNode = undefined;
}
}

Expand All @@ -277,7 +288,9 @@ namespace ts {
emitType(signature.type);
}
else {
errorNameNode = signature.name;
resolver.writeReturnTypeOfSignatureDeclaration(signature, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction, writer);
errorNameNode = undefined;
}
}

Expand Down Expand Up @@ -326,6 +339,7 @@ namespace ts {
case SyntaxKind.BooleanKeyword:
case SyntaxKind.SymbolKeyword:
case SyntaxKind.VoidKeyword:
case SyntaxKind.ThisKeyword:
case SyntaxKind.StringLiteral:
return writeTextOfNode(currentSourceFile, type);
case SyntaxKind.ExpressionWithTypeArguments:
Expand Down
8 changes: 8 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1652,6 +1652,14 @@
"category": "Error",
"code": 2525
},
"A 'this' type is available only in a non-static member of a class or interface.": {
"category": "Error",
"code": 2526
},
"The inferred type of '{0}' references an inaccessible 'this' type. A type annotation is necessary.": {
"category": "Error",
"code": 2527
},
"JSX element attributes type '{0}' must be an object type.": {
"category": "Error",
"code": 2600
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2360,6 +2360,7 @@ namespace ts {
let node = tryParse(parseKeywordAndNoDot);
return node || parseTypeReferenceOrTypePredicate();
case SyntaxKind.VoidKeyword:
case SyntaxKind.ThisKeyword:
return parseTokenNode<TypeNode>();
case SyntaxKind.TypeOfKeyword:
return parseTypeQuery();
Expand All @@ -2382,6 +2383,7 @@ namespace ts {
case SyntaxKind.BooleanKeyword:
case SyntaxKind.SymbolKeyword:
case SyntaxKind.VoidKeyword:
case SyntaxKind.ThisKeyword:
case SyntaxKind.TypeOfKeyword:
case SyntaxKind.OpenBraceToken:
case SyntaxKind.OpenBracketToken:
Expand Down
15 changes: 13 additions & 2 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,7 @@ namespace ts {
OctalLiteral = 0x00010000, // Octal numeric literal
Namespace = 0x00020000, // Namespace declaration
ExportContext = 0x00040000, // Export context (initialized by binding)
ContainsThis = 0x00080000, // Interface contains references to "this"

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

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

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

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

// Generic class and interface types
Expand Down
16 changes: 10 additions & 6 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ namespace ts {
increaseIndent: () => { },
decreaseIndent: () => { },
clear: () => str = "",
trackSymbol: () => { }
trackSymbol: () => { },
reportInaccessibleThisError: () => { }
};
}

Expand Down Expand Up @@ -468,13 +469,12 @@ namespace ts {
else if (node.parent.kind === SyntaxKind.PropertyAccessExpression && (<PropertyAccessExpression>node.parent).name === node) {
node = node.parent;
}
// fall through
case SyntaxKind.QualifiedName:
case SyntaxKind.PropertyAccessExpression:
// At this point, node is either a qualified name or an identifier
Debug.assert(node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.QualifiedName || node.kind === SyntaxKind.PropertyAccessExpression,
"'node' was expected to be a qualified name, identifier or property access in 'isTypeNode'.");

case SyntaxKind.QualifiedName:
case SyntaxKind.PropertyAccessExpression:
case SyntaxKind.ThisKeyword:
let parent = node.parent;
if (parent.kind === SyntaxKind.TypeQuery) {
return false;
Expand Down Expand Up @@ -733,6 +733,9 @@ namespace ts {
case SyntaxKind.Constructor:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.CallSignature:
case SyntaxKind.ConstructSignature:
case SyntaxKind.IndexSignature:
case SyntaxKind.EnumDeclaration:
case SyntaxKind.SourceFile:
return node;
Expand Down Expand Up @@ -895,7 +898,6 @@ namespace ts {

export function isExpression(node: Node): boolean {
switch (node.kind) {
case SyntaxKind.ThisKeyword:
case SyntaxKind.SuperKeyword:
case SyntaxKind.NullKeyword:
case SyntaxKind.TrueKeyword:
Expand Down Expand Up @@ -928,6 +930,7 @@ namespace ts {
case SyntaxKind.JsxElement:
case SyntaxKind.JsxSelfClosingElement:
case SyntaxKind.YieldExpression:
case SyntaxKind.AwaitExpression:
return true;
case SyntaxKind.QualifiedName:
while (node.parent.kind === SyntaxKind.QualifiedName) {
Expand All @@ -941,6 +944,7 @@ namespace ts {
// fall through
case SyntaxKind.NumericLiteral:
case SyntaxKind.StringLiteral:
case SyntaxKind.ThisKeyword:
let parent = node.parent;
switch (parent.kind) {
case SyntaxKind.VariableDeclaration:
Expand Down
5 changes: 3 additions & 2 deletions src/services/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -725,7 +725,7 @@ namespace ts {
}
getBaseTypes(): ObjectType[] {
return this.flags & (TypeFlags.Class | TypeFlags.Interface)
? this.checker.getBaseTypes(<TypeObject & InterfaceType>this)
? this.checker.getBaseTypes(<InterfaceType><Type>this)
: undefined;
}
}
Expand Down Expand Up @@ -6249,7 +6249,8 @@ namespace ts {
}

return node.parent.kind === SyntaxKind.TypeReference ||
(node.parent.kind === SyntaxKind.ExpressionWithTypeArguments && !isExpressionWithTypeArgumentsInClassExtendsClause(<ExpressionWithTypeArguments>node.parent));
(node.parent.kind === SyntaxKind.ExpressionWithTypeArguments && !isExpressionWithTypeArgumentsInClassExtendsClause(<ExpressionWithTypeArguments>node.parent)) ||
node.kind === SyntaxKind.ThisKeyword && !isExpression(node);
}

function isNamespaceReference(node: Node): boolean {
Expand Down
3 changes: 2 additions & 1 deletion src/services/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -626,7 +626,8 @@ namespace ts {
increaseIndent: () => { indent++; },
decreaseIndent: () => { indent--; },
clear: resetWriter,
trackSymbol: () => { }
trackSymbol: () => { },
reportInaccessibleThisError: () => { }
};

function writeIndent() {
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/2dArrays.types
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class Board {
>this.ships.every(function (val) { return val.isSunk; }) : boolean
>this.ships.every : (callbackfn: (value: Ship, index: number, array: Ship[]) => boolean, thisArg?: any) => boolean
>this.ships : Ship[]
>this : Board
>this : this
>ships : Ship[]
>every : (callbackfn: (value: Ship, index: number, array: Ship[]) => boolean, thisArg?: any) => boolean
>function (val) { return val.isSunk; } : (val: Ship) => boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ class Point {
>"x=" + this.x : string
>"x=" : string
>this.x : number
>this : Point
>this : this
>x : number
>" y=" : string
>this.y : number
>this : Point
>this : this
>y : number
}
}
Expand Down Expand Up @@ -50,7 +50,7 @@ class ColoredPoint extends Point {
>toString : () => string
>" color=" : string
>this.color : string
>this : ColoredPoint
>this : this
>color : string
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class C2 {

return this.x;
>this.x : IHasVisualizationModel
>this : C2
>this : this
>x : IHasVisualizationModel
}
set A(x) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class TestClass {
this.bar(x); // should not error
>this.bar(x) : void
>this.bar : { (x: string): void; (x: string[]): void; }
>this : TestClass
>this : this
>bar : { (x: string): void; (x: string[]): void; }
>x : any
}
Expand Down Expand Up @@ -71,7 +71,7 @@ class TestClass2 {
return this.bar(x); // should not error
>this.bar(x) : number
>this.bar : { (x: string): number; (x: string[]): number; }
>this : TestClass2
>this : this
>bar : { (x: string): number; (x: string[]): number; }
>x : any
}
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/amdModuleName1.types
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class Foo {
this.x = 5;
>this.x = 5 : number
>this.x : number
>this : Foo
>this : this
>x : number
>5 : number
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(25,5): error
tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(26,5): error TS2322: Type 'StrNum' is not assignable to type '[string]'.
Types of property 'pop' are incompatible.
Type '() => string | number' is not assignable to type '() => string'.
Type 'string | number' is not assignable to type 'string'.
Type 'number' is not assignable to type 'string'.
tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(27,5): error TS2322: Type '{ 0: string; 1: number; }' is not assignable to type '[string]'.
Property 'length' is missing in type '{ 0: string; 1: number; }'.
tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(28,5): error TS2322: Type '[string, number]' is not assignable to type '[number, string]'.
Expand Down Expand Up @@ -125,6 +127,8 @@ tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(30,5): error
!!! error TS2322: Type 'StrNum' is not assignable to type '[string]'.
!!! error TS2322: Types of property 'pop' are incompatible.
!!! error TS2322: Type '() => string | number' is not assignable to type '() => string'.
!!! error TS2322: Type 'string | number' is not assignable to type 'string'.
!!! error TS2322: Type 'number' is not assignable to type 'string'.
var m3: [string] = z;
~~
!!! error TS2322: Type '{ 0: string; 1: number; }' is not assignable to type '[string]'.
Expand Down
Loading