Skip to content

Commit 26929ee

Browse files
Merge pull request #1816 from Microsoft/typeArgsInSuperCall
Contextually type parameters in super calls using type arguments on the base class.
2 parents b3731f1 + 0ffa722 commit 26929ee

File tree

30 files changed

+562
-44
lines changed

30 files changed

+562
-44
lines changed

src/compiler/checker.ts

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6009,11 +6009,41 @@ module ts {
60096009
return args;
60106010
}
60116011

6012+
/**
6013+
* In a 'super' call, type arguments are not provided within the CallExpression node itself.
6014+
* Instead, they must be fetched from the class declaration's base type node.
6015+
*
6016+
* If 'node' is a 'super' call (e.g. super(...), new super(...)), then we attempt to fetch
6017+
* the type arguments off the containing class's first heritage clause (if one exists). Note that if
6018+
* type arguments are supplied on the 'super' call, they are ignored (though this is syntactically incorrect).
6019+
*
6020+
* In all other cases, the call's explicit type arguments are returned.
6021+
*/
6022+
function getEffectiveTypeArguments(callExpression: CallExpression): TypeNode[] {
6023+
if (callExpression.expression.kind === SyntaxKind.SuperKeyword) {
6024+
var containingClass = <ClassDeclaration>getAncestor(callExpression, SyntaxKind.ClassDeclaration);
6025+
var baseClassTypeNode = containingClass && getClassBaseTypeNode(containingClass);
6026+
return baseClassTypeNode && baseClassTypeNode.typeArguments;
6027+
}
6028+
else {
6029+
// Ordinary case - simple function invocation.
6030+
return callExpression.typeArguments;
6031+
}
6032+
}
6033+
60126034
function resolveCall(node: CallLikeExpression, signatures: Signature[], candidatesOutArray: Signature[]): Signature {
60136035
var isTaggedTemplate = node.kind === SyntaxKind.TaggedTemplateExpression;
60146036

6015-
var typeArguments = isTaggedTemplate ? undefined : (<CallExpression>node).typeArguments;
6016-
forEach(typeArguments, checkSourceElement);
6037+
var typeArguments: TypeNode[];
6038+
6039+
if (!isTaggedTemplate) {
6040+
typeArguments = getEffectiveTypeArguments(<CallExpression>node);
6041+
6042+
// We already perform checking on the type arguments on the class declaration itself.
6043+
if ((<CallExpression>node).expression.kind !== SyntaxKind.SuperKeyword) {
6044+
forEach(typeArguments, checkSourceElement);
6045+
}
6046+
}
60176047

60186048
var candidates = candidatesOutArray || [];
60196049
// collectCandidates fills up the candidates array directly
@@ -6279,7 +6309,7 @@ module ts {
62796309
// Another error has already been reported
62806310
return resolveErrorCall(node);
62816311
}
6282-
6312+
62836313
// Technically, this signatures list may be incomplete. We are taking the apparent type,
62846314
// but we are not including call signatures that may have been added to the Object or
62856315
// Function interface, since they have none by default. This is a bit of a leap of faith
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
tests/cases/compiler/superCallArgsMustMatch.ts(17,15): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
2+
3+
4+
==== tests/cases/compiler/superCallArgsMustMatch.ts (1 errors) ====
5+
class T5<T>{
6+
7+
public foo: T;
8+
9+
constructor(public bar: T) { }
10+
11+
}
12+
13+
14+
15+
class T6 extends T5<number>{
16+
17+
constructor() {
18+
19+
// Should error; base constructor has type T for first arg,
20+
// which is instantiated with 'number' in the extends clause
21+
super("hi");
22+
~~~~
23+
!!! error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
24+
25+
var x: number = this.foo;
26+
27+
}
28+
29+
}
30+
31+

tests/baselines/reference/superCallArgsMustMatch.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ class T6 extends T5<number>{
1313

1414
constructor() {
1515

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

1820
var x: number = this.foo;
1921

@@ -39,7 +41,9 @@ var T5 = (function () {
3941
var T6 = (function (_super) {
4042
__extends(T6, _super);
4143
function T6() {
42-
_super.call(this, "hi"); // Should error, base constructor has type T for first arg, which is fixed as number in the extends clause
44+
// Should error; base constructor has type T for first arg,
45+
// which is instantiated with 'number' in the extends clause
46+
_super.call(this, "hi");
4347
var x = this.foo;
4448
}
4549
return T6;

tests/baselines/reference/superCallArgsMustMatch.types

Lines changed: 0 additions & 38 deletions
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
tests/cases/compiler/superCallFromClassThatDerivesFromGenericTypeButWithIncorrectNumberOfTypeArguments1.ts(8,17): error TS2314: Generic type 'A<T1, T2>' requires 2 type argument(s).
2+
tests/cases/compiler/superCallFromClassThatDerivesFromGenericTypeButWithIncorrectNumberOfTypeArguments1.ts(9,21): error TS2335: 'super' can only be referenced in a derived class.
3+
4+
5+
==== tests/cases/compiler/superCallFromClassThatDerivesFromGenericTypeButWithIncorrectNumberOfTypeArguments1.ts (2 errors) ====
6+
7+
class A<T1, T2> {
8+
constructor(private map: (value: T1) => T2) {
9+
10+
}
11+
}
12+
13+
class B extends A<number> {
14+
~~~~~~~~~
15+
!!! error TS2314: Generic type 'A<T1, T2>' requires 2 type argument(s).
16+
constructor() { super(value => String(value)); }
17+
~~~~~
18+
!!! error TS2335: 'super' can only be referenced in a derived class.
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//// [superCallFromClassThatDerivesFromGenericTypeButWithIncorrectNumberOfTypeArguments1.ts]
2+
3+
class A<T1, T2> {
4+
constructor(private map: (value: T1) => T2) {
5+
6+
}
7+
}
8+
9+
class B extends A<number> {
10+
constructor() { super(value => String(value)); }
11+
}
12+
13+
//// [superCallFromClassThatDerivesFromGenericTypeButWithIncorrectNumberOfTypeArguments1.js]
14+
var __extends = this.__extends || function (d, b) {
15+
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
16+
function __() { this.constructor = d; }
17+
__.prototype = b.prototype;
18+
d.prototype = new __();
19+
};
20+
var A = (function () {
21+
function A(map) {
22+
this.map = map;
23+
}
24+
return A;
25+
})();
26+
var B = (function (_super) {
27+
__extends(B, _super);
28+
function B() {
29+
_super.call(this, function (value) { return String(value); });
30+
}
31+
return B;
32+
})(A);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
tests/cases/compiler/superCallFromClassThatDerivesFromGenericTypeButWithNoTypeArguments1.ts(8,17): error TS2314: Generic type 'A<T1, T2>' requires 2 type argument(s).
2+
tests/cases/compiler/superCallFromClassThatDerivesFromGenericTypeButWithNoTypeArguments1.ts(9,21): error TS2335: 'super' can only be referenced in a derived class.
3+
4+
5+
==== tests/cases/compiler/superCallFromClassThatDerivesFromGenericTypeButWithNoTypeArguments1.ts (2 errors) ====
6+
7+
class A<T1, T2> {
8+
constructor(private map: (value: T1) => T2) {
9+
10+
}
11+
}
12+
13+
class B extends A {
14+
~
15+
!!! error TS2314: Generic type 'A<T1, T2>' requires 2 type argument(s).
16+
constructor() { super(value => String(value)); }
17+
~~~~~
18+
!!! error TS2335: 'super' can only be referenced in a derived class.
19+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//// [superCallFromClassThatDerivesFromGenericTypeButWithNoTypeArguments1.ts]
2+
3+
class A<T1, T2> {
4+
constructor(private map: (value: T1) => T2) {
5+
6+
}
7+
}
8+
9+
class B extends A {
10+
constructor() { super(value => String(value)); }
11+
}
12+
13+
//// [superCallFromClassThatDerivesFromGenericTypeButWithNoTypeArguments1.js]
14+
var __extends = this.__extends || function (d, b) {
15+
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
16+
function __() { this.constructor = d; }
17+
__.prototype = b.prototype;
18+
d.prototype = new __();
19+
};
20+
var A = (function () {
21+
function A(map) {
22+
this.map = map;
23+
}
24+
return A;
25+
})();
26+
var B = (function (_super) {
27+
__extends(B, _super);
28+
function B() {
29+
_super.call(this, function (value) { return String(value); });
30+
}
31+
return B;
32+
})(A);
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
tests/cases/compiler/superCallFromClassThatDerivesNonGenericTypeButWithTypeArguments1.ts(8,17): error TS2315: Type 'A' is not generic.
2+
tests/cases/compiler/superCallFromClassThatDerivesNonGenericTypeButWithTypeArguments1.ts(9,21): error TS2335: 'super' can only be referenced in a derived class.
3+
4+
5+
==== tests/cases/compiler/superCallFromClassThatDerivesNonGenericTypeButWithTypeArguments1.ts (2 errors) ====
6+
7+
class A {
8+
constructor(private map: (value: number) => string) {
9+
10+
}
11+
}
12+
13+
class B extends A<number, string> {
14+
~~~~~~~~~~~~~~~~~
15+
!!! error TS2315: Type 'A' is not generic.
16+
constructor() { super(value => String(value)); }
17+
~~~~~
18+
!!! error TS2335: 'super' can only be referenced in a derived class.
19+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//// [superCallFromClassThatDerivesNonGenericTypeButWithTypeArguments1.ts]
2+
3+
class A {
4+
constructor(private map: (value: number) => string) {
5+
6+
}
7+
}
8+
9+
class B extends A<number, string> {
10+
constructor() { super(value => String(value)); }
11+
}
12+
13+
//// [superCallFromClassThatDerivesNonGenericTypeButWithTypeArguments1.js]
14+
var __extends = this.__extends || function (d, b) {
15+
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
16+
function __() { this.constructor = d; }
17+
__.prototype = b.prototype;
18+
d.prototype = new __();
19+
};
20+
var A = (function () {
21+
function A(map) {
22+
this.map = map;
23+
}
24+
return A;
25+
})();
26+
var B = (function (_super) {
27+
__extends(B, _super);
28+
function B() {
29+
_super.call(this, function (value) { return String(value); });
30+
}
31+
return B;
32+
})(A);
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
tests/cases/compiler/superCallFromClassThatHasNoBaseType1.ts(9,21): error TS2335: 'super' can only be referenced in a derived class.
2+
3+
4+
==== tests/cases/compiler/superCallFromClassThatHasNoBaseType1.ts (1 errors) ====
5+
6+
class A {
7+
constructor(private map: (value: number) => string) {
8+
9+
}
10+
}
11+
12+
class B {
13+
constructor() { super(value => String(value)); }
14+
~~~~~
15+
!!! error TS2335: 'super' can only be referenced in a derived class.
16+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//// [superCallFromClassThatHasNoBaseType1.ts]
2+
3+
class A {
4+
constructor(private map: (value: number) => string) {
5+
6+
}
7+
}
8+
9+
class B {
10+
constructor() { super(value => String(value)); }
11+
}
12+
13+
//// [superCallFromClassThatHasNoBaseType1.js]
14+
var A = (function () {
15+
function A(map) {
16+
this.map = map;
17+
}
18+
return A;
19+
})();
20+
var B = (function () {
21+
function B() {
22+
_super.call(this, function (value) { return String(value); });
23+
}
24+
return B;
25+
})();
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
tests/cases/compiler/superCallFromFunction1.ts(3,5): error TS2335: 'super' can only be referenced in a derived class.
2+
3+
4+
==== tests/cases/compiler/superCallFromFunction1.ts (1 errors) ====
5+
6+
function foo() {
7+
super(value => String(value));
8+
~~~~~
9+
!!! error TS2335: 'super' can only be referenced in a derived class.
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//// [superCallFromFunction1.ts]
2+
3+
function foo() {
4+
super(value => String(value));
5+
}
6+
7+
//// [superCallFromFunction1.js]
8+
function foo() {
9+
_super.call(this, function (value) { return String(value); });
10+
}

0 commit comments

Comments
 (0)