Skip to content

Allow expressions in class extends clauses #3516

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

Merged
merged 18 commits into from
Jun 17, 2015
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
266 changes: 150 additions & 116 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions src/compiler/diagnosticInformationMap.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,11 @@ module ts {
Cannot_find_namespace_0: { code: 2503, category: DiagnosticCategory.Error, key: "Cannot find namespace '{0}'." },
No_best_common_type_exists_among_yield_expressions: { code: 2504, category: DiagnosticCategory.Error, key: "No best common type exists among yield expressions." },
A_generator_cannot_have_a_void_type_annotation: { code: 2505, category: DiagnosticCategory.Error, key: "A generator cannot have a 'void' type annotation." },
_0_is_referenced_directly_or_indirectly_in_its_own_base_expression: { code: 2506, category: DiagnosticCategory.Error, key: "'{0}' is referenced directly or indirectly in its own base expression." },
Base_expression_is_not_of_a_constructor_function_type: { code: 2507, category: DiagnosticCategory.Error, key: "Base expression is not of a constructor function type." },
No_base_constructor_has_the_specified_number_of_type_arguments: { code: 2508, category: DiagnosticCategory.Error, key: "No base constructor has the specified number of type arguments." },
Base_constructor_does_not_return_a_class_or_interface_type: { code: 2509, category: DiagnosticCategory.Error, key: "Base constructor does not return a class or interface type." },
Base_constructors_must_all_have_the_same_return_type: { code: 2510, category: DiagnosticCategory.Error, key: "Base constructors must all have the same return type." },
Import_declaration_0_is_using_private_name_1: { code: 4000, category: DiagnosticCategory.Error, key: "Import declaration '{0}' is using private name '{1}'." },
Type_parameter_0_of_exported_class_has_or_is_using_private_name_1: { code: 4002, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported class has or is using private name '{1}'." },
Type_parameter_0_of_exported_interface_has_or_is_using_private_name_1: { code: 4004, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported interface has or is using private name '{1}'." },
Expand Down
20 changes: 20 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1505,6 +1505,26 @@
"category": "Error",
"code": 2505
},
"'{0}' is referenced directly or indirectly in its own base expression.": {
"category": "Error",
"code": 2506
},
"Base expression is not of a constructor function type.": {
"category": "Error",
"code": 2507
},
"No base constructor has the specified number of type arguments.": {
"category": "Error",
"code": 2508
},
"Base constructor does not return a class or interface type.": {
"category": "Error",
"code": 2509
},
"Base constructors must all have the same return type.": {
"category": "Error",
"code": 2510
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be helpful in these 4 error messages to mention what type the expression resolved to.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, at least for the first and the third message.


"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",
Expand Down
1 change: 1 addition & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1660,6 +1660,7 @@ module 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)
baseConstructorType?: Type; // Base constructor type of class
}

export interface InterfaceTypeWithBaseTypes extends InterfaceType {
Expand Down
13 changes: 10 additions & 3 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ module ts {
// Specialized signatures can have string literals as their parameters' type names
return node.parent.kind === SyntaxKind.Parameter;
case SyntaxKind.ExpressionWithTypeArguments:
return true;
return !isClassExtendsExpressionWithTypeArguments(node);

// Identifiers and qualified names may be type nodes, depending on their context. Climb
// above them to find the lowest container
Expand Down Expand Up @@ -460,7 +460,7 @@ module ts {
}
switch (parent.kind) {
case SyntaxKind.ExpressionWithTypeArguments:
return true;
return !isClassExtendsExpressionWithTypeArguments(parent);
case SyntaxKind.TypeParameter:
return node === (<TypeParameterDeclaration>parent).constraint;
case SyntaxKind.PropertyDeclaration:
Expand Down Expand Up @@ -872,7 +872,6 @@ module ts {
while (node.parent.kind === SyntaxKind.QualifiedName) {
node = node.parent;
}

return node.parent.kind === SyntaxKind.TypeQuery;
case SyntaxKind.Identifier:
if (node.parent.kind === SyntaxKind.TypeQuery) {
Expand Down Expand Up @@ -920,6 +919,8 @@ module ts {
return node === (<ComputedPropertyName>parent).expression;
case SyntaxKind.Decorator:
return true;
case SyntaxKind.ExpressionWithTypeArguments:
return isClassExtendsExpressionWithTypeArguments(parent);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

&& node === (<ExpressionWithTypeArguments>parent).expression

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah.

default:
if (isExpression(parent)) {
return true;
Expand Down Expand Up @@ -1878,6 +1879,12 @@ module ts {
return token >= SyntaxKind.FirstAssignment && token <= SyntaxKind.LastAssignment;
}

export function isClassExtendsExpressionWithTypeArguments(node: Node): boolean {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you reword this name? It sounds weird in English to have "extends" modify "expression with type arguments".

Actually, if you make this take ExpressionWithTypeArguments instead of Node, you can just call it isInClassExtendsClause.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need the function to take type Node and check the kind. Can't think of a better name.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The kind can be checked before calling the function.

If you keep the check you can call it isExpressionWithTypeArgumentsInClassExtendsClause.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Otherwise it sounds like bad English for doesClassExtendExpressionWithTypeArguments.

return node.kind === SyntaxKind.ExpressionWithTypeArguments &&
(<HeritageClause>node.parent).token === SyntaxKind.ExtendsKeyword &&
node.parent.parent.kind === SyntaxKind.ClassDeclaration;
}

// Returns false if this heritage clause element's expression contains something unsupported
// (i.e. not a name or dotted name).
export function isSupportedExpressionWithTypeArguments(node: ExpressionWithTypeArguments): boolean {
Expand Down
3 changes: 2 additions & 1 deletion src/services/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5811,7 +5811,8 @@ module ts {
node = node.parent;
}

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

function isNamespaceReference(node: Node): boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ module A {

export class Point3d extends Point {
>Point3d : Point3d
>Point : Point
>Point : typeof Point

z: number;
>z : number
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ module A {

export class Point3d extends Point {
>Point3d : Point3d
>Point : Point
>Point : typeof Point

z: number;
>z : number
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class Point {
}
class ColoredPoint extends Point {
>ColoredPoint : ColoredPoint
>Point : Point
>Point : typeof Point

constructor(x: number, y: number, public color: string) {
>x : number
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/aliasUsageInAccessorsOfClass.types
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ import Backbone = require("aliasUsage1_backbone");

export class VisualizationModel extends Backbone.Model {
>VisualizationModel : VisualizationModel
>Backbone.Model : any
>Backbone.Model : typeof Backbone.Model
>Backbone : typeof Backbone
>Model : Backbone.Model
>Model : typeof Backbone.Model

// interesting stuff here
}
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/aliasUsageInArray.types
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ import Backbone = require("aliasUsageInArray_backbone");

export class VisualizationModel extends Backbone.Model {
>VisualizationModel : VisualizationModel
>Backbone.Model : any
>Backbone.Model : typeof Backbone.Model
>Backbone : typeof Backbone
>Model : Backbone.Model
>Model : typeof Backbone.Model

// interesting stuff here
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ import Backbone = require("aliasUsageInFunctionExpression_backbone");

export class VisualizationModel extends Backbone.Model {
>VisualizationModel : VisualizationModel
>Backbone.Model : any
>Backbone.Model : typeof Backbone.Model
>Backbone : typeof Backbone
>Model : Backbone.Model
>Model : typeof Backbone.Model

// interesting stuff here
}
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/aliasUsageInGenericFunction.types
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ import Backbone = require("aliasUsageInGenericFunction_backbone");

export class VisualizationModel extends Backbone.Model {
>VisualizationModel : VisualizationModel
>Backbone.Model : any
>Backbone.Model : typeof Backbone.Model
>Backbone : typeof Backbone
>Model : Backbone.Model
>Model : typeof Backbone.Model

// interesting stuff here
}
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/aliasUsageInIndexerOfClass.types
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ import Backbone = require("aliasUsageInIndexerOfClass_backbone");

export class VisualizationModel extends Backbone.Model {
>VisualizationModel : VisualizationModel
>Backbone.Model : any
>Backbone.Model : typeof Backbone.Model
>Backbone : typeof Backbone
>Model : Backbone.Model
>Model : typeof Backbone.Model

// interesting stuff here
}
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/aliasUsageInObjectLiteral.types
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ import Backbone = require("aliasUsageInObjectLiteral_backbone");

export class VisualizationModel extends Backbone.Model {
>VisualizationModel : VisualizationModel
>Backbone.Model : any
>Backbone.Model : typeof Backbone.Model
>Backbone : typeof Backbone
>Model : Backbone.Model
>Model : typeof Backbone.Model

// interesting stuff here
}
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/aliasUsageInOrExpression.types
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,9 @@ import Backbone = require("aliasUsageInOrExpression_backbone");

export class VisualizationModel extends Backbone.Model {
>VisualizationModel : VisualizationModel
>Backbone.Model : any
>Backbone.Model : typeof Backbone.Model
>Backbone : typeof Backbone
>Model : Backbone.Model
>Model : typeof Backbone.Model

// interesting stuff here
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class C<T extends IHasVisualizationModel> {
}
class D extends C<IHasVisualizationModel> {
>D : D
>C : C<T>
>C : typeof C
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The best way to handle all these changes is to tweak typeWriter to produce results like what we used to get. Then, we can actually review the handful of files that have relevant changes. Then, once we can see that nothing else changed unintentionally, we can remove that tweak in a followup change and then update all the baselines. With several hundred files changed, github won't show all the changes. And it becomes very difficult to ascertain if something else might have broke as part of this.

We've done this numerous times as we've changed hte compiler. For example, even when moving over from the old compiler to the new one, we tweaked things in the new typewriter to produce the same output as hte previous one.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a workaround to reduce the number of changes, but it isn't possible to completely eliminate the differences. Should be a lot better now, though.

>IHasVisualizationModel : IHasVisualizationModel

x = moduleA;
Expand All @@ -46,9 +46,9 @@ import Backbone = require("aliasUsageInTypeArgumentOfExtendsClause_backbone");

export class VisualizationModel extends Backbone.Model {
>VisualizationModel : VisualizationModel
>Backbone.Model : any
>Backbone.Model : typeof Backbone.Model
>Backbone : typeof Backbone
>Model : Backbone.Model
>Model : typeof Backbone.Model

// interesting stuff here
}
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/aliasUsageInVarAssignment.types
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ import Backbone = require("aliasUsageInVarAssignment_backbone");

export class VisualizationModel extends Backbone.Model {
>VisualizationModel : VisualizationModel
>Backbone.Model : any
>Backbone.Model : typeof Backbone.Model
>Backbone : typeof Backbone
>Model : Backbone.Model
>Model : typeof Backbone.Model

// interesting stuff here
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ declare class A { }

declare class B extends A { }
>B : B
>A : A
>A : typeof A

Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ declare module foo {

class B extends A { }
>B : B
>A : A
>A : typeof A
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ class A { }

class B extends A { x: number; }
>B : B
>A : A
>A : typeof A
>x : number

declare function f(p: A, q: B): number;
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/arrayBestCommonTypes.types
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ module EmptyTypes {

class derived extends base { }
>derived : derived
>base : base
>base : typeof base


class f {
Expand Down Expand Up @@ -417,7 +417,7 @@ module NonEmptyTypes {

class derived extends base { a: string; }
>derived : derived
>base : base
>base : typeof base
>a : string


Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/arrayLiteralTypeInference.types
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ class Action {

class ActionA extends Action {
>ActionA : ActionA
>Action : Action
>Action : typeof Action

value: string;
>value : string
}

class ActionB extends Action {
>ActionB : ActionB
>Action : Action
>Action : typeof Action

trueNess: boolean;
>trueNess : boolean
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/arrayLiterals.types
Original file line number Diff line number Diff line change
Expand Up @@ -123,12 +123,12 @@ class Base { private p; }

class Derived1 extends Base { private m };
>Derived1 : Derived1
>Base : Base
>Base : typeof Base
>m : any

class Derived2 extends Base { private n };
>Derived2 : Derived2
>Base : Base
>Base : typeof Base
>n : any

var context3: Base[] = [new Derived1(), new Derived2()];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class List<T> {
class DerivedList<U> extends List<U> {
>DerivedList : DerivedList<U>
>U : U
>List : List<T>
>List : typeof List
>U : U

foo: U;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ class Base { foo: string; }

class Derived extends Base { bar: string; }
>Derived : Derived
>Base : Base
>Base : typeof Base
>bar : string

class Derived2 extends Derived { baz: string; }
>Derived2 : Derived2
>Derived : Derived
>Derived : typeof Derived
>baz : string

class OtherDerived extends Base { bing: string; }
>OtherDerived : OtherDerived
>Base : Base
>Base : typeof Base
>bing : string

var a: (x: number) => number[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ class Base { foo: string; }

class Derived extends Base { bar: string; }
>Derived : Derived
>Base : Base
>Base : typeof Base
>bar : string

class Derived2 extends Derived { baz: string; }
>Derived2 : Derived2
>Derived : Derived
>Derived : typeof Derived
>baz : string

class OtherDerived extends Base { bing: string; }
>OtherDerived : OtherDerived
>Base : Base
>Base : typeof Base
>bing : string

var a: <T>(x: T) => T[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ class Base { foo: string; }

class Derived extends Base { bar: string; }
>Derived : Derived
>Base : Base
>Base : typeof Base
>bar : string

class Derived2 extends Derived { baz: string; }
>Derived2 : Derived2
>Derived : Derived
>Derived : typeof Derived
>baz : string

class OtherDerived extends Base { bing: string; }
>OtherDerived : OtherDerived
>Base : Base
>Base : typeof Base
>bing : string

interface A {
Expand Down
Loading