Skip to content

Commit 55ae4a3

Browse files
committed
Check @overload sigs against impl sig
Check @overload signatures and implementation signatures. checkFunctionOrConstructorSymbolWorker doesn't recognise functions with @overload tags as overloaded because it's using a simple syntactic check based on TS syntax. This PR adds a check for @overload tags as well.
1 parent 0141d1d commit 55ae4a3

File tree

6 files changed

+273
-1
lines changed

6 files changed

+273
-1
lines changed

src/compiler/checker.ts

+13-1
Original file line numberDiff line numberDiff line change
@@ -38583,9 +38583,21 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3858338583
lastSeenNonAmbientDeclaration = node as FunctionLikeDeclaration;
3858438584
}
3858538585
}
38586+
if (isInJSFile(current) && isFunctionLike(current) && current.jsDoc) {
38587+
// TODO: De-duplicate tag check and improve error span
38588+
outer: for (const node of current.jsDoc) {
38589+
if (node.tags) {
38590+
for (const tag of node.tags) {
38591+
if (isJSDocOverloadTag(tag)) {
38592+
hasOverloads = true;
38593+
break outer;
38594+
}
38595+
}
38596+
}
38597+
}
38598+
}
3858638599
}
3858738600
}
38588-
3858938601
if (multipleConstructorImplementation) {
3859038602
forEach(functionDeclarations, declaration => {
3859138603
error(declaration, Diagnostics.Multiple_constructor_implementations_are_not_allowed);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
tests/cases/conformance/jsdoc/overloadTag1.js(27,1): error TS2769: No overload matches this call.
2+
Overload 1 of 2, '(a: number, b: number): number', gave the following error.
3+
Argument of type 'string' is not assignable to parameter of type 'number'.
4+
Overload 2 of 2, '(a: string, b: boolean): string', gave the following error.
5+
Argument of type 'string' is not assignable to parameter of type 'boolean'.
6+
7+
8+
==== tests/cases/conformance/jsdoc/overloadTag1.js (1 errors) ====
9+
/**
10+
* @overload
11+
* @param {number} a
12+
* @param {number} b
13+
* @returns {number}
14+
*/
15+
/**
16+
* @overload
17+
* @param {string} a
18+
* @param {boolean} b
19+
* @returns {string}
20+
*/
21+
/**
22+
* @param {string | number} a
23+
* @param {string | number} b
24+
* @returns {string | number}
25+
*/
26+
export function overloaded(a,b) {
27+
if (typeof a === "string" && typeof b === "string") {
28+
return a + b;
29+
} else if (typeof a === "number" && typeof b === "number") {
30+
return a + b;
31+
}
32+
throw new Error("Invalid arguments");
33+
}
34+
overloaded(1,2)
35+
overloaded("zero", "one")
36+
~~~~~~~~~~~~~~~~~~~~~~~~~
37+
!!! error TS2769: No overload matches this call.
38+
!!! error TS2769: Overload 1 of 2, '(a: number, b: number): number', gave the following error.
39+
!!! error TS2769: Argument of type 'string' is not assignable to parameter of type 'number'.
40+
!!! error TS2769: Overload 2 of 2, '(a: string, b: boolean): string', gave the following error.
41+
!!! error TS2769: Argument of type 'string' is not assignable to parameter of type 'boolean'.
+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
//// [overloadTag1.js]
2+
/**
3+
* @overload
4+
* @param {number} a
5+
* @param {number} b
6+
* @returns {number}
7+
*/
8+
/**
9+
* @overload
10+
* @param {string} a
11+
* @param {boolean} b
12+
* @returns {string}
13+
*/
14+
/**
15+
* @param {string | number} a
16+
* @param {string | number} b
17+
* @returns {string | number}
18+
*/
19+
export function overloaded(a,b) {
20+
if (typeof a === "string" && typeof b === "string") {
21+
return a + b;
22+
} else if (typeof a === "number" && typeof b === "number") {
23+
return a + b;
24+
}
25+
throw new Error("Invalid arguments");
26+
}
27+
overloaded(1,2)
28+
overloaded("zero", "one")
29+
30+
//// [overloadTag1.js]
31+
"use strict";
32+
Object.defineProperty(exports, "__esModule", { value: true });
33+
exports.overloaded = void 0;
34+
/**
35+
* @overload
36+
* @param {number} a
37+
* @param {number} b
38+
* @returns {number}
39+
*/
40+
/**
41+
* @overload
42+
* @param {string} a
43+
* @param {boolean} b
44+
* @returns {string}
45+
*/
46+
/**
47+
* @param {string | number} a
48+
* @param {string | number} b
49+
* @returns {string | number}
50+
*/
51+
function overloaded(a, b) {
52+
if (typeof a === "string" && typeof b === "string") {
53+
return a + b;
54+
}
55+
else if (typeof a === "number" && typeof b === "number") {
56+
return a + b;
57+
}
58+
throw new Error("Invalid arguments");
59+
}
60+
exports.overloaded = overloaded;
61+
overloaded(1, 2);
62+
overloaded("zero", "one");
63+
64+
65+
//// [overloadTag1.d.ts]
66+
export function overloaded(a: number, b: number): number;
67+
export function overloaded(a: string, b: boolean): string;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
=== tests/cases/conformance/jsdoc/overloadTag1.js ===
2+
/**
3+
* @overload
4+
* @param {number} a
5+
* @param {number} b
6+
* @returns {number}
7+
*/
8+
/**
9+
* @overload
10+
* @param {string} a
11+
* @param {boolean} b
12+
* @returns {string}
13+
*/
14+
/**
15+
* @param {string | number} a
16+
* @param {string | number} b
17+
* @returns {string | number}
18+
*/
19+
export function overloaded(a,b) {
20+
>overloaded : Symbol(overloaded, Decl(overloadTag1.js, 0, 0))
21+
>a : Symbol(a, Decl(overloadTag1.js, 17, 27))
22+
>b : Symbol(b, Decl(overloadTag1.js, 17, 29))
23+
24+
if (typeof a === "string" && typeof b === "string") {
25+
>a : Symbol(a, Decl(overloadTag1.js, 17, 27))
26+
>b : Symbol(b, Decl(overloadTag1.js, 17, 29))
27+
28+
return a + b;
29+
>a : Symbol(a, Decl(overloadTag1.js, 17, 27))
30+
>b : Symbol(b, Decl(overloadTag1.js, 17, 29))
31+
32+
} else if (typeof a === "number" && typeof b === "number") {
33+
>a : Symbol(a, Decl(overloadTag1.js, 17, 27))
34+
>b : Symbol(b, Decl(overloadTag1.js, 17, 29))
35+
36+
return a + b;
37+
>a : Symbol(a, Decl(overloadTag1.js, 17, 27))
38+
>b : Symbol(b, Decl(overloadTag1.js, 17, 29))
39+
}
40+
throw new Error("Invalid arguments");
41+
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
42+
}
43+
overloaded(1,2)
44+
>overloaded : Symbol(overloaded, Decl(overloadTag1.js, 0, 0))
45+
46+
overloaded("zero", "one")
47+
>overloaded : Symbol(overloaded, Decl(overloadTag1.js, 0, 0))
48+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
=== tests/cases/conformance/jsdoc/overloadTag1.js ===
2+
/**
3+
* @overload
4+
* @param {number} a
5+
* @param {number} b
6+
* @returns {number}
7+
*/
8+
/**
9+
* @overload
10+
* @param {string} a
11+
* @param {boolean} b
12+
* @returns {string}
13+
*/
14+
/**
15+
* @param {string | number} a
16+
* @param {string | number} b
17+
* @returns {string | number}
18+
*/
19+
export function overloaded(a,b) {
20+
>overloaded : { (a: number, b: number): number; (a: string, b: boolean): string; }
21+
>a : string | number
22+
>b : string | number
23+
24+
if (typeof a === "string" && typeof b === "string") {
25+
>typeof a === "string" && typeof b === "string" : boolean
26+
>typeof a === "string" : boolean
27+
>typeof a : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
28+
>a : string | number
29+
>"string" : "string"
30+
>typeof b === "string" : boolean
31+
>typeof b : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
32+
>b : string | number
33+
>"string" : "string"
34+
35+
return a + b;
36+
>a + b : string
37+
>a : string
38+
>b : string
39+
40+
} else if (typeof a === "number" && typeof b === "number") {
41+
>typeof a === "number" && typeof b === "number" : boolean
42+
>typeof a === "number" : boolean
43+
>typeof a : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
44+
>a : string | number
45+
>"number" : "number"
46+
>typeof b === "number" : boolean
47+
>typeof b : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
48+
>b : string | number
49+
>"number" : "number"
50+
51+
return a + b;
52+
>a + b : number
53+
>a : number
54+
>b : number
55+
}
56+
throw new Error("Invalid arguments");
57+
>new Error("Invalid arguments") : Error
58+
>Error : ErrorConstructor
59+
>"Invalid arguments" : "Invalid arguments"
60+
}
61+
overloaded(1,2)
62+
>overloaded(1,2) : number
63+
>overloaded : { (a: number, b: number): number; (a: string, b: boolean): string; }
64+
>1 : 1
65+
>2 : 2
66+
67+
overloaded("zero", "one")
68+
>overloaded("zero", "one") : never
69+
>overloaded : { (a: number, b: number): number; (a: string, b: boolean): string; }
70+
>"zero" : "zero"
71+
>"one" : "one"
72+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// @checkJs: true
2+
// @allowJs: true
3+
// @outdir: foo
4+
// @declaration: true
5+
// @filename: overloadTag1.js
6+
/**
7+
* @overload
8+
* @param {number} a
9+
* @param {number} b
10+
* @returns {number}
11+
*/
12+
/**
13+
* @overload
14+
* @param {string} a
15+
* @param {boolean} b
16+
* @returns {string}
17+
*/
18+
/**
19+
* @param {string | number} a
20+
* @param {string | number} b
21+
* @returns {string | number}
22+
*/
23+
export function overloaded(a,b) {
24+
if (typeof a === "string" && typeof b === "string") {
25+
return a + b;
26+
} else if (typeof a === "number" && typeof b === "number") {
27+
return a + b;
28+
}
29+
throw new Error("Invalid arguments");
30+
}
31+
overloaded(1,2)
32+
overloaded("zero", "one")

0 commit comments

Comments
 (0)