Skip to content

Commit 0d251ba

Browse files
authored
Improve @overload's interactions with constructors (#52577)
1 parent 0f41b7a commit 0d251ba

File tree

6 files changed

+306
-1
lines changed

6 files changed

+306
-1
lines changed

src/compiler/checker.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14458,7 +14458,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1445814458
for (const tag of node.tags) {
1445914459
if (isJSDocOverloadTag(tag)) {
1446014460
const jsDocSignature = tag.typeExpression;
14461-
if (jsDocSignature.type === undefined) {
14461+
if (jsDocSignature.type === undefined && !isConstructorDeclaration(decl)) {
1446214462
reportImplicitAny(jsDocSignature, anyType);
1446314463
}
1446414464
result.push(getSignatureFromDeclaration(jsDocSignature));
@@ -14584,6 +14584,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1458414584
if (declaration.kind === SyntaxKind.Constructor) {
1458514585
return getDeclaredTypeOfClassOrInterface(getMergedSymbol((declaration.parent as ClassDeclaration).symbol));
1458614586
}
14587+
if (isJSDocSignature(declaration)) {
14588+
const root = getJSDocRoot(declaration);
14589+
if (root && isConstructorDeclaration(root.parent)) {
14590+
return getDeclaredTypeOfClassOrInterface(getMergedSymbol((root.parent.parent as ClassDeclaration).symbol));
14591+
}
14592+
}
1458714593
if (isJSDocConstructSignature(declaration)) {
1458814594
return getTypeFromTypeNode((declaration.parameters[0] as ParameterDeclaration).type!); // TODO: GH#18217
1458914595
}
@@ -33773,6 +33779,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3377333779
declaration.kind !== SyntaxKind.Constructor &&
3377433780
declaration.kind !== SyntaxKind.ConstructSignature &&
3377533781
declaration.kind !== SyntaxKind.ConstructorType &&
33782+
!(isJSDocSignature(declaration) && getJSDocRoot(declaration)?.parent?.kind === SyntaxKind.Constructor) &&
3377633783
!isJSDocConstructSignature(declaration) &&
3377733784
!isJSConstructor(declaration)) {
3377833785

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
tests/cases/conformance/jsdoc/overloadTag2.js(25,20): error TS7006: Parameter 'b' implicitly has an 'any' type.
2+
tests/cases/conformance/jsdoc/overloadTag2.js(30,9): error TS2554: Expected 1-2 arguments, but got 0.
3+
4+
5+
==== tests/cases/conformance/jsdoc/overloadTag2.js (2 errors) ====
6+
export class Foo {
7+
#a = true ? 1 : "1"
8+
#b
9+
10+
/**
11+
* Should not have an implicit any error, because constructor's return type is always implicit
12+
* @constructor
13+
* @overload
14+
* @param {string} a
15+
* @param {number} b
16+
*/
17+
/**
18+
* @constructor
19+
* @overload
20+
* @param {number} a
21+
*/
22+
/**
23+
* @constructor
24+
* @overload
25+
* @param {string} a
26+
*//**
27+
* @constructor
28+
* @param {number | string} a
29+
*/
30+
constructor(a, b) {
31+
~
32+
!!! error TS7006: Parameter 'b' implicitly has an 'any' type.
33+
this.#a = a
34+
this.#b = b
35+
}
36+
}
37+
var a = new Foo()
38+
~~~~~~~~~
39+
!!! error TS2554: Expected 1-2 arguments, but got 0.
40+
!!! related TS6210 tests/cases/conformance/jsdoc/overloadTag2.js:15:8: An argument for 'a' was not provided.
41+
var b = new Foo('str')
42+
var c = new Foo(2)
43+
var d = new Foo('str', 2)
44+
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
//// [overloadTag2.js]
2+
export class Foo {
3+
#a = true ? 1 : "1"
4+
#b
5+
6+
/**
7+
* Should not have an implicit any error, because constructor's return type is always implicit
8+
* @constructor
9+
* @overload
10+
* @param {string} a
11+
* @param {number} b
12+
*/
13+
/**
14+
* @constructor
15+
* @overload
16+
* @param {number} a
17+
*/
18+
/**
19+
* @constructor
20+
* @overload
21+
* @param {string} a
22+
*//**
23+
* @constructor
24+
* @param {number | string} a
25+
*/
26+
constructor(a, b) {
27+
this.#a = a
28+
this.#b = b
29+
}
30+
}
31+
var a = new Foo()
32+
var b = new Foo('str')
33+
var c = new Foo(2)
34+
var d = new Foo('str', 2)
35+
36+
37+
//// [overloadTag2.js]
38+
export class Foo {
39+
#a = true ? 1 : "1";
40+
#b;
41+
/**
42+
* Should not have an implicit any error, because constructor's return type is always implicit
43+
* @constructor
44+
* @overload
45+
* @param {string} a
46+
* @param {number} b
47+
*/
48+
/**
49+
* @constructor
50+
* @overload
51+
* @param {number} a
52+
*/
53+
/**
54+
* @constructor
55+
* @overload
56+
* @param {string} a
57+
*/ /**
58+
* @constructor
59+
* @param {number | string} a
60+
*/
61+
constructor(a, b) {
62+
this.#a = a;
63+
this.#b = b;
64+
}
65+
}
66+
var a = new Foo();
67+
var b = new Foo('str');
68+
var c = new Foo(2);
69+
var d = new Foo('str', 2);
70+
71+
72+
//// [overloadTag2.d.ts]
73+
export class Foo {
74+
constructor(a: string, b: number);
75+
constructor(a: number);
76+
constructor(a: string);
77+
#private;
78+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
=== tests/cases/conformance/jsdoc/overloadTag2.js ===
2+
export class Foo {
3+
>Foo : Symbol(Foo, Decl(overloadTag2.js, 0, 0))
4+
5+
#a = true ? 1 : "1"
6+
>#a : Symbol(Foo.#a, Decl(overloadTag2.js, 0, 18))
7+
8+
#b
9+
>#b : Symbol(Foo.#b, Decl(overloadTag2.js, 1, 23))
10+
11+
/**
12+
* Should not have an implicit any error, because constructor's return type is always implicit
13+
* @constructor
14+
* @overload
15+
* @param {string} a
16+
* @param {number} b
17+
*/
18+
/**
19+
* @constructor
20+
* @overload
21+
* @param {number} a
22+
*/
23+
/**
24+
* @constructor
25+
* @overload
26+
* @param {string} a
27+
*//**
28+
* @constructor
29+
* @param {number | string} a
30+
*/
31+
constructor(a, b) {
32+
>a : Symbol(a, Decl(overloadTag2.js, 24, 16))
33+
>b : Symbol(b, Decl(overloadTag2.js, 24, 18))
34+
35+
this.#a = a
36+
>this.#a : Symbol(Foo.#a, Decl(overloadTag2.js, 0, 18))
37+
>this : Symbol(Foo, Decl(overloadTag2.js, 0, 0))
38+
>a : Symbol(a, Decl(overloadTag2.js, 24, 16))
39+
40+
this.#b = b
41+
>this.#b : Symbol(Foo.#b, Decl(overloadTag2.js, 1, 23))
42+
>this : Symbol(Foo, Decl(overloadTag2.js, 0, 0))
43+
>b : Symbol(b, Decl(overloadTag2.js, 24, 18))
44+
}
45+
}
46+
var a = new Foo()
47+
>a : Symbol(a, Decl(overloadTag2.js, 29, 3))
48+
>Foo : Symbol(Foo, Decl(overloadTag2.js, 0, 0))
49+
50+
var b = new Foo('str')
51+
>b : Symbol(b, Decl(overloadTag2.js, 30, 3))
52+
>Foo : Symbol(Foo, Decl(overloadTag2.js, 0, 0))
53+
54+
var c = new Foo(2)
55+
>c : Symbol(c, Decl(overloadTag2.js, 31, 3))
56+
>Foo : Symbol(Foo, Decl(overloadTag2.js, 0, 0))
57+
58+
var d = new Foo('str', 2)
59+
>d : Symbol(d, Decl(overloadTag2.js, 32, 3))
60+
>Foo : Symbol(Foo, Decl(overloadTag2.js, 0, 0))
61+
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
=== tests/cases/conformance/jsdoc/overloadTag2.js ===
2+
export class Foo {
3+
>Foo : Foo
4+
5+
#a = true ? 1 : "1"
6+
>#a : string | number
7+
>true ? 1 : "1" : 1 | "1"
8+
>true : true
9+
>1 : 1
10+
>"1" : "1"
11+
12+
#b
13+
>#b : any
14+
15+
/**
16+
* Should not have an implicit any error, because constructor's return type is always implicit
17+
* @constructor
18+
* @overload
19+
* @param {string} a
20+
* @param {number} b
21+
*/
22+
/**
23+
* @constructor
24+
* @overload
25+
* @param {number} a
26+
*/
27+
/**
28+
* @constructor
29+
* @overload
30+
* @param {string} a
31+
*//**
32+
* @constructor
33+
* @param {number | string} a
34+
*/
35+
constructor(a, b) {
36+
>a : string | number
37+
>b : any
38+
39+
this.#a = a
40+
>this.#a = a : string | number
41+
>this.#a : string | number
42+
>this : this
43+
>a : string | number
44+
45+
this.#b = b
46+
>this.#b = b : any
47+
>this.#b : any
48+
>this : this
49+
>b : any
50+
}
51+
}
52+
var a = new Foo()
53+
>a : Foo
54+
>new Foo() : Foo
55+
>Foo : typeof Foo
56+
57+
var b = new Foo('str')
58+
>b : Foo
59+
>new Foo('str') : Foo
60+
>Foo : typeof Foo
61+
>'str' : "str"
62+
63+
var c = new Foo(2)
64+
>c : Foo
65+
>new Foo(2) : Foo
66+
>Foo : typeof Foo
67+
>2 : 2
68+
69+
var d = new Foo('str', 2)
70+
>d : Foo
71+
>new Foo('str', 2) : Foo
72+
>Foo : typeof Foo
73+
>'str' : "str"
74+
>2 : 2
75+
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// @checkJs: true
2+
// @allowJs: true
3+
// @target: esnext
4+
// @outdir: foo
5+
// @declaration: true
6+
// @filename: overloadTag2.js
7+
// @strict: true
8+
export class Foo {
9+
#a = true ? 1 : "1"
10+
#b
11+
12+
/**
13+
* Should not have an implicit any error, because constructor's return type is always implicit
14+
* @constructor
15+
* @overload
16+
* @param {string} a
17+
* @param {number} b
18+
*/
19+
/**
20+
* @constructor
21+
* @overload
22+
* @param {number} a
23+
*/
24+
/**
25+
* @constructor
26+
* @overload
27+
* @param {string} a
28+
*//**
29+
* @constructor
30+
* @param {number | string} a
31+
*/
32+
constructor(a, b) {
33+
this.#a = a
34+
this.#b = b
35+
}
36+
}
37+
var a = new Foo()
38+
var b = new Foo('str')
39+
var c = new Foo(2)
40+
var d = new Foo('str', 2)

0 commit comments

Comments
 (0)