Fixes #26128 - signature comp for jsdoc @class. (#26160)

* Fixes #26128 - signature comp for  jsdoc @class.

Another issue caused by js functions tagged with jsdoc
`@constructor` not having construct signatures.

A jsdoc function type that constructs a type (`function(new: Ex)`),
has a construct signature and return value inferred as the
constructed type where as a jsdoc `@constructor` has no construct
signatures, and it's call signature has a void return type
(or undefined).

i.e:
```javascript
/** @constructor **/ function E() {};

// typeof E -> call signature: () => void

/** @param {function(new: E)} d */ function c(d) {}

// typeof d -> construct: () => E
```

--

This commit fixes this (in an inelegant way) by considering `@class` function signatures as construct signatures and synthesizing it's return value _only for signature comparison_.

There might be a slight performance hit, since the synthesized return value is not cached; but changing the `@class` function's return type in `getReturnTypeOfSignature` causes other issues.

* Update jsdoc function test to fix mistake.
This commit is contained in:
James Keane 2018-08-14 16:35:51 -04:00 committed by Nathan Shively-Sanders
parent cea4838972
commit a1089893bd
5 changed files with 279 additions and 4 deletions

View file

@ -10744,11 +10744,13 @@ namespace ts {
}
if (!ignoreReturnTypes) {
const targetReturnType = getReturnTypeOfSignature(target);
const targetReturnType = (target.declaration && isJavaScriptConstructor(target.declaration)) ?
getJavaScriptClassType(target.declaration.symbol)! : getReturnTypeOfSignature(target);
if (targetReturnType === voidType) {
return result;
}
const sourceReturnType = getReturnTypeOfSignature(source);
const sourceReturnType = (source.declaration && isJavaScriptConstructor(source.declaration)) ?
getJavaScriptClassType(source.declaration.symbol)! : getReturnTypeOfSignature(source);
// The following block preserves behavior forbidding boolean returning functions from being assignable to type guard returning functions
const targetTypePredicate = getTypePredicateOfSignature(target);
@ -12015,8 +12017,14 @@ namespace ts {
return Ternary.True;
}
const sourceSignatures = getSignaturesOfType(source, kind);
const targetSignatures = getSignaturesOfType(target, kind);
const sourceIsJSConstructor = source.symbol && isJavaScriptConstructor(source.symbol.valueDeclaration);
const targetIsJSConstructor = target.symbol && isJavaScriptConstructor(target.symbol.valueDeclaration);
const sourceSignatures = getSignaturesOfType(source, (sourceIsJSConstructor && kind === SignatureKind.Construct) ?
SignatureKind.Call : kind);
const targetSignatures = getSignaturesOfType(target, (targetIsJSConstructor && kind === SignatureKind.Construct) ?
SignatureKind.Call : kind);
if (kind === SignatureKind.Construct && sourceSignatures.length && targetSignatures.length) {
if (isAbstractConstructorType(source) && !isAbstractConstructorType(target)) {
// An abstract constructor type is not assignable to a non-abstract constructor type

View file

@ -0,0 +1,76 @@
tests/cases/conformance/jsdoc/functions.js(65,14): error TS2345: Argument of type 'typeof E' is not assignable to parameter of type 'new (arg1: number) => { length: number; }'.
Type 'E' is not assignable to type '{ length: number; }'.
Property 'length' is missing in type 'E'.
==== tests/cases/conformance/jsdoc/functions.js (1 errors) ====
/**
* @param {function(this: string, number): number} c is just passing on through
* @return {function(this: string, number): number}
*/
function id1(c) {
return c
}
var x = id1(function (n) { return this.length + n });
/**
* @param {function(new: { length: number }, number): number} c is just passing on through
* @return {function(new: { length: number }, number): number}
*/
function id2(c) {
return c
}
class C {
/** @param {number} n */
constructor(n) {
this.length = n;
}
}
var y = id2(C);
var z = new y(12);
z.length;
/** @type {function ("a" | "b", 1 | 2): 3 | 4} */
var f = function (ab, onetwo) { return ab === "a" ? 3 : 4; }
/**
* @constructor
* @param {number} n
*/
function D(n) {
this.length = n;
}
var y2 = id2(D);
var z2 = new y2(33);
z2.length;
/**
* @param {function(new: D, number)} dref
* @return {D}
*/
var construct = function(dref) { return new dref(33); }
var z3 = construct(D);
z3.length;
/**
* @constructor
* @param {number} n
*/
var E = function(n) {
this.not_length_on_purpose = n;
};
var y3 = id2(E);
~
!!! error TS2345: Argument of type 'typeof E' is not assignable to parameter of type 'new (arg1: number) => { length: number; }'.
!!! error TS2345: Type 'E' is not assignable to type '{ length: number; }'.
!!! error TS2345: Property 'length' is missing in type 'E'.

View file

@ -68,3 +68,76 @@ var f = function (ab, onetwo) { return ab === "a" ? 3 : 4; }
>onetwo : Symbol(onetwo, Decl(functions.js, 30, 21))
>ab : Symbol(ab, Decl(functions.js, 30, 18))
/**
* @constructor
* @param {number} n
*/
function D(n) {
>D : Symbol(D, Decl(functions.js, 30, 61))
>n : Symbol(n, Decl(functions.js, 37, 11))
this.length = n;
>this.length : Symbol(D.length, Decl(functions.js, 37, 15))
>this : Symbol(D, Decl(functions.js, 30, 61))
>length : Symbol(D.length, Decl(functions.js, 37, 15))
>n : Symbol(n, Decl(functions.js, 37, 11))
}
var y2 = id2(D);
>y2 : Symbol(y2, Decl(functions.js, 41, 3))
>id2 : Symbol(id2, Decl(functions.js, 8, 53))
>D : Symbol(D, Decl(functions.js, 30, 61))
var z2 = new y2(33);
>z2 : Symbol(z2, Decl(functions.js, 42, 3))
>y2 : Symbol(y2, Decl(functions.js, 41, 3))
z2.length;
>z2.length : Symbol(length, Decl(functions.js, 12, 27))
>z2 : Symbol(z2, Decl(functions.js, 42, 3))
>length : Symbol(length, Decl(functions.js, 12, 27))
/**
* @param {function(new: D, number)} dref
* @return {D}
*/
var construct = function(dref) { return new dref(33); }
>construct : Symbol(construct, Decl(functions.js, 50, 3))
>dref : Symbol(dref, Decl(functions.js, 50, 25))
>dref : Symbol(dref, Decl(functions.js, 50, 25))
var z3 = construct(D);
>z3 : Symbol(z3, Decl(functions.js, 51, 3))
>construct : Symbol(construct, Decl(functions.js, 50, 3))
>D : Symbol(D, Decl(functions.js, 30, 61))
z3.length;
>z3.length : Symbol(D.length, Decl(functions.js, 37, 15))
>z3 : Symbol(z3, Decl(functions.js, 51, 3))
>length : Symbol(D.length, Decl(functions.js, 37, 15))
/**
* @constructor
* @param {number} n
*/
var E = function(n) {
>E : Symbol(E, Decl(functions.js, 59, 3))
>n : Symbol(n, Decl(functions.js, 59, 17))
this.not_length_on_purpose = n;
>this.not_length_on_purpose : Symbol(E.not_length_on_purpose, Decl(functions.js, 59, 21))
>this : Symbol(E, Decl(functions.js, 59, 7))
>not_length_on_purpose : Symbol(E.not_length_on_purpose, Decl(functions.js, 59, 21))
>n : Symbol(n, Decl(functions.js, 59, 17))
};
var y3 = id2(E);
>y3 : Symbol(y3, Decl(functions.js, 64, 3))
>id2 : Symbol(id2, Decl(functions.js, 8, 53))
>E : Symbol(E, Decl(functions.js, 59, 3))

View file

@ -81,3 +81,87 @@ var f = function (ab, onetwo) { return ab === "a" ? 3 : 4; }
>3 : 3
>4 : 4
/**
* @constructor
* @param {number} n
*/
function D(n) {
>D : typeof D
>n : number
this.length = n;
>this.length = n : number
>this.length : number
>this : D
>length : number
>n : number
}
var y2 = id2(D);
>y2 : new (arg1: number) => { length: number; }
>id2(D) : new (arg1: number) => { length: number; }
>id2 : (c: new (arg1: number) => { length: number; }) => new (arg1: number) => { length: number; }
>D : typeof D
var z2 = new y2(33);
>z2 : { length: number; }
>new y2(33) : { length: number; }
>y2 : new (arg1: number) => { length: number; }
>33 : 33
z2.length;
>z2.length : number
>z2 : { length: number; }
>length : number
/**
* @param {function(new: D, number)} dref
* @return {D}
*/
var construct = function(dref) { return new dref(33); }
>construct : (dref: new (arg1: number) => D) => D
>function(dref) { return new dref(33); } : (dref: new (arg1: number) => D) => D
>dref : new (arg1: number) => D
>new dref(33) : D
>dref : new (arg1: number) => D
>33 : 33
var z3 = construct(D);
>z3 : D
>construct(D) : D
>construct : (dref: new (arg1: number) => D) => D
>D : typeof D
z3.length;
>z3.length : number
>z3 : D
>length : number
/**
* @constructor
* @param {number} n
*/
var E = function(n) {
>E : typeof E
>function(n) { this.not_length_on_purpose = n;} : typeof E
>n : number
this.not_length_on_purpose = n;
>this.not_length_on_purpose = n : number
>this.not_length_on_purpose : number
>this : E
>not_length_on_purpose : number
>n : number
};
var y3 = id2(E);
>y3 : new (arg1: number) => { length: number; }
>id2(E) : new (arg1: number) => { length: number; }
>id2 : (c: new (arg1: number) => { length: number; }) => new (arg1: number) => { length: number; }
>E : typeof E

View file

@ -36,3 +36,37 @@ z.length;
/** @type {function ("a" | "b", 1 | 2): 3 | 4} */
var f = function (ab, onetwo) { return ab === "a" ? 3 : 4; }
/**
* @constructor
* @param {number} n
*/
function D(n) {
this.length = n;
}
var y2 = id2(D);
var z2 = new y2(33);
z2.length;
/**
* @param {function(new: D, number)} dref
* @return {D}
*/
var construct = function(dref) { return new dref(33); }
var z3 = construct(D);
z3.length;
/**
* @constructor
* @param {number} n
*/
var E = function(n) {
this.not_length_on_purpose = n;
};
var y3 = id2(E);