From 0cd58251a2c0c448e14b4ae2423b8dea03490226 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Fri, 17 Jun 2022 15:04:25 -0700 Subject: [PATCH 1/4] Ensure resolved signature is cached before processing call errors --- src/compiler/checker.ts | 7 +- .../circularResolvedSignature.errors.txt | 29 ++++++++ .../reference/circularResolvedSignature.js | 41 ++++++++++++ .../circularResolvedSignature.symbols | 58 ++++++++++++++++ .../reference/circularResolvedSignature.types | 67 +++++++++++++++++++ ...lWithGenericSignatureArguments3.errors.txt | 12 ++-- ...icCallWithGenericSignatureArguments3.types | 16 ++--- .../compiler/circularResolvedSignature.ts | 15 +++++ 8 files changed, 230 insertions(+), 15 deletions(-) create mode 100644 tests/baselines/reference/circularResolvedSignature.errors.txt create mode 100644 tests/baselines/reference/circularResolvedSignature.js create mode 100644 tests/baselines/reference/circularResolvedSignature.symbols create mode 100644 tests/baselines/reference/circularResolvedSignature.types create mode 100644 tests/cases/compiler/circularResolvedSignature.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6b45b23c1e819..272675bb4e81a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -30732,11 +30732,16 @@ namespace ts { return result; } + result = getCandidateForOverloadFailure(node, candidates, args, !!candidatesOutArray, checkMode); + // No signatures were applicable. Now report errors based on the last applicable signature with // no arguments excluded from assignability checks. // If candidate is undefined, it means that no candidates had a suitable arity. In that case, // skip the checkApplicableSignature check. if (reportErrors) { + // TODO(jakebailey): explain this, and maybe move getResolvedSignature's caching here. + getNodeLinks(node).resolvedSignature = result; + if (candidatesForArgumentError) { if (candidatesForArgumentError.length === 1 || candidatesForArgumentError.length > 3) { const last = candidatesForArgumentError[candidatesForArgumentError.length - 1]; @@ -30822,7 +30827,7 @@ namespace ts { } } - return getCandidateForOverloadFailure(node, candidates, args, !!candidatesOutArray, checkMode); + return result; function addImplementationSuccessElaboration(failed: Signature, diagnostic: Diagnostic) { const oldCandidatesForArgumentError = candidatesForArgumentError; diff --git a/tests/baselines/reference/circularResolvedSignature.errors.txt b/tests/baselines/reference/circularResolvedSignature.errors.txt new file mode 100644 index 0000000000000..c060bb67d040c --- /dev/null +++ b/tests/baselines/reference/circularResolvedSignature.errors.txt @@ -0,0 +1,29 @@ +tests/cases/compiler/circularResolvedSignature.ts(11,9): error TS2322: Type 'string' is not assignable to type 'number'. +tests/cases/compiler/circularResolvedSignature.ts(12,32): error TS2345: Argument of type '(prev: any) => any' is not assignable to parameter of type 'Readonly<{ value: number; foo: (arg: any) => void; bar: (arg: any) => void; }>'. +tests/cases/compiler/circularResolvedSignature.ts(13,32): error TS2345: Argument of type '(prev: any) => any' is not assignable to parameter of type 'Readonly<{ value: number; foo: (arg: any) => void; bar: (arg: any) => void; }>'. + + +==== tests/cases/compiler/circularResolvedSignature.ts (3 errors) ==== + declare function useState(initialState: (() => S)): [S, (s: S) => void]; + + type Data = Readonly<{ + value: number; + foo: (arg: any) => void; + bar: (arg: any) => void; + }>; + + export function Component() { + const [state, setState] = useState(() => ({ + value: "string", // this should be a number + ~~~~~ +!!! error TS2322: Type 'string' is not assignable to type 'number'. +!!! related TS6500 tests/cases/compiler/circularResolvedSignature.ts:4:5: The expected type comes from property 'value' which is declared here on type 'Readonly<{ value: number; foo: (arg: any) => void; bar: (arg: any) => void; }>' + foo: (arg) => setState((prev) => ({ ...prev, arg })), + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2345: Argument of type '(prev: any) => any' is not assignable to parameter of type 'Readonly<{ value: number; foo: (arg: any) => void; bar: (arg: any) => void; }>'. + bar: (arg) => setState((prev) => ({ ...prev, arg })), + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2345: Argument of type '(prev: any) => any' is not assignable to parameter of type 'Readonly<{ value: number; foo: (arg: any) => void; bar: (arg: any) => void; }>'. + })); + } + \ No newline at end of file diff --git a/tests/baselines/reference/circularResolvedSignature.js b/tests/baselines/reference/circularResolvedSignature.js new file mode 100644 index 0000000000000..bd23f228df41f --- /dev/null +++ b/tests/baselines/reference/circularResolvedSignature.js @@ -0,0 +1,41 @@ +//// [circularResolvedSignature.ts] +declare function useState(initialState: (() => S)): [S, (s: S) => void]; + +type Data = Readonly<{ + value: number; + foo: (arg: any) => void; + bar: (arg: any) => void; +}>; + +export function Component() { + const [state, setState] = useState(() => ({ + value: "string", // this should be a number + foo: (arg) => setState((prev) => ({ ...prev, arg })), + bar: (arg) => setState((prev) => ({ ...prev, arg })), + })); +} + + +//// [circularResolvedSignature.js] +"use strict"; +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +exports.__esModule = true; +exports.Component = void 0; +function Component() { + var _a = useState(function () { return ({ + value: "string", + foo: function (arg) { return setState(function (prev) { return (__assign(__assign({}, prev), { arg: arg })); }); }, + bar: function (arg) { return setState(function (prev) { return (__assign(__assign({}, prev), { arg: arg })); }); } + }); }), state = _a[0], setState = _a[1]; +} +exports.Component = Component; diff --git a/tests/baselines/reference/circularResolvedSignature.symbols b/tests/baselines/reference/circularResolvedSignature.symbols new file mode 100644 index 0000000000000..392bb2535c65b --- /dev/null +++ b/tests/baselines/reference/circularResolvedSignature.symbols @@ -0,0 +1,58 @@ +=== tests/cases/compiler/circularResolvedSignature.ts === +declare function useState(initialState: (() => S)): [S, (s: S) => void]; +>useState : Symbol(useState, Decl(circularResolvedSignature.ts, 0, 0)) +>S : Symbol(S, Decl(circularResolvedSignature.ts, 0, 26)) +>initialState : Symbol(initialState, Decl(circularResolvedSignature.ts, 0, 29)) +>S : Symbol(S, Decl(circularResolvedSignature.ts, 0, 26)) +>S : Symbol(S, Decl(circularResolvedSignature.ts, 0, 26)) +>s : Symbol(s, Decl(circularResolvedSignature.ts, 0, 60)) +>S : Symbol(S, Decl(circularResolvedSignature.ts, 0, 26)) + +type Data = Readonly<{ +>Data : Symbol(Data, Decl(circularResolvedSignature.ts, 0, 75)) +>Readonly : Symbol(Readonly, Decl(lib.es5.d.ts, --, --)) + + value: number; +>value : Symbol(value, Decl(circularResolvedSignature.ts, 2, 22)) + + foo: (arg: any) => void; +>foo : Symbol(foo, Decl(circularResolvedSignature.ts, 3, 18)) +>arg : Symbol(arg, Decl(circularResolvedSignature.ts, 4, 10)) + + bar: (arg: any) => void; +>bar : Symbol(bar, Decl(circularResolvedSignature.ts, 4, 28)) +>arg : Symbol(arg, Decl(circularResolvedSignature.ts, 5, 10)) + +}>; + +export function Component() { +>Component : Symbol(Component, Decl(circularResolvedSignature.ts, 6, 3)) + + const [state, setState] = useState(() => ({ +>state : Symbol(state, Decl(circularResolvedSignature.ts, 9, 11)) +>setState : Symbol(setState, Decl(circularResolvedSignature.ts, 9, 17)) +>useState : Symbol(useState, Decl(circularResolvedSignature.ts, 0, 0)) +>Data : Symbol(Data, Decl(circularResolvedSignature.ts, 0, 75)) + + value: "string", // this should be a number +>value : Symbol(value, Decl(circularResolvedSignature.ts, 9, 53)) + + foo: (arg) => setState((prev) => ({ ...prev, arg })), +>foo : Symbol(foo, Decl(circularResolvedSignature.ts, 10, 24)) +>arg : Symbol(arg, Decl(circularResolvedSignature.ts, 11, 14)) +>setState : Symbol(setState, Decl(circularResolvedSignature.ts, 9, 17)) +>prev : Symbol(prev, Decl(circularResolvedSignature.ts, 11, 32)) +>prev : Symbol(prev, Decl(circularResolvedSignature.ts, 11, 32)) +>arg : Symbol(arg, Decl(circularResolvedSignature.ts, 11, 52)) + + bar: (arg) => setState((prev) => ({ ...prev, arg })), +>bar : Symbol(bar, Decl(circularResolvedSignature.ts, 11, 61)) +>arg : Symbol(arg, Decl(circularResolvedSignature.ts, 12, 14)) +>setState : Symbol(setState, Decl(circularResolvedSignature.ts, 9, 17)) +>prev : Symbol(prev, Decl(circularResolvedSignature.ts, 12, 32)) +>prev : Symbol(prev, Decl(circularResolvedSignature.ts, 12, 32)) +>arg : Symbol(arg, Decl(circularResolvedSignature.ts, 12, 52)) + + })); +} + diff --git a/tests/baselines/reference/circularResolvedSignature.types b/tests/baselines/reference/circularResolvedSignature.types new file mode 100644 index 0000000000000..685eba93908e3 --- /dev/null +++ b/tests/baselines/reference/circularResolvedSignature.types @@ -0,0 +1,67 @@ +=== tests/cases/compiler/circularResolvedSignature.ts === +declare function useState(initialState: (() => S)): [S, (s: S) => void]; +>useState : (initialState: (() => S)) => [S, (s: S) => void] +>initialState : () => S +>s : S + +type Data = Readonly<{ +>Data : Readonly<{ value: number; foo: (arg: any) => void; bar: (arg: any) => void; }> + + value: number; +>value : number + + foo: (arg: any) => void; +>foo : (arg: any) => void +>arg : any + + bar: (arg: any) => void; +>bar : (arg: any) => void +>arg : any + +}>; + +export function Component() { +>Component : () => void + + const [state, setState] = useState(() => ({ +>state : Readonly<{ value: number; foo: (arg: any) => void; bar: (arg: any) => void; }> +>setState : (s: Readonly<{ value: number; foo: (arg: any) => void; bar: (arg: any) => void; }>) => void +>useState(() => ({ value: "string", // this should be a number foo: (arg) => setState((prev) => ({ ...prev, arg })), bar: (arg) => setState((prev) => ({ ...prev, arg })), })) : [Readonly<{ value: number; foo: (arg: any) => void; bar: (arg: any) => void; }>, (s: Readonly<{ value: number; foo: (arg: any) => void; bar: (arg: any) => void; }>) => void] +>useState : (initialState: () => S) => [S, (s: S) => void] +>() => ({ value: "string", // this should be a number foo: (arg) => setState((prev) => ({ ...prev, arg })), bar: (arg) => setState((prev) => ({ ...prev, arg })), }) : () => { value: string; foo: (arg: any) => void; bar: (arg: any) => void; } +>({ value: "string", // this should be a number foo: (arg) => setState((prev) => ({ ...prev, arg })), bar: (arg) => setState((prev) => ({ ...prev, arg })), }) : { value: string; foo: (arg: any) => void; bar: (arg: any) => void; } +>{ value: "string", // this should be a number foo: (arg) => setState((prev) => ({ ...prev, arg })), bar: (arg) => setState((prev) => ({ ...prev, arg })), } : { value: string; foo: (arg: any) => void; bar: (arg: any) => void; } + + value: "string", // this should be a number +>value : string +>"string" : "string" + + foo: (arg) => setState((prev) => ({ ...prev, arg })), +>foo : (arg: any) => void +>(arg) => setState((prev) => ({ ...prev, arg })) : (arg: any) => void +>arg : any +>setState((prev) => ({ ...prev, arg })) : void +>setState : (s: Readonly<{ value: number; foo: (arg: any) => void; bar: (arg: any) => void; }>) => void +>(prev) => ({ ...prev, arg }) : (prev: any) => any +>prev : any +>({ ...prev, arg }) : any +>{ ...prev, arg } : any +>prev : any +>arg : any + + bar: (arg) => setState((prev) => ({ ...prev, arg })), +>bar : (arg: any) => void +>(arg) => setState((prev) => ({ ...prev, arg })) : (arg: any) => void +>arg : any +>setState((prev) => ({ ...prev, arg })) : void +>setState : (s: Readonly<{ value: number; foo: (arg: any) => void; bar: (arg: any) => void; }>) => void +>(prev) => ({ ...prev, arg }) : (prev: any) => any +>prev : any +>({ ...prev, arg }) : any +>{ ...prev, arg } : any +>prev : any +>arg : any + + })); +} + diff --git a/tests/baselines/reference/genericCallWithGenericSignatureArguments3.errors.txt b/tests/baselines/reference/genericCallWithGenericSignatureArguments3.errors.txt index 4c29b010295b6..9a61bc49f37cf 100644 --- a/tests/baselines/reference/genericCallWithGenericSignatureArguments3.errors.txt +++ b/tests/baselines/reference/genericCallWithGenericSignatureArguments3.errors.txt @@ -1,8 +1,8 @@ -tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithGenericSignatureArguments3.ts(32,19): error TS2345: Argument of type '(a1: (y: string) => string) => (n: Object) => number' is not assignable to parameter of type '(x: (a: string) => boolean) => (n: Object) => number'. +tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithGenericSignatureArguments3.ts(32,19): error TS2345: Argument of type '(a1: (y: string) => string) => (n: Object) => 1' is not assignable to parameter of type '(x: (a: string) => boolean) => (n: Object) => 1'. Types of parameters 'a1' and 'x' are incompatible. Type 'boolean' is not assignable to type 'string'. -tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithGenericSignatureArguments3.ts(33,69): error TS2345: Argument of type '(a2: (z: string) => boolean) => number' is not assignable to parameter of type '(x: (z: string) => boolean) => (n: Object) => number'. - Type 'number' is not assignable to type '(n: Object) => number'. +tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithGenericSignatureArguments3.ts(33,69): error TS2345: Argument of type '(a2: (z: string) => boolean) => number' is not assignable to parameter of type '(x: (z: string) => boolean) => (n: Object) => 1'. + Type 'number' is not assignable to type '(n: Object) => 1'. ==== tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithGenericSignatureArguments3.ts (2 errors) ==== @@ -39,10 +39,10 @@ tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithGen var x: (a: string) => boolean; var r11 = foo2(x, (a1: (y: string) => string) => (n: Object) => 1, (a2: (z: string) => string) => 2); // error ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -!!! error TS2345: Argument of type '(a1: (y: string) => string) => (n: Object) => number' is not assignable to parameter of type '(x: (a: string) => boolean) => (n: Object) => number'. +!!! error TS2345: Argument of type '(a1: (y: string) => string) => (n: Object) => 1' is not assignable to parameter of type '(x: (a: string) => boolean) => (n: Object) => 1'. !!! error TS2345: Types of parameters 'a1' and 'x' are incompatible. !!! error TS2345: Type 'boolean' is not assignable to type 'string'. var r12 = foo2(x, (a1: (y: string) => boolean) => (n: Object) => 1, (a2: (z: string) => boolean) => 2); // error ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -!!! error TS2345: Argument of type '(a2: (z: string) => boolean) => number' is not assignable to parameter of type '(x: (z: string) => boolean) => (n: Object) => number'. -!!! error TS2345: Type 'number' is not assignable to type '(n: Object) => number'. \ No newline at end of file +!!! error TS2345: Argument of type '(a2: (z: string) => boolean) => number' is not assignable to parameter of type '(x: (z: string) => boolean) => (n: Object) => 1'. +!!! error TS2345: Type 'number' is not assignable to type '(n: Object) => 1'. \ No newline at end of file diff --git a/tests/baselines/reference/genericCallWithGenericSignatureArguments3.types b/tests/baselines/reference/genericCallWithGenericSignatureArguments3.types index 531225ad6b25f..0e93495a88c68 100644 --- a/tests/baselines/reference/genericCallWithGenericSignatureArguments3.types +++ b/tests/baselines/reference/genericCallWithGenericSignatureArguments3.types @@ -175,14 +175,14 @@ var x: (a: string) => boolean; >a : string var r11 = foo2(x, (a1: (y: string) => string) => (n: Object) => 1, (a2: (z: string) => string) => 2); // error ->r11 : (x: (a: string) => boolean) => (n: Object) => number ->foo2(x, (a1: (y: string) => string) => (n: Object) => 1, (a2: (z: string) => string) => 2) : (x: (a: string) => boolean) => (n: Object) => number +>r11 : (x: (a: string) => boolean) => (n: Object) => 1 +>foo2(x, (a1: (y: string) => string) => (n: Object) => 1, (a2: (z: string) => string) => 2) : (x: (a: string) => boolean) => (n: Object) => 1 >foo2 : (x: T, a: (x: T) => U, b: (x: T) => U) => (x: T) => U >x : (a: string) => boolean ->(a1: (y: string) => string) => (n: Object) => 1 : (a1: (y: string) => string) => (n: Object) => number +>(a1: (y: string) => string) => (n: Object) => 1 : (a1: (y: string) => string) => (n: Object) => 1 >a1 : (y: string) => string >y : string ->(n: Object) => 1 : (n: Object) => number +>(n: Object) => 1 : (n: Object) => 1 >n : Object >1 : 1 >(a2: (z: string) => string) => 2 : (a2: (z: string) => string) => number @@ -191,14 +191,14 @@ var r11 = foo2(x, (a1: (y: string) => string) => (n: Object) => 1, (a2: (z: stri >2 : 2 var r12 = foo2(x, (a1: (y: string) => boolean) => (n: Object) => 1, (a2: (z: string) => boolean) => 2); // error ->r12 : (x: (z: string) => boolean) => (n: Object) => number ->foo2(x, (a1: (y: string) => boolean) => (n: Object) => 1, (a2: (z: string) => boolean) => 2) : (x: (z: string) => boolean) => (n: Object) => number +>r12 : (x: (z: string) => boolean) => (n: Object) => 1 +>foo2(x, (a1: (y: string) => boolean) => (n: Object) => 1, (a2: (z: string) => boolean) => 2) : (x: (z: string) => boolean) => (n: Object) => 1 >foo2 : (x: T, a: (x: T) => U, b: (x: T) => U) => (x: T) => U >x : (a: string) => boolean ->(a1: (y: string) => boolean) => (n: Object) => 1 : (a1: (y: string) => boolean) => (n: Object) => number +>(a1: (y: string) => boolean) => (n: Object) => 1 : (a1: (y: string) => boolean) => (n: Object) => 1 >a1 : (y: string) => boolean >y : string ->(n: Object) => 1 : (n: Object) => number +>(n: Object) => 1 : (n: Object) => 1 >n : Object >1 : 1 >(a2: (z: string) => boolean) => 2 : (a2: (z: string) => boolean) => number diff --git a/tests/cases/compiler/circularResolvedSignature.ts b/tests/cases/compiler/circularResolvedSignature.ts new file mode 100644 index 0000000000000..2b2209a3332a4 --- /dev/null +++ b/tests/cases/compiler/circularResolvedSignature.ts @@ -0,0 +1,15 @@ +declare function useState(initialState: (() => S)): [S, (s: S) => void]; + +type Data = Readonly<{ + value: number; + foo: (arg: any) => void; + bar: (arg: any) => void; +}>; + +export function Component() { + const [state, setState] = useState(() => ({ + value: "string", // this should be a number + foo: (arg) => setState((prev) => ({ ...prev, arg })), + bar: (arg) => setState((prev) => ({ ...prev, arg })), + })); +} From 5641e39836da465d8c9d4d0351bdf47470cfee96 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Fri, 17 Jun 2022 15:16:11 -0700 Subject: [PATCH 2/4] Simplify test and code --- src/compiler/checker.ts | 5 ++-- .../circularResolvedSignature.errors.txt | 12 ++------ .../reference/circularResolvedSignature.js | 19 +++--------- .../circularResolvedSignature.symbols | 14 ++++----- .../reference/circularResolvedSignature.types | 30 +++++++------------ .../compiler/circularResolvedSignature.ts | 4 +-- 6 files changed, 26 insertions(+), 58 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 272675bb4e81a..b9b0099cb7fcc 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -30733,15 +30733,14 @@ namespace ts { } result = getCandidateForOverloadFailure(node, candidates, args, !!candidatesOutArray, checkMode); + // TODO(jakebailey): explain this, and maybe move getResolvedSignature's caching here. + getNodeLinks(node).resolvedSignature = result; // No signatures were applicable. Now report errors based on the last applicable signature with // no arguments excluded from assignability checks. // If candidate is undefined, it means that no candidates had a suitable arity. In that case, // skip the checkApplicableSignature check. if (reportErrors) { - // TODO(jakebailey): explain this, and maybe move getResolvedSignature's caching here. - getNodeLinks(node).resolvedSignature = result; - if (candidatesForArgumentError) { if (candidatesForArgumentError.length === 1 || candidatesForArgumentError.length > 3) { const last = candidatesForArgumentError[candidatesForArgumentError.length - 1]; diff --git a/tests/baselines/reference/circularResolvedSignature.errors.txt b/tests/baselines/reference/circularResolvedSignature.errors.txt index c060bb67d040c..6db1a5eb04f30 100644 --- a/tests/baselines/reference/circularResolvedSignature.errors.txt +++ b/tests/baselines/reference/circularResolvedSignature.errors.txt @@ -1,9 +1,7 @@ tests/cases/compiler/circularResolvedSignature.ts(11,9): error TS2322: Type 'string' is not assignable to type 'number'. -tests/cases/compiler/circularResolvedSignature.ts(12,32): error TS2345: Argument of type '(prev: any) => any' is not assignable to parameter of type 'Readonly<{ value: number; foo: (arg: any) => void; bar: (arg: any) => void; }>'. -tests/cases/compiler/circularResolvedSignature.ts(13,32): error TS2345: Argument of type '(prev: any) => any' is not assignable to parameter of type 'Readonly<{ value: number; foo: (arg: any) => void; bar: (arg: any) => void; }>'. -==== tests/cases/compiler/circularResolvedSignature.ts (3 errors) ==== +==== tests/cases/compiler/circularResolvedSignature.ts (1 errors) ==== declare function useState(initialState: (() => S)): [S, (s: S) => void]; type Data = Readonly<{ @@ -18,12 +16,8 @@ tests/cases/compiler/circularResolvedSignature.ts(13,32): error TS2345: Argument ~~~~~ !!! error TS2322: Type 'string' is not assignable to type 'number'. !!! related TS6500 tests/cases/compiler/circularResolvedSignature.ts:4:5: The expected type comes from property 'value' which is declared here on type 'Readonly<{ value: number; foo: (arg: any) => void; bar: (arg: any) => void; }>' - foo: (arg) => setState((prev) => ({ ...prev, arg })), - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -!!! error TS2345: Argument of type '(prev: any) => any' is not assignable to parameter of type 'Readonly<{ value: number; foo: (arg: any) => void; bar: (arg: any) => void; }>'. - bar: (arg) => setState((prev) => ({ ...prev, arg })), - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -!!! error TS2345: Argument of type '(prev: any) => any' is not assignable to parameter of type 'Readonly<{ value: number; foo: (arg: any) => void; bar: (arg: any) => void; }>'. + foo: (arg) => setState(arg), + bar: (arg) => setState(arg), })); } \ No newline at end of file diff --git a/tests/baselines/reference/circularResolvedSignature.js b/tests/baselines/reference/circularResolvedSignature.js index bd23f228df41f..79f5fe6296b5d 100644 --- a/tests/baselines/reference/circularResolvedSignature.js +++ b/tests/baselines/reference/circularResolvedSignature.js @@ -10,32 +10,21 @@ type Data = Readonly<{ export function Component() { const [state, setState] = useState(() => ({ value: "string", // this should be a number - foo: (arg) => setState((prev) => ({ ...prev, arg })), - bar: (arg) => setState((prev) => ({ ...prev, arg })), + foo: (arg) => setState(arg), + bar: (arg) => setState(arg), })); } //// [circularResolvedSignature.js] "use strict"; -var __assign = (this && this.__assign) || function () { - __assign = Object.assign || function(t) { - for (var s, i = 1, n = arguments.length; i < n; i++) { - s = arguments[i]; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) - t[p] = s[p]; - } - return t; - }; - return __assign.apply(this, arguments); -}; exports.__esModule = true; exports.Component = void 0; function Component() { var _a = useState(function () { return ({ value: "string", - foo: function (arg) { return setState(function (prev) { return (__assign(__assign({}, prev), { arg: arg })); }); }, - bar: function (arg) { return setState(function (prev) { return (__assign(__assign({}, prev), { arg: arg })); }); } + foo: function (arg) { return setState(arg); }, + bar: function (arg) { return setState(arg); } }); }), state = _a[0], setState = _a[1]; } exports.Component = Component; diff --git a/tests/baselines/reference/circularResolvedSignature.symbols b/tests/baselines/reference/circularResolvedSignature.symbols index 392bb2535c65b..01f95cbac07b6 100644 --- a/tests/baselines/reference/circularResolvedSignature.symbols +++ b/tests/baselines/reference/circularResolvedSignature.symbols @@ -37,21 +37,17 @@ export function Component() { value: "string", // this should be a number >value : Symbol(value, Decl(circularResolvedSignature.ts, 9, 53)) - foo: (arg) => setState((prev) => ({ ...prev, arg })), + foo: (arg) => setState(arg), >foo : Symbol(foo, Decl(circularResolvedSignature.ts, 10, 24)) >arg : Symbol(arg, Decl(circularResolvedSignature.ts, 11, 14)) >setState : Symbol(setState, Decl(circularResolvedSignature.ts, 9, 17)) ->prev : Symbol(prev, Decl(circularResolvedSignature.ts, 11, 32)) ->prev : Symbol(prev, Decl(circularResolvedSignature.ts, 11, 32)) ->arg : Symbol(arg, Decl(circularResolvedSignature.ts, 11, 52)) +>arg : Symbol(arg, Decl(circularResolvedSignature.ts, 11, 14)) - bar: (arg) => setState((prev) => ({ ...prev, arg })), ->bar : Symbol(bar, Decl(circularResolvedSignature.ts, 11, 61)) + bar: (arg) => setState(arg), +>bar : Symbol(bar, Decl(circularResolvedSignature.ts, 11, 36)) >arg : Symbol(arg, Decl(circularResolvedSignature.ts, 12, 14)) >setState : Symbol(setState, Decl(circularResolvedSignature.ts, 9, 17)) ->prev : Symbol(prev, Decl(circularResolvedSignature.ts, 12, 32)) ->prev : Symbol(prev, Decl(circularResolvedSignature.ts, 12, 32)) ->arg : Symbol(arg, Decl(circularResolvedSignature.ts, 12, 52)) +>arg : Symbol(arg, Decl(circularResolvedSignature.ts, 12, 14)) })); } diff --git a/tests/baselines/reference/circularResolvedSignature.types b/tests/baselines/reference/circularResolvedSignature.types index 685eba93908e3..23efb99838a5d 100644 --- a/tests/baselines/reference/circularResolvedSignature.types +++ b/tests/baselines/reference/circularResolvedSignature.types @@ -26,40 +26,30 @@ export function Component() { const [state, setState] = useState(() => ({ >state : Readonly<{ value: number; foo: (arg: any) => void; bar: (arg: any) => void; }> >setState : (s: Readonly<{ value: number; foo: (arg: any) => void; bar: (arg: any) => void; }>) => void ->useState(() => ({ value: "string", // this should be a number foo: (arg) => setState((prev) => ({ ...prev, arg })), bar: (arg) => setState((prev) => ({ ...prev, arg })), })) : [Readonly<{ value: number; foo: (arg: any) => void; bar: (arg: any) => void; }>, (s: Readonly<{ value: number; foo: (arg: any) => void; bar: (arg: any) => void; }>) => void] +>useState(() => ({ value: "string", // this should be a number foo: (arg) => setState(arg), bar: (arg) => setState(arg), })) : [Readonly<{ value: number; foo: (arg: any) => void; bar: (arg: any) => void; }>, (s: Readonly<{ value: number; foo: (arg: any) => void; bar: (arg: any) => void; }>) => void] >useState : (initialState: () => S) => [S, (s: S) => void] ->() => ({ value: "string", // this should be a number foo: (arg) => setState((prev) => ({ ...prev, arg })), bar: (arg) => setState((prev) => ({ ...prev, arg })), }) : () => { value: string; foo: (arg: any) => void; bar: (arg: any) => void; } ->({ value: "string", // this should be a number foo: (arg) => setState((prev) => ({ ...prev, arg })), bar: (arg) => setState((prev) => ({ ...prev, arg })), }) : { value: string; foo: (arg: any) => void; bar: (arg: any) => void; } ->{ value: "string", // this should be a number foo: (arg) => setState((prev) => ({ ...prev, arg })), bar: (arg) => setState((prev) => ({ ...prev, arg })), } : { value: string; foo: (arg: any) => void; bar: (arg: any) => void; } +>() => ({ value: "string", // this should be a number foo: (arg) => setState(arg), bar: (arg) => setState(arg), }) : () => { value: string; foo: (arg: any) => void; bar: (arg: any) => void; } +>({ value: "string", // this should be a number foo: (arg) => setState(arg), bar: (arg) => setState(arg), }) : { value: string; foo: (arg: any) => void; bar: (arg: any) => void; } +>{ value: "string", // this should be a number foo: (arg) => setState(arg), bar: (arg) => setState(arg), } : { value: string; foo: (arg: any) => void; bar: (arg: any) => void; } value: "string", // this should be a number >value : string >"string" : "string" - foo: (arg) => setState((prev) => ({ ...prev, arg })), + foo: (arg) => setState(arg), >foo : (arg: any) => void ->(arg) => setState((prev) => ({ ...prev, arg })) : (arg: any) => void +>(arg) => setState(arg) : (arg: any) => void >arg : any ->setState((prev) => ({ ...prev, arg })) : void +>setState(arg) : void >setState : (s: Readonly<{ value: number; foo: (arg: any) => void; bar: (arg: any) => void; }>) => void ->(prev) => ({ ...prev, arg }) : (prev: any) => any ->prev : any ->({ ...prev, arg }) : any ->{ ...prev, arg } : any ->prev : any >arg : any - bar: (arg) => setState((prev) => ({ ...prev, arg })), + bar: (arg) => setState(arg), >bar : (arg: any) => void ->(arg) => setState((prev) => ({ ...prev, arg })) : (arg: any) => void +>(arg) => setState(arg) : (arg: any) => void >arg : any ->setState((prev) => ({ ...prev, arg })) : void +>setState(arg) : void >setState : (s: Readonly<{ value: number; foo: (arg: any) => void; bar: (arg: any) => void; }>) => void ->(prev) => ({ ...prev, arg }) : (prev: any) => any ->prev : any ->({ ...prev, arg }) : any ->{ ...prev, arg } : any ->prev : any >arg : any })); diff --git a/tests/cases/compiler/circularResolvedSignature.ts b/tests/cases/compiler/circularResolvedSignature.ts index 2b2209a3332a4..5a7cf15b00c4b 100644 --- a/tests/cases/compiler/circularResolvedSignature.ts +++ b/tests/cases/compiler/circularResolvedSignature.ts @@ -9,7 +9,7 @@ type Data = Readonly<{ export function Component() { const [state, setState] = useState(() => ({ value: "string", // this should be a number - foo: (arg) => setState((prev) => ({ ...prev, arg })), - bar: (arg) => setState((prev) => ({ ...prev, arg })), + foo: (arg) => setState(arg), + bar: (arg) => setState(arg), })); } From a65ebb2a8f0853ec35d49c7f2ca34df8bf31af85 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Fri, 17 Jun 2022 17:07:29 -0700 Subject: [PATCH 3/4] Add a big comment --- src/compiler/checker.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b9b0099cb7fcc..f175b0db1de07 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -30733,7 +30733,13 @@ namespace ts { } result = getCandidateForOverloadFailure(node, candidates, args, !!candidatesOutArray, checkMode); - // TODO(jakebailey): explain this, and maybe move getResolvedSignature's caching here. + // Preemptively cache the result; getResolvedSignature will do this after we return, but + // we need to ensure that the result is present for the error checks below so that if + // this signature is encountered again, we handle the circularity (rather than producing a + // different result which may produce no errors and assert). Callers of getResolvedSignature + // don't hit this issue becuase they only observe this result after it's had a chance to + // be cached, but the error reporting code below executes before getResolvedSignature sets + // resolvedSignature. getNodeLinks(node).resolvedSignature = result; // No signatures were applicable. Now report errors based on the last applicable signature with From b2935be279a4aa9ec40c92f3f85690ec99bbb4d3 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Mon, 20 Jun 2022 10:04:49 -0700 Subject: [PATCH 4/4] Fix typo --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f175b0db1de07..e46143d147dbb 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -30737,7 +30737,7 @@ namespace ts { // we need to ensure that the result is present for the error checks below so that if // this signature is encountered again, we handle the circularity (rather than producing a // different result which may produce no errors and assert). Callers of getResolvedSignature - // don't hit this issue becuase they only observe this result after it's had a chance to + // don't hit this issue because they only observe this result after it's had a chance to // be cached, but the error reporting code below executes before getResolvedSignature sets // resolvedSignature. getNodeLinks(node).resolvedSignature = result;