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

Contextually type parameters in super calls using type arguments on the base class. #1816

Merged
merged 6 commits into from
Jan 28, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
36 changes: 33 additions & 3 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5978,11 +5978,41 @@ module ts {
return args;
}

/**
* In a 'super' call, type arguments are not provided within the CallExpression node itself.
* Instead, they must be fetched from the class declaration's base type node.
*
* If 'node' is a 'super' call (e.g. super(...), new super(...)), then we attempt to fetch
* the type arguments off the containing class's first heritage clause (if one exists). Note that if
* type arguments are supplied on the 'super' call, they are ignored (though this is syntactically incorrect).
*
* In all other cases, the call's explicit type arguments are returned.
*/
function getEffectiveTypeArguments(callExpression: CallExpression): TypeNode[] {
if (callExpression.expression.kind === SyntaxKind.SuperKeyword) {
var containingClass = <ClassDeclaration>getAncestor(callExpression, SyntaxKind.ClassDeclaration);
var baseClassTypeNode = containingClass && getClassBaseTypeNode(containingClass);
return baseClassTypeNode && baseClassTypeNode.typeArguments;
}
else {
// Ordinary case - simple function invocation.
return callExpression.typeArguments;
}
}

function resolveCall(node: CallLikeExpression, signatures: Signature[], candidatesOutArray: Signature[]): Signature {
var isTaggedTemplate = node.kind === SyntaxKind.TaggedTemplateExpression;

var typeArguments = isTaggedTemplate ? undefined : (<CallExpression>node).typeArguments;
forEach(typeArguments, checkSourceElement);
var typeArguments: TypeNode[];

if (!isTaggedTemplate) {
typeArguments = getEffectiveTypeArguments(<CallExpression>node);
Copy link
Contributor

Choose a reason for hiding this comment

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

This is fine, but one alternative you might consider is for resolveCall to take a parameter for the type arguments, and have the caller determine what to pass. So whichever caller handles super calls would get the type arguments from the base type reference. But I admit it's a bit weird to pass the entire CallLikeExpression, with the the type arguments alongside it. It's like having meat and potatoes, with a side of potatoes.

Copy link
Member Author

Choose a reason for hiding this comment

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

Exactly! It's like if you gave me corn alongside corn-off-the-cob - it's kind of odd to give both.

Copy link
Member Author

Choose a reason for hiding this comment

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

We can do this without passing the type arguments. But I think we'll leave it for another PR.


// We already perform checking on the type arguments on the class declaration itself.
if ((<CallExpression>node).expression.kind !== SyntaxKind.SuperKeyword) {
forEach(typeArguments, checkSourceElement);
}
}

var candidates = candidatesOutArray || [];
// collectCandidates fills up the candidates array directly
Expand Down Expand Up @@ -6248,7 +6278,7 @@ module ts {
// Another error has already been reported
return resolveErrorCall(node);
}

// Technically, this signatures list may be incomplete. We are taking the apparent type,
// but we are not including call signatures that may have been added to the Object or
// Function interface, since they have none by default. This is a bit of a leap of faith
Expand Down
31 changes: 31 additions & 0 deletions tests/baselines/reference/superCallArgsMustMatch.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
tests/cases/compiler/superCallArgsMustMatch.ts(17,15): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.


==== tests/cases/compiler/superCallArgsMustMatch.ts (1 errors) ====
class T5<T>{

public foo: T;

constructor(public bar: T) { }

}



class T6 extends T5<number>{

constructor() {

// Should error; base constructor has type T for first arg,
// which is instantiated with 'number' in the extends clause
super("hi");
~~~~
!!! error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.

var x: number = this.foo;

}

}


8 changes: 6 additions & 2 deletions tests/baselines/reference/superCallArgsMustMatch.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ class T6 extends T5<number>{

constructor() {

super("hi"); // Should error, base constructor has type T for first arg, which is fixed as number in the extends clause
// Should error; base constructor has type T for first arg,
// which is instantiated with 'number' in the extends clause
super("hi");

var x: number = this.foo;

Expand All @@ -39,7 +41,9 @@ var T5 = (function () {
var T6 = (function (_super) {
__extends(T6, _super);
function T6() {
_super.call(this, "hi"); // Should error, base constructor has type T for first arg, which is fixed as number in the extends clause
// Should error; base constructor has type T for first arg,
// which is instantiated with 'number' in the extends clause
_super.call(this, "hi");
var x = this.foo;
}
return T6;
Expand Down
38 changes: 0 additions & 38 deletions tests/baselines/reference/superCallArgsMustMatch.types

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
tests/cases/compiler/superCallFromClassThatDerivesFromGenericTypeButWithIncorrectNumberOfTypeArguments1.ts(8,17): error TS2314: Generic type 'A<T1, T2>' requires 2 type argument(s).
tests/cases/compiler/superCallFromClassThatDerivesFromGenericTypeButWithIncorrectNumberOfTypeArguments1.ts(9,21): error TS2335: 'super' can only be referenced in a derived class.


==== tests/cases/compiler/superCallFromClassThatDerivesFromGenericTypeButWithIncorrectNumberOfTypeArguments1.ts (2 errors) ====

class A<T1, T2> {
constructor(private map: (value: T1) => T2) {

}
}

class B extends A<number> {
~~~~~~~~~
!!! error TS2314: Generic type 'A<T1, T2>' requires 2 type argument(s).
constructor() { super(value => String(value)); }
~~~~~
!!! error TS2335: 'super' can only be referenced in a derived class.
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//// [superCallFromClassThatDerivesFromGenericTypeButWithIncorrectNumberOfTypeArguments1.ts]

class A<T1, T2> {
constructor(private map: (value: T1) => T2) {

}
}

class B extends A<number> {
constructor() { super(value => String(value)); }
}

//// [superCallFromClassThatDerivesFromGenericTypeButWithIncorrectNumberOfTypeArguments1.js]
var __extends = this.__extends || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
__.prototype = b.prototype;
d.prototype = new __();
};
var A = (function () {
function A(map) {
this.map = map;
}
return A;
})();
var B = (function (_super) {
__extends(B, _super);
function B() {
_super.call(this, function (value) { return String(value); });
}
return B;
})(A);
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
tests/cases/compiler/superCallFromClassThatDerivesFromGenericTypeButWithNoTypeArguments1.ts(8,17): error TS2314: Generic type 'A<T1, T2>' requires 2 type argument(s).
tests/cases/compiler/superCallFromClassThatDerivesFromGenericTypeButWithNoTypeArguments1.ts(9,21): error TS2335: 'super' can only be referenced in a derived class.


==== tests/cases/compiler/superCallFromClassThatDerivesFromGenericTypeButWithNoTypeArguments1.ts (2 errors) ====

class A<T1, T2> {
constructor(private map: (value: T1) => T2) {

}
}

class B extends A {
~
!!! error TS2314: Generic type 'A<T1, T2>' requires 2 type argument(s).
constructor() { super(value => String(value)); }
~~~~~
!!! error TS2335: 'super' can only be referenced in a derived class.
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//// [superCallFromClassThatDerivesFromGenericTypeButWithNoTypeArguments1.ts]

class A<T1, T2> {
constructor(private map: (value: T1) => T2) {

}
}

class B extends A {
constructor() { super(value => String(value)); }
}

//// [superCallFromClassThatDerivesFromGenericTypeButWithNoTypeArguments1.js]
var __extends = this.__extends || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
__.prototype = b.prototype;
d.prototype = new __();
};
var A = (function () {
function A(map) {
this.map = map;
}
return A;
})();
var B = (function (_super) {
__extends(B, _super);
function B() {
_super.call(this, function (value) { return String(value); });
}
return B;
})(A);
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
tests/cases/compiler/superCallFromClassThatDerivesNonGenericTypeButWithTypeArguments1.ts(8,17): error TS2315: Type 'A' is not generic.
tests/cases/compiler/superCallFromClassThatDerivesNonGenericTypeButWithTypeArguments1.ts(9,21): error TS2335: 'super' can only be referenced in a derived class.


==== tests/cases/compiler/superCallFromClassThatDerivesNonGenericTypeButWithTypeArguments1.ts (2 errors) ====

class A {
constructor(private map: (value: number) => string) {

}
}

class B extends A<number, string> {
~~~~~~~~~~~~~~~~~
!!! error TS2315: Type 'A' is not generic.
constructor() { super(value => String(value)); }
~~~~~
!!! error TS2335: 'super' can only be referenced in a derived class.
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//// [superCallFromClassThatDerivesNonGenericTypeButWithTypeArguments1.ts]

class A {
constructor(private map: (value: number) => string) {

}
}

class B extends A<number, string> {
constructor() { super(value => String(value)); }
}

//// [superCallFromClassThatDerivesNonGenericTypeButWithTypeArguments1.js]
var __extends = this.__extends || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
__.prototype = b.prototype;
d.prototype = new __();
};
var A = (function () {
function A(map) {
this.map = map;
}
return A;
})();
var B = (function (_super) {
__extends(B, _super);
function B() {
_super.call(this, function (value) { return String(value); });
}
return B;
})(A);
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
tests/cases/compiler/superCallFromClassThatHasNoBaseType1.ts(9,21): error TS2335: 'super' can only be referenced in a derived class.


==== tests/cases/compiler/superCallFromClassThatHasNoBaseType1.ts (1 errors) ====

class A {
constructor(private map: (value: number) => string) {

}
}

class B {
constructor() { super(value => String(value)); }
~~~~~
!!! error TS2335: 'super' can only be referenced in a derived class.
}
25 changes: 25 additions & 0 deletions tests/baselines/reference/superCallFromClassThatHasNoBaseType1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//// [superCallFromClassThatHasNoBaseType1.ts]

class A {
constructor(private map: (value: number) => string) {

}
}

class B {
constructor() { super(value => String(value)); }
}

//// [superCallFromClassThatHasNoBaseType1.js]
var A = (function () {
function A(map) {
this.map = map;
}
return A;
})();
var B = (function () {
function B() {
_super.call(this, function (value) { return String(value); });
}
return B;
})();
10 changes: 10 additions & 0 deletions tests/baselines/reference/superCallFromFunction1.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
tests/cases/compiler/superCallFromFunction1.ts(3,5): error TS2335: 'super' can only be referenced in a derived class.


==== tests/cases/compiler/superCallFromFunction1.ts (1 errors) ====

function foo() {
super(value => String(value));
~~~~~
!!! error TS2335: 'super' can only be referenced in a derived class.
}
10 changes: 10 additions & 0 deletions tests/baselines/reference/superCallFromFunction1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//// [superCallFromFunction1.ts]

function foo() {
super(value => String(value));
}

//// [superCallFromFunction1.js]
function foo() {
_super.call(this, function (value) { return String(value); });
}
Loading