From 9a0c1d62cb96eaf57131c8e58a88db84d506259d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sun, 31 Dec 2023 00:28:20 +0100 Subject: [PATCH 1/2] Treat contextually typed functions in JS files as typed --- src/compiler/checker.ts | 3 +- .../checkJsdocSatisfiesTag15.errors.txt | 59 ++++++++++ .../reference/checkJsdocSatisfiesTag15.js | 109 ++++++++++++++++++ .../checkJsdocSatisfiesTag15.symbols | 68 +++++++++++ .../reference/checkJsdocSatisfiesTag15.types | 78 +++++++++++++ .../jsdoc/checkJsdocSatisfiesTag15.ts | 50 ++++++++ 6 files changed, 366 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/checkJsdocSatisfiesTag15.errors.txt create mode 100644 tests/baselines/reference/checkJsdocSatisfiesTag15.js create mode 100644 tests/baselines/reference/checkJsdocSatisfiesTag15.symbols create mode 100644 tests/baselines/reference/checkJsdocSatisfiesTag15.types create mode 100644 tests/cases/conformance/jsdoc/checkJsdocSatisfiesTag15.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3d4f288f1293b..7387b86619749 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -15107,7 +15107,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { isInJSFile(declaration) && isValueSignatureDeclaration(declaration) && !hasJSDocParameterTags(declaration) && - !getJSDocType(declaration); + !getJSDocType(declaration) && + !getContextualSignatureForFunctionLikeDeclaration(declaration); if (isUntypedSignatureInJSFile) { flags |= SignatureFlags.IsUntypedSignatureInJSFile; } diff --git a/tests/baselines/reference/checkJsdocSatisfiesTag15.errors.txt b/tests/baselines/reference/checkJsdocSatisfiesTag15.errors.txt new file mode 100644 index 0000000000000..40ceaa21a4e46 --- /dev/null +++ b/tests/baselines/reference/checkJsdocSatisfiesTag15.errors.txt @@ -0,0 +1,59 @@ +/a.js(9,20): error TS2322: Type 'number' is not assignable to type 'string'. +/a.js(28,5): error TS1360: Type '(a: string, b: string) => void' does not satisfy the expected type '(a: string, ...args: number[]) => void'. + Types of parameters 'b' and 'args' are incompatible. + Type 'number' is not assignable to type 'string'. +/a.js(42,21): error TS7006: Parameter 'uuid' implicitly has an 'any' type. + + +==== /a.js (3 errors) ==== + /** @satisfies {(uuid: string) => void} */ + export const fn1 = uuid => {}; + + /** @typedef {Parameters} Foo */ + + /** @type Foo */ + export const v1 = ['abc']; + /** @type Foo */ + export const v2 = [123]; // error + ~~~ +!!! error TS2322: Type 'number' is not assignable to type 'string'. + + /** @satisfies {(a: string, ...args: never) => void} */ + export const fn2 = (a, b) => {}; + + /** + * @satisfies {(a: string, ...args: never) => void} + * @param {string} a + */ + export const fn3 = (a, b) => {}; + + /** + * @satisfies {(a: string, ...args: never) => void} + * @param {string} a + * @param {number} b + */ + export const fn4 = (a, b) => {}; + + /** + * @satisfies {(a: string, ...args: number[]) => void} + ~~~~~~~~~ +!!! error TS1360: Type '(a: string, b: string) => void' does not satisfy the expected type '(a: string, ...args: number[]) => void'. +!!! error TS1360: Types of parameters 'b' and 'args' are incompatible. +!!! error TS1360: Type 'number' is not assignable to type 'string'. + * @param {string} a + * @param {string} b + */ + export const fn5 = (a, b) => {}; + + /** + * @satisfies {(a: string, ...args: number[]) => void} + * @param {string} a + * @param {string | number} b + */ + export const fn6 = (a, b) => {}; + + /** @satisfies {(uuid: string) => void} */ + export function fn7(uuid) {} + ~~~~ +!!! error TS7006: Parameter 'uuid' implicitly has an 'any' type. + \ No newline at end of file diff --git a/tests/baselines/reference/checkJsdocSatisfiesTag15.js b/tests/baselines/reference/checkJsdocSatisfiesTag15.js new file mode 100644 index 0000000000000..72cf641f949b2 --- /dev/null +++ b/tests/baselines/reference/checkJsdocSatisfiesTag15.js @@ -0,0 +1,109 @@ +//// [tests/cases/conformance/jsdoc/checkJsdocSatisfiesTag15.ts] //// + +//// [a.js] +/** @satisfies {(uuid: string) => void} */ +export const fn1 = uuid => {}; + +/** @typedef {Parameters} Foo */ + +/** @type Foo */ +export const v1 = ['abc']; +/** @type Foo */ +export const v2 = [123]; // error + +/** @satisfies {(a: string, ...args: never) => void} */ +export const fn2 = (a, b) => {}; + +/** + * @satisfies {(a: string, ...args: never) => void} + * @param {string} a + */ +export const fn3 = (a, b) => {}; + +/** + * @satisfies {(a: string, ...args: never) => void} + * @param {string} a + * @param {number} b + */ +export const fn4 = (a, b) => {}; + +/** + * @satisfies {(a: string, ...args: number[]) => void} + * @param {string} a + * @param {string} b + */ +export const fn5 = (a, b) => {}; + +/** + * @satisfies {(a: string, ...args: number[]) => void} + * @param {string} a + * @param {string | number} b + */ +export const fn6 = (a, b) => {}; + +/** @satisfies {(uuid: string) => void} */ +export function fn7(uuid) {} + + +//// [a.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.fn7 = exports.fn6 = exports.fn5 = exports.fn4 = exports.fn3 = exports.fn2 = exports.v2 = exports.v1 = exports.fn1 = void 0; +/** @satisfies {(uuid: string) => void} */ +var fn1 = function (uuid) { }; +exports.fn1 = fn1; +/** @typedef {Parameters} Foo */ +/** @type Foo */ +exports.v1 = ['abc']; +/** @type Foo */ +exports.v2 = [123]; // error +/** @satisfies {(a: string, ...args: never) => void} */ +var fn2 = function (a, b) { }; +exports.fn2 = fn2; +/** + * @satisfies {(a: string, ...args: never) => void} + * @param {string} a + */ +var fn3 = function (a, b) { }; +exports.fn3 = fn3; +/** + * @satisfies {(a: string, ...args: never) => void} + * @param {string} a + * @param {number} b + */ +var fn4 = function (a, b) { }; +exports.fn4 = fn4; +/** + * @satisfies {(a: string, ...args: number[]) => void} + * @param {string} a + * @param {string} b + */ +var fn5 = function (a, b) { }; +exports.fn5 = fn5; +/** + * @satisfies {(a: string, ...args: number[]) => void} + * @param {string} a + * @param {string | number} b + */ +var fn6 = function (a, b) { }; +exports.fn6 = fn6; +/** @satisfies {(uuid: string) => void} */ +function fn7(uuid) { } +exports.fn7 = fn7; + + +//// [a.d.ts] +/** @satisfies {(uuid: string) => void} */ +export function fn7(uuid: any): void; +export function fn1(uuid: string): void; +/** @typedef {Parameters} Foo */ +/** @type Foo */ +export const v1: [uuid: string]; +/** @type Foo */ +export const v2: [uuid: string]; +export function fn2(a: string, b: never): void; +export function fn3(a: string, b: never): void; +export function fn4(a: string, b: number): void; +export function fn5(a: string, b: string): void; +export function fn6(a: string, b: string | number): void; +export type Foo = Parameters; diff --git a/tests/baselines/reference/checkJsdocSatisfiesTag15.symbols b/tests/baselines/reference/checkJsdocSatisfiesTag15.symbols new file mode 100644 index 0000000000000..9c1be899b6068 --- /dev/null +++ b/tests/baselines/reference/checkJsdocSatisfiesTag15.symbols @@ -0,0 +1,68 @@ +//// [tests/cases/conformance/jsdoc/checkJsdocSatisfiesTag15.ts] //// + +=== /a.js === +/** @satisfies {(uuid: string) => void} */ +export const fn1 = uuid => {}; +>fn1 : Symbol(fn1, Decl(a.js, 1, 12)) +>uuid : Symbol(uuid, Decl(a.js, 1, 18)) + +/** @typedef {Parameters} Foo */ + +/** @type Foo */ +export const v1 = ['abc']; +>v1 : Symbol(v1, Decl(a.js, 6, 12)) + +/** @type Foo */ +export const v2 = [123]; // error +>v2 : Symbol(v2, Decl(a.js, 8, 12)) + +/** @satisfies {(a: string, ...args: never) => void} */ +export const fn2 = (a, b) => {}; +>fn2 : Symbol(fn2, Decl(a.js, 11, 12)) +>a : Symbol(a, Decl(a.js, 11, 20)) +>b : Symbol(b, Decl(a.js, 11, 22)) + +/** + * @satisfies {(a: string, ...args: never) => void} + * @param {string} a + */ +export const fn3 = (a, b) => {}; +>fn3 : Symbol(fn3, Decl(a.js, 17, 12)) +>a : Symbol(a, Decl(a.js, 17, 20)) +>b : Symbol(b, Decl(a.js, 17, 22)) + +/** + * @satisfies {(a: string, ...args: never) => void} + * @param {string} a + * @param {number} b + */ +export const fn4 = (a, b) => {}; +>fn4 : Symbol(fn4, Decl(a.js, 24, 12)) +>a : Symbol(a, Decl(a.js, 24, 20)) +>b : Symbol(b, Decl(a.js, 24, 22)) + +/** + * @satisfies {(a: string, ...args: number[]) => void} + * @param {string} a + * @param {string} b + */ +export const fn5 = (a, b) => {}; +>fn5 : Symbol(fn5, Decl(a.js, 31, 12)) +>a : Symbol(a, Decl(a.js, 31, 20)) +>b : Symbol(b, Decl(a.js, 31, 22)) + +/** + * @satisfies {(a: string, ...args: number[]) => void} + * @param {string} a + * @param {string | number} b + */ +export const fn6 = (a, b) => {}; +>fn6 : Symbol(fn6, Decl(a.js, 38, 12)) +>a : Symbol(a, Decl(a.js, 38, 20)) +>b : Symbol(b, Decl(a.js, 38, 22)) + +/** @satisfies {(uuid: string) => void} */ +export function fn7(uuid) {} +>fn7 : Symbol(fn7, Decl(a.js, 38, 32)) +>uuid : Symbol(uuid, Decl(a.js, 41, 20)) + diff --git a/tests/baselines/reference/checkJsdocSatisfiesTag15.types b/tests/baselines/reference/checkJsdocSatisfiesTag15.types new file mode 100644 index 0000000000000..3bf41566b20b1 --- /dev/null +++ b/tests/baselines/reference/checkJsdocSatisfiesTag15.types @@ -0,0 +1,78 @@ +//// [tests/cases/conformance/jsdoc/checkJsdocSatisfiesTag15.ts] //// + +=== /a.js === +/** @satisfies {(uuid: string) => void} */ +export const fn1 = uuid => {}; +>fn1 : (uuid: string) => void +>uuid => {} : (uuid: string) => void +>uuid : string + +/** @typedef {Parameters} Foo */ + +/** @type Foo */ +export const v1 = ['abc']; +>v1 : [uuid: string] +>['abc'] : [string] +>'abc' : "abc" + +/** @type Foo */ +export const v2 = [123]; // error +>v2 : [uuid: string] +>[123] : [number] +>123 : 123 + +/** @satisfies {(a: string, ...args: never) => void} */ +export const fn2 = (a, b) => {}; +>fn2 : (a: string, b: never) => void +>(a, b) => {} : (a: string, b: never) => void +>a : string +>b : never + +/** + * @satisfies {(a: string, ...args: never) => void} + * @param {string} a + */ +export const fn3 = (a, b) => {}; +>fn3 : (a: string, b: never) => void +>(a, b) => {} : (a: string, b: never) => void +>a : string +>b : never + +/** + * @satisfies {(a: string, ...args: never) => void} + * @param {string} a + * @param {number} b + */ +export const fn4 = (a, b) => {}; +>fn4 : (a: string, b: number) => void +>(a, b) => {} : (a: string, b: number) => void +>a : string +>b : number + +/** + * @satisfies {(a: string, ...args: number[]) => void} + * @param {string} a + * @param {string} b + */ +export const fn5 = (a, b) => {}; +>fn5 : (a: string, b: string) => void +>(a, b) => {} : (a: string, b: string) => void +>a : string +>b : string + +/** + * @satisfies {(a: string, ...args: number[]) => void} + * @param {string} a + * @param {string | number} b + */ +export const fn6 = (a, b) => {}; +>fn6 : (a: string, b: string | number) => void +>(a, b) => {} : (a: string, b: string | number) => void +>a : string +>b : string | number + +/** @satisfies {(uuid: string) => void} */ +export function fn7(uuid) {} +>fn7 : (uuid: any) => void +>uuid : any + diff --git a/tests/cases/conformance/jsdoc/checkJsdocSatisfiesTag15.ts b/tests/cases/conformance/jsdoc/checkJsdocSatisfiesTag15.ts new file mode 100644 index 0000000000000..172f7ba2ed61b --- /dev/null +++ b/tests/cases/conformance/jsdoc/checkJsdocSatisfiesTag15.ts @@ -0,0 +1,50 @@ +// @strict: true +// @allowJS: true +// @checkJs: true +// @declaration: true +// @outDir: lib + +// @filename: /a.js + +/** @satisfies {(uuid: string) => void} */ +export const fn1 = uuid => {}; + +/** @typedef {Parameters} Foo */ + +/** @type Foo */ +export const v1 = ['abc']; +/** @type Foo */ +export const v2 = [123]; // error + +/** @satisfies {(a: string, ...args: never) => void} */ +export const fn2 = (a, b) => {}; + +/** + * @satisfies {(a: string, ...args: never) => void} + * @param {string} a + */ +export const fn3 = (a, b) => {}; + +/** + * @satisfies {(a: string, ...args: never) => void} + * @param {string} a + * @param {number} b + */ +export const fn4 = (a, b) => {}; + +/** + * @satisfies {(a: string, ...args: number[]) => void} + * @param {string} a + * @param {string} b + */ +export const fn5 = (a, b) => {}; + +/** + * @satisfies {(a: string, ...args: number[]) => void} + * @param {string} a + * @param {string | number} b + */ +export const fn6 = (a, b) => {}; + +/** @satisfies {(uuid: string) => void} */ +export function fn7(uuid) {} From 3e6140c57b95f6b1a48d7c8bb0808111f05d47e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Mon, 26 Aug 2024 18:27:42 +0200 Subject: [PATCH 2/2] update baseline --- .../reference/checkJsdocSatisfiesTag15.js | 8 ++--- .../reference/checkJsdocSatisfiesTag15.types | 31 +++++++++++++++++++ 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/tests/baselines/reference/checkJsdocSatisfiesTag15.js b/tests/baselines/reference/checkJsdocSatisfiesTag15.js index 72cf641f949b2..9bf48fb0c473b 100644 --- a/tests/baselines/reference/checkJsdocSatisfiesTag15.js +++ b/tests/baselines/reference/checkJsdocSatisfiesTag15.js @@ -48,7 +48,8 @@ export function fn7(uuid) {} //// [a.js] "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.fn7 = exports.fn6 = exports.fn5 = exports.fn4 = exports.fn3 = exports.fn2 = exports.v2 = exports.v1 = exports.fn1 = void 0; +exports.fn6 = exports.fn5 = exports.fn4 = exports.fn3 = exports.fn2 = exports.v2 = exports.v1 = exports.fn1 = void 0; +exports.fn7 = fn7; /** @satisfies {(uuid: string) => void} */ var fn1 = function (uuid) { }; exports.fn1 = fn1; @@ -89,7 +90,6 @@ var fn6 = function (a, b) { }; exports.fn6 = fn6; /** @satisfies {(uuid: string) => void} */ function fn7(uuid) { } -exports.fn7 = fn7; //// [a.d.ts] @@ -98,9 +98,9 @@ export function fn7(uuid: any): void; export function fn1(uuid: string): void; /** @typedef {Parameters} Foo */ /** @type Foo */ -export const v1: [uuid: string]; +export const v1: Foo; /** @type Foo */ -export const v2: [uuid: string]; +export const v2: Foo; export function fn2(a: string, b: never): void; export function fn3(a: string, b: never): void; export function fn4(a: string, b: number): void; diff --git a/tests/baselines/reference/checkJsdocSatisfiesTag15.types b/tests/baselines/reference/checkJsdocSatisfiesTag15.types index 3bf41566b20b1..302dfda0901df 100644 --- a/tests/baselines/reference/checkJsdocSatisfiesTag15.types +++ b/tests/baselines/reference/checkJsdocSatisfiesTag15.types @@ -4,29 +4,42 @@ /** @satisfies {(uuid: string) => void} */ export const fn1 = uuid => {}; >fn1 : (uuid: string) => void +> : ^ ^^^^^^^^^^^^^^^^^ >uuid => {} : (uuid: string) => void +> : ^ ^^^^^^^^^^^^^^^^^ >uuid : string +> : ^^^^^^ /** @typedef {Parameters} Foo */ /** @type Foo */ export const v1 = ['abc']; >v1 : [uuid: string] +> : ^^^^^^^^^^^^^^ >['abc'] : [string] +> : ^^^^^^^^ >'abc' : "abc" +> : ^^^^^ /** @type Foo */ export const v2 = [123]; // error >v2 : [uuid: string] +> : ^^^^^^^^^^^^^^ >[123] : [number] +> : ^^^^^^^^ >123 : 123 +> : ^^^ /** @satisfies {(a: string, ...args: never) => void} */ export const fn2 = (a, b) => {}; >fn2 : (a: string, b: never) => void +> : ^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^^ >(a, b) => {} : (a: string, b: never) => void +> : ^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^^ >a : string +> : ^^^^^^ >b : never +> : ^^^^^ /** * @satisfies {(a: string, ...args: never) => void} @@ -34,9 +47,13 @@ export const fn2 = (a, b) => {}; */ export const fn3 = (a, b) => {}; >fn3 : (a: string, b: never) => void +> : ^ ^^ ^^ ^^^^^^^^^^^^^^^^ >(a, b) => {} : (a: string, b: never) => void +> : ^ ^^ ^^ ^^^^^^^^^^^^^^^^ >a : string +> : ^^^^^^ >b : never +> : ^^^^^ /** * @satisfies {(a: string, ...args: never) => void} @@ -45,9 +62,13 @@ export const fn3 = (a, b) => {}; */ export const fn4 = (a, b) => {}; >fn4 : (a: string, b: number) => void +> : ^ ^^ ^^ ^^ ^^^^^^^^^ >(a, b) => {} : (a: string, b: number) => void +> : ^ ^^ ^^ ^^ ^^^^^^^^^ >a : string +> : ^^^^^^ >b : number +> : ^^^^^^ /** * @satisfies {(a: string, ...args: number[]) => void} @@ -56,9 +77,13 @@ export const fn4 = (a, b) => {}; */ export const fn5 = (a, b) => {}; >fn5 : (a: string, b: string) => void +> : ^ ^^ ^^ ^^ ^^^^^^^^^ >(a, b) => {} : (a: string, b: string) => void +> : ^ ^^ ^^ ^^ ^^^^^^^^^ >a : string +> : ^^^^^^ >b : string +> : ^^^^^^ /** * @satisfies {(a: string, ...args: number[]) => void} @@ -67,12 +92,18 @@ export const fn5 = (a, b) => {}; */ export const fn6 = (a, b) => {}; >fn6 : (a: string, b: string | number) => void +> : ^ ^^ ^^ ^^ ^^^^^^^^^ >(a, b) => {} : (a: string, b: string | number) => void +> : ^ ^^ ^^ ^^ ^^^^^^^^^ >a : string +> : ^^^^^^ >b : string | number +> : ^^^^^^^^^^^^^^^ /** @satisfies {(uuid: string) => void} */ export function fn7(uuid) {} >fn7 : (uuid: any) => void +> : ^ ^^^^^^^^^^^^^^ >uuid : any +> : ^^^