mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Contextually type parameters in super calls using type arguments on the base class.
This commit is contained in:
+28
-3
@@ -5978,11 +5978,36 @@ 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.
|
||||
function getEffectiveTypeArguments(callExpression: CallExpression): TypeNode[] {
|
||||
if (callExpression.expression.kind === SyntaxKind.SuperKeyword) {
|
||||
// TODO (drosen): 1) Discuss if checking needs to be done at this point.
|
||||
// 2) Have a test where type arguments are not provided on the base class.
|
||||
// 3) Have a test where the base class is not generic.
|
||||
var containingClass = <ClassDeclaration>getAncestor(callExpression, SyntaxKind.ClassDeclaration);
|
||||
var baseClassTypeNode = getClassBaseTypeNode(containingClass);
|
||||
return baseClassTypeNode.typeArguments;
|
||||
}
|
||||
else {
|
||||
// Ordinary case - simple function invocation.
|
||||
return (<CallExpression>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
|
||||
@@ -6248,7 +6273,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
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
tests/cases/compiler/superCallArgsMustMatch.ts(15,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() {
|
||||
|
||||
super("hi"); // Should error, base constructor has type T for first arg, which is fixed as number in the extends clause
|
||||
~~~~
|
||||
!!! error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
|
||||
|
||||
var x: number = this.foo;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
}
|
||||
@@ -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);
|
||||
+11
@@ -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())); }
|
||||
}
|
||||
+11
@@ -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>())); }
|
||||
}
|
||||
@@ -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('(var) value: number');
|
||||
|
||||
goTo.marker('2');
|
||||
verify.quickInfoIs('(var) value: number');
|
||||
|
||||
goTo.marker('3');
|
||||
verify.quickInfoIs('(method) Number.toExponential(fractionDigits?: number): string');
|
||||
Reference in New Issue
Block a user