diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f2c1c71eb6..691462a6db 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -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 diff --git a/tests/baselines/reference/jsdocFunctionType.errors.txt b/tests/baselines/reference/jsdocFunctionType.errors.txt new file mode 100644 index 0000000000..248448c099 --- /dev/null +++ b/tests/baselines/reference/jsdocFunctionType.errors.txt @@ -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'. + \ No newline at end of file diff --git a/tests/baselines/reference/jsdocFunctionType.symbols b/tests/baselines/reference/jsdocFunctionType.symbols index 775bec8877..70917e7657 100644 --- a/tests/baselines/reference/jsdocFunctionType.symbols +++ b/tests/baselines/reference/jsdocFunctionType.symbols @@ -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)) + diff --git a/tests/baselines/reference/jsdocFunctionType.types b/tests/baselines/reference/jsdocFunctionType.types index 0f4c0c2feb..ebacf52ab1 100644 --- a/tests/baselines/reference/jsdocFunctionType.types +++ b/tests/baselines/reference/jsdocFunctionType.types @@ -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 + diff --git a/tests/cases/conformance/jsdoc/jsdocFunctionType.ts b/tests/cases/conformance/jsdoc/jsdocFunctionType.ts index b124989f96..95c8f8966f 100644 --- a/tests/cases/conformance/jsdoc/jsdocFunctionType.ts +++ b/tests/cases/conformance/jsdoc/jsdocFunctionType.ts @@ -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);