Merge pull request #1816 from Microsoft/typeArgsInSuperCall

Contextually type parameters in super calls using type arguments on the base class.
This commit is contained in:
Daniel Rosenwasser
2015-01-27 17:29:48 -08:00
30 changed files with 562 additions and 44 deletions

View File

@@ -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
@@ -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

View File

@@ -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;
}
}

View File

@@ -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;
@@ -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;

View File

@@ -1,38 +0,0 @@
=== tests/cases/compiler/superCallArgsMustMatch.ts ===
class T5<T>{
>T5 : T5<T>
>T : T
public foo: T;
>foo : T
>T : T
constructor(public bar: T) { }
>bar : T
>T : T
}
class T6 extends T5<number>{
>T6 : T6
>T5 : T5<T>
constructor() {
super("hi"); // Should error, base constructor has type T for first arg, which is fixed as number in the extends clause
>super("hi") : void
>super : typeof T5
var x: number = this.foo;
>x : number
>this.foo : number
>this : T6
>foo : number
}
}

View File

@@ -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.
}

View File

@@ -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);

View File

@@ -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.
}

View File

@@ -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);

View File

@@ -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.
}

View File

@@ -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);

View File

@@ -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.
}

View File

@@ -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;
})();

View File

@@ -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.
}

View File

@@ -0,0 +1,10 @@
//// [superCallFromFunction1.ts]
function foo() {
super(value => String(value));
}
//// [superCallFromFunction1.js]
function foo() {
_super.call(this, function (value) { return String(value); });
}

View File

@@ -0,0 +1,35 @@
//// [superCallParameterContextualTyping1.ts]
class A<T1, T2> {
constructor(private map: (value: T1) => T2) {
}
}
class B extends A<number, string> {
// Ensure 'value' is of type 'number (and not '{}') by using its 'toExponential()' method.
constructor() { super(value => String(value.toExponential())); }
}
//// [superCallParameterContextualTyping1.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);
// Ensure 'value' is of type 'number (and not '{}') by using its 'toExponential()' method.
function B() {
_super.call(this, function (value) { return String(value.toExponential()); });
}
return B;
})(A);

View File

@@ -0,0 +1,34 @@
=== tests/cases/conformance/expressions/contextualTyping/superCallParameterContextualTyping1.ts ===
class A<T1, T2> {
>A : A<T1, T2>
>T1 : T1
>T2 : T2
constructor(private map: (value: T1) => T2) {
>map : (value: T1) => T2
>value : T1
>T1 : T1
>T2 : T2
}
}
class B extends A<number, string> {
>B : B
>A : A<T1, T2>
// Ensure 'value' is of type 'number (and not '{}') by using its 'toExponential()' method.
constructor() { super(value => String(value.toExponential())); }
>super(value => String(value.toExponential())) : void
>super : typeof A
>value => String(value.toExponential()) : (value: number) => string
>value : number
>String(value.toExponential()) : string
>String : StringConstructor
>value.toExponential() : string
>value.toExponential : (fractionDigits?: number) => string
>value : number
>toExponential : (fractionDigits?: number) => string
}

View File

@@ -0,0 +1,17 @@
tests/cases/conformance/expressions/contextualTyping/superCallParameterContextualTyping2.ts(10,43): error TS2349: Cannot invoke an expression whose type lacks a call signature.
==== tests/cases/conformance/expressions/contextualTyping/superCallParameterContextualTyping2.ts (1 errors) ====
class A<T1, T2> {
constructor(private map: (value: T1) => T2) {
}
}
class C extends A<number, string> {
// Ensure 'value' is not of type 'any' by invoking it with type arguments.
constructor() { super(value => String(value<string>())); }
~~~~~~~~~~~~~~~
!!! error TS2349: Cannot invoke an expression whose type lacks a call signature.
}

View File

@@ -0,0 +1,34 @@
//// [superCallParameterContextualTyping2.ts]
class A<T1, T2> {
constructor(private map: (value: T1) => T2) {
}
}
class C extends A<number, string> {
// Ensure 'value' is not of type 'any' by invoking it with type arguments.
constructor() { super(value => String(value<string>())); }
}
//// [superCallParameterContextualTyping2.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 C = (function (_super) {
__extends(C, _super);
// Ensure 'value' is not of type 'any' by invoking it with type arguments.
function C() {
_super.call(this, function (value) { return String(value()); });
}
return C;
})(A);

View File

@@ -0,0 +1,23 @@
tests/cases/compiler/superNewCall1.ts(9,5): error TS2377: Constructors for derived classes must contain a 'super' call.
tests/cases/compiler/superNewCall1.ts(10,9): error TS2351: Cannot use 'new' with an expression whose type lacks a call or construct signature.
==== tests/cases/compiler/superNewCall1.ts (2 errors) ====
class A<T1, T2> {
constructor(private map: (value: T1) => T2) {
}
}
class B extends A<number, string> {
constructor() {
~~~~~~~~~~~~~~~
new super(value => String(value));
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2351: Cannot use 'new' with an expression whose type lacks a call or construct signature.
}
~~~~~
!!! error TS2377: Constructors for derived classes must contain a 'super' call.
}

View File

@@ -0,0 +1,34 @@
//// [superNewCall1.ts]
class A<T1, T2> {
constructor(private map: (value: T1) => T2) {
}
}
class B extends A<number, string> {
constructor() {
new super(value => String(value));
}
}
//// [superNewCall1.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() {
new _super.prototype(function (value) { return String(value); });
}
return B;
})(A);

View File

@@ -12,7 +12,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;

View File

@@ -0,0 +1,10 @@
class A<T1, T2> {
constructor(private map: (value: T1) => T2) {
}
}
class B extends A<number> {
constructor() { super(value => String(value)); }
}

View File

@@ -0,0 +1,10 @@
class A<T1, T2> {
constructor(private map: (value: T1) => T2) {
}
}
class B extends A {
constructor() { super(value => String(value)); }
}

View File

@@ -0,0 +1,10 @@
class A {
constructor(private map: (value: number) => string) {
}
}
class B extends A<number, string> {
constructor() { super(value => String(value)); }
}

View File

@@ -0,0 +1,10 @@
class A {
constructor(private map: (value: number) => string) {
}
}
class B {
constructor() { super(value => String(value)); }
}

View File

@@ -0,0 +1,4 @@
function foo() {
super(value => String(value));
}

View File

@@ -0,0 +1,12 @@
class A<T1, T2> {
constructor(private map: (value: T1) => T2) {
}
}
class B extends A<number, string> {
constructor() {
new super(value => String(value));
}
}

View File

@@ -0,0 +1,11 @@
class A<T1, T2> {
constructor(private map: (value: T1) => T2) {
}
}
class B extends A<number, string> {
// Ensure 'value' is of type 'number (and not '{}') by using its 'toExponential()' method.
constructor() { super(value => String(value.toExponential())); }
}

View File

@@ -0,0 +1,11 @@
class A<T1, T2> {
constructor(private map: (value: T1) => T2) {
}
}
class C extends A<number, string> {
// Ensure 'value' is not of type 'any' by invoking it with type arguments.
constructor() { super(value => String(value<string>())); }
}

View File

@@ -0,0 +1,20 @@
/// <reference path='fourslash.ts'/>
////class A<T1, T2> {
//// constructor(private map: (value: T1) => T2) {
////
//// }
////}
////
////class B extends A<number, string> {
//// constructor() { super(va/*1*/lue => String(va/*2*/lue.toExpone/*3*/ntial())); }
////}
goTo.marker('1');
verify.quickInfoIs('(parameter) value: number');
goTo.marker('2');
verify.quickInfoIs('(parameter) value: number');
goTo.marker('3');
verify.quickInfoIs('(method) Number.toExponential(fractionDigits?: number): string');