Skip to content

Commit

Permalink
Merge pull request #1816 from Microsoft/typeArgsInSuperCall
Browse files Browse the repository at this point in the history
Contextually type parameters in super calls using type arguments on the base class.
  • Loading branch information
DanielRosenwasser committed Jan 28, 2015
2 parents b3731f1 + 0ffa722 commit 26929ee
Show file tree
Hide file tree
Showing 30 changed files with 562 additions and 44 deletions.
36 changes: 33 additions & 3 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6009,11 +6009,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);

// 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 @@ -6279,7 +6309,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

0 comments on commit 26929ee

Please sign in to comment.