Skip to content

Commit 9a0c1d6

Browse files
committed
Treat contextually typed functions in JS files as typed
1 parent f2e9ebd commit 9a0c1d6

File tree

6 files changed

+366
-1
lines changed

6 files changed

+366
-1
lines changed

src/compiler/checker.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15107,7 +15107,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1510715107
isInJSFile(declaration) &&
1510815108
isValueSignatureDeclaration(declaration) &&
1510915109
!hasJSDocParameterTags(declaration) &&
15110-
!getJSDocType(declaration);
15110+
!getJSDocType(declaration) &&
15111+
!getContextualSignatureForFunctionLikeDeclaration(declaration);
1511115112
if (isUntypedSignatureInJSFile) {
1511215113
flags |= SignatureFlags.IsUntypedSignatureInJSFile;
1511315114
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/a.js(9,20): error TS2322: Type 'number' is not assignable to type 'string'.
2+
/a.js(28,5): error TS1360: Type '(a: string, b: string) => void' does not satisfy the expected type '(a: string, ...args: number[]) => void'.
3+
Types of parameters 'b' and 'args' are incompatible.
4+
Type 'number' is not assignable to type 'string'.
5+
/a.js(42,21): error TS7006: Parameter 'uuid' implicitly has an 'any' type.
6+
7+
8+
==== /a.js (3 errors) ====
9+
/** @satisfies {(uuid: string) => void} */
10+
export const fn1 = uuid => {};
11+
12+
/** @typedef {Parameters<typeof fn1>} Foo */
13+
14+
/** @type Foo */
15+
export const v1 = ['abc'];
16+
/** @type Foo */
17+
export const v2 = [123]; // error
18+
~~~
19+
!!! error TS2322: Type 'number' is not assignable to type 'string'.
20+
21+
/** @satisfies {(a: string, ...args: never) => void} */
22+
export const fn2 = (a, b) => {};
23+
24+
/**
25+
* @satisfies {(a: string, ...args: never) => void}
26+
* @param {string} a
27+
*/
28+
export const fn3 = (a, b) => {};
29+
30+
/**
31+
* @satisfies {(a: string, ...args: never) => void}
32+
* @param {string} a
33+
* @param {number} b
34+
*/
35+
export const fn4 = (a, b) => {};
36+
37+
/**
38+
* @satisfies {(a: string, ...args: number[]) => void}
39+
~~~~~~~~~
40+
!!! error TS1360: Type '(a: string, b: string) => void' does not satisfy the expected type '(a: string, ...args: number[]) => void'.
41+
!!! error TS1360: Types of parameters 'b' and 'args' are incompatible.
42+
!!! error TS1360: Type 'number' is not assignable to type 'string'.
43+
* @param {string} a
44+
* @param {string} b
45+
*/
46+
export const fn5 = (a, b) => {};
47+
48+
/**
49+
* @satisfies {(a: string, ...args: number[]) => void}
50+
* @param {string} a
51+
* @param {string | number} b
52+
*/
53+
export const fn6 = (a, b) => {};
54+
55+
/** @satisfies {(uuid: string) => void} */
56+
export function fn7(uuid) {}
57+
~~~~
58+
!!! error TS7006: Parameter 'uuid' implicitly has an 'any' type.
59+
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
//// [tests/cases/conformance/jsdoc/checkJsdocSatisfiesTag15.ts] ////
2+
3+
//// [a.js]
4+
/** @satisfies {(uuid: string) => void} */
5+
export const fn1 = uuid => {};
6+
7+
/** @typedef {Parameters<typeof fn1>} Foo */
8+
9+
/** @type Foo */
10+
export const v1 = ['abc'];
11+
/** @type Foo */
12+
export const v2 = [123]; // error
13+
14+
/** @satisfies {(a: string, ...args: never) => void} */
15+
export const fn2 = (a, b) => {};
16+
17+
/**
18+
* @satisfies {(a: string, ...args: never) => void}
19+
* @param {string} a
20+
*/
21+
export const fn3 = (a, b) => {};
22+
23+
/**
24+
* @satisfies {(a: string, ...args: never) => void}
25+
* @param {string} a
26+
* @param {number} b
27+
*/
28+
export const fn4 = (a, b) => {};
29+
30+
/**
31+
* @satisfies {(a: string, ...args: number[]) => void}
32+
* @param {string} a
33+
* @param {string} b
34+
*/
35+
export const fn5 = (a, b) => {};
36+
37+
/**
38+
* @satisfies {(a: string, ...args: number[]) => void}
39+
* @param {string} a
40+
* @param {string | number} b
41+
*/
42+
export const fn6 = (a, b) => {};
43+
44+
/** @satisfies {(uuid: string) => void} */
45+
export function fn7(uuid) {}
46+
47+
48+
//// [a.js]
49+
"use strict";
50+
Object.defineProperty(exports, "__esModule", { value: true });
51+
exports.fn7 = exports.fn6 = exports.fn5 = exports.fn4 = exports.fn3 = exports.fn2 = exports.v2 = exports.v1 = exports.fn1 = void 0;
52+
/** @satisfies {(uuid: string) => void} */
53+
var fn1 = function (uuid) { };
54+
exports.fn1 = fn1;
55+
/** @typedef {Parameters<typeof fn1>} Foo */
56+
/** @type Foo */
57+
exports.v1 = ['abc'];
58+
/** @type Foo */
59+
exports.v2 = [123]; // error
60+
/** @satisfies {(a: string, ...args: never) => void} */
61+
var fn2 = function (a, b) { };
62+
exports.fn2 = fn2;
63+
/**
64+
* @satisfies {(a: string, ...args: never) => void}
65+
* @param {string} a
66+
*/
67+
var fn3 = function (a, b) { };
68+
exports.fn3 = fn3;
69+
/**
70+
* @satisfies {(a: string, ...args: never) => void}
71+
* @param {string} a
72+
* @param {number} b
73+
*/
74+
var fn4 = function (a, b) { };
75+
exports.fn4 = fn4;
76+
/**
77+
* @satisfies {(a: string, ...args: number[]) => void}
78+
* @param {string} a
79+
* @param {string} b
80+
*/
81+
var fn5 = function (a, b) { };
82+
exports.fn5 = fn5;
83+
/**
84+
* @satisfies {(a: string, ...args: number[]) => void}
85+
* @param {string} a
86+
* @param {string | number} b
87+
*/
88+
var fn6 = function (a, b) { };
89+
exports.fn6 = fn6;
90+
/** @satisfies {(uuid: string) => void} */
91+
function fn7(uuid) { }
92+
exports.fn7 = fn7;
93+
94+
95+
//// [a.d.ts]
96+
/** @satisfies {(uuid: string) => void} */
97+
export function fn7(uuid: any): void;
98+
export function fn1(uuid: string): void;
99+
/** @typedef {Parameters<typeof fn1>} Foo */
100+
/** @type Foo */
101+
export const v1: [uuid: string];
102+
/** @type Foo */
103+
export const v2: [uuid: string];
104+
export function fn2(a: string, b: never): void;
105+
export function fn3(a: string, b: never): void;
106+
export function fn4(a: string, b: number): void;
107+
export function fn5(a: string, b: string): void;
108+
export function fn6(a: string, b: string | number): void;
109+
export type Foo = Parameters<typeof fn1>;
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
//// [tests/cases/conformance/jsdoc/checkJsdocSatisfiesTag15.ts] ////
2+
3+
=== /a.js ===
4+
/** @satisfies {(uuid: string) => void} */
5+
export const fn1 = uuid => {};
6+
>fn1 : Symbol(fn1, Decl(a.js, 1, 12))
7+
>uuid : Symbol(uuid, Decl(a.js, 1, 18))
8+
9+
/** @typedef {Parameters<typeof fn1>} Foo */
10+
11+
/** @type Foo */
12+
export const v1 = ['abc'];
13+
>v1 : Symbol(v1, Decl(a.js, 6, 12))
14+
15+
/** @type Foo */
16+
export const v2 = [123]; // error
17+
>v2 : Symbol(v2, Decl(a.js, 8, 12))
18+
19+
/** @satisfies {(a: string, ...args: never) => void} */
20+
export const fn2 = (a, b) => {};
21+
>fn2 : Symbol(fn2, Decl(a.js, 11, 12))
22+
>a : Symbol(a, Decl(a.js, 11, 20))
23+
>b : Symbol(b, Decl(a.js, 11, 22))
24+
25+
/**
26+
* @satisfies {(a: string, ...args: never) => void}
27+
* @param {string} a
28+
*/
29+
export const fn3 = (a, b) => {};
30+
>fn3 : Symbol(fn3, Decl(a.js, 17, 12))
31+
>a : Symbol(a, Decl(a.js, 17, 20))
32+
>b : Symbol(b, Decl(a.js, 17, 22))
33+
34+
/**
35+
* @satisfies {(a: string, ...args: never) => void}
36+
* @param {string} a
37+
* @param {number} b
38+
*/
39+
export const fn4 = (a, b) => {};
40+
>fn4 : Symbol(fn4, Decl(a.js, 24, 12))
41+
>a : Symbol(a, Decl(a.js, 24, 20))
42+
>b : Symbol(b, Decl(a.js, 24, 22))
43+
44+
/**
45+
* @satisfies {(a: string, ...args: number[]) => void}
46+
* @param {string} a
47+
* @param {string} b
48+
*/
49+
export const fn5 = (a, b) => {};
50+
>fn5 : Symbol(fn5, Decl(a.js, 31, 12))
51+
>a : Symbol(a, Decl(a.js, 31, 20))
52+
>b : Symbol(b, Decl(a.js, 31, 22))
53+
54+
/**
55+
* @satisfies {(a: string, ...args: number[]) => void}
56+
* @param {string} a
57+
* @param {string | number} b
58+
*/
59+
export const fn6 = (a, b) => {};
60+
>fn6 : Symbol(fn6, Decl(a.js, 38, 12))
61+
>a : Symbol(a, Decl(a.js, 38, 20))
62+
>b : Symbol(b, Decl(a.js, 38, 22))
63+
64+
/** @satisfies {(uuid: string) => void} */
65+
export function fn7(uuid) {}
66+
>fn7 : Symbol(fn7, Decl(a.js, 38, 32))
67+
>uuid : Symbol(uuid, Decl(a.js, 41, 20))
68+
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
//// [tests/cases/conformance/jsdoc/checkJsdocSatisfiesTag15.ts] ////
2+
3+
=== /a.js ===
4+
/** @satisfies {(uuid: string) => void} */
5+
export const fn1 = uuid => {};
6+
>fn1 : (uuid: string) => void
7+
>uuid => {} : (uuid: string) => void
8+
>uuid : string
9+
10+
/** @typedef {Parameters<typeof fn1>} Foo */
11+
12+
/** @type Foo */
13+
export const v1 = ['abc'];
14+
>v1 : [uuid: string]
15+
>['abc'] : [string]
16+
>'abc' : "abc"
17+
18+
/** @type Foo */
19+
export const v2 = [123]; // error
20+
>v2 : [uuid: string]
21+
>[123] : [number]
22+
>123 : 123
23+
24+
/** @satisfies {(a: string, ...args: never) => void} */
25+
export const fn2 = (a, b) => {};
26+
>fn2 : (a: string, b: never) => void
27+
>(a, b) => {} : (a: string, b: never) => void
28+
>a : string
29+
>b : never
30+
31+
/**
32+
* @satisfies {(a: string, ...args: never) => void}
33+
* @param {string} a
34+
*/
35+
export const fn3 = (a, b) => {};
36+
>fn3 : (a: string, b: never) => void
37+
>(a, b) => {} : (a: string, b: never) => void
38+
>a : string
39+
>b : never
40+
41+
/**
42+
* @satisfies {(a: string, ...args: never) => void}
43+
* @param {string} a
44+
* @param {number} b
45+
*/
46+
export const fn4 = (a, b) => {};
47+
>fn4 : (a: string, b: number) => void
48+
>(a, b) => {} : (a: string, b: number) => void
49+
>a : string
50+
>b : number
51+
52+
/**
53+
* @satisfies {(a: string, ...args: number[]) => void}
54+
* @param {string} a
55+
* @param {string} b
56+
*/
57+
export const fn5 = (a, b) => {};
58+
>fn5 : (a: string, b: string) => void
59+
>(a, b) => {} : (a: string, b: string) => void
60+
>a : string
61+
>b : string
62+
63+
/**
64+
* @satisfies {(a: string, ...args: number[]) => void}
65+
* @param {string} a
66+
* @param {string | number} b
67+
*/
68+
export const fn6 = (a, b) => {};
69+
>fn6 : (a: string, b: string | number) => void
70+
>(a, b) => {} : (a: string, b: string | number) => void
71+
>a : string
72+
>b : string | number
73+
74+
/** @satisfies {(uuid: string) => void} */
75+
export function fn7(uuid) {}
76+
>fn7 : (uuid: any) => void
77+
>uuid : any
78+
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// @strict: true
2+
// @allowJS: true
3+
// @checkJs: true
4+
// @declaration: true
5+
// @outDir: lib
6+
7+
// @filename: /a.js
8+
9+
/** @satisfies {(uuid: string) => void} */
10+
export const fn1 = uuid => {};
11+
12+
/** @typedef {Parameters<typeof fn1>} Foo */
13+
14+
/** @type Foo */
15+
export const v1 = ['abc'];
16+
/** @type Foo */
17+
export const v2 = [123]; // error
18+
19+
/** @satisfies {(a: string, ...args: never) => void} */
20+
export const fn2 = (a, b) => {};
21+
22+
/**
23+
* @satisfies {(a: string, ...args: never) => void}
24+
* @param {string} a
25+
*/
26+
export const fn3 = (a, b) => {};
27+
28+
/**
29+
* @satisfies {(a: string, ...args: never) => void}
30+
* @param {string} a
31+
* @param {number} b
32+
*/
33+
export const fn4 = (a, b) => {};
34+
35+
/**
36+
* @satisfies {(a: string, ...args: number[]) => void}
37+
* @param {string} a
38+
* @param {string} b
39+
*/
40+
export const fn5 = (a, b) => {};
41+
42+
/**
43+
* @satisfies {(a: string, ...args: number[]) => void}
44+
* @param {string} a
45+
* @param {string | number} b
46+
*/
47+
export const fn6 = (a, b) => {};
48+
49+
/** @satisfies {(uuid: string) => void} */
50+
export function fn7(uuid) {}

0 commit comments

Comments
 (0)