-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Incorrect inferred function return type with destructuring assignments #56771
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Destructuring creates a contextual type of IIRC @andrewbranch was working on improvements in this area in the past so maybe he'd have some ideas how a fix for this could look like |
It might also be worth noticing that other cases with the same contextual type of type R = {
A:
| {
a: "A";
}
| null
| undefined;
};
function fn<T extends R>(t: T): T {
return {} as T;
}
function test(): { A: any } {
return fn({ A: { a: "A" } });
// ^? function fn<R>(t: R): R
} |
Wait, then why isn't the call itself an error?! Do you mean to tell me |
Yes, this is roughly how it works under the hood. Arguments during inference are checked multiple times - with different contextual types. So one of the first passes in Later, once the compiler settles~ on the inferred type, it re-checks the same argument in |
Some extra data points. In one of the last PRs that touched this, we can see this test case: declare function pick<O, T extends keyof O>(keys: T[], obj?: O): Pick<O, T>;
const _ = pick(['b'], { a: 'a', b: 'b' }); // T: "b"
const { } = pick(['b'], { a: 'a', b: 'b' }); // T: "b" | "a" ??? (before fix) Both infer declare function pick<O, T extends keyof O>(keys: T[], obj?: O): Pick<O, T>;
const { b } = pick(['b'], { a: 'a', b: 'b' }); // T: "b" It's still ok π If we add a constraint to this object then it still works fine: declare function pick<O extends { a?: "a"; b?: "b" }, T extends keyof O>(
keys: T[],
obj?: O,
): Pick<O, T>;
const { b } = pick(["b"], { a: "a", b: "b" }); // T: "b" but now if we make that constraint nullable it gives us incorrect results: declare function pick<O extends { a?: "a"; b?: "b" } | null, T extends keyof O>(
keys: T[],
obj?: O,
): Pick<O, T>;
const { b } = pick(["b"], { a: "a", b: "b" }); // T: "a" | "b" I assume that this is the same problem but a different manifestation of it. |
π Search Terms
"return type destructure"
"destructuring assignment type"
π Version & Regression Information
β― Playground Link
https://www.typescriptlang.org/play?ts=5.3.2#code/C4TwDgpgBASlC8UDeAoK6oEEBcy0YIENcByTE-dAXygB8oA7AVwBsW6omGATCAMwCWDCNxRUUfLgGNgAgPYMofBgB4AKlAgAPYBB4BnWAD4AFMGxqAlLg14CAJwjAm9xUhqFDagNxiUKAHoAKhQASUUIQikACyhdfWAoKU9oACMIFjkAdwAaKFCSbihPfSYAW2hgaOhHZ1c48Gg5PiVpWQU4uSh0qAADJBxkYigyEioqXoA6FAAJbIgANwh7POT9aAAWTnXDXgT7JhkXIQBzYp446IFDQjBIQntDYC7ePkJWRKqapxdFUEhOn0YFMUEEAv4AgEoABhFJQACMgSh0Lk9kcMhYICgQj4yyejSgzT6jlKLGAk0wvWKhn6gyQw1G416KCkCgSUBJHwQSgYJgGuHppHI40svk5ZIpk0I3igkKgADkust7KiIci4QAmJEw1Ho4CY7EMXGPBoAomYeHUvqCkbC5mshjsi3c5R8ukM4VUSwU3wWqUyuWKzRo1Xa2HrKAAZjDuogGKxOLxpqaLUwkat-Q9Y3tbMS-KwGpoiFd+ZtjI8hgdCVFKEwGv9sqhQeVobl4c22vCrLRcf1CaNSf+KYLGdLWZo9GYbA4XFeQhEExZueQgzTRZ5boF469vsjDblAFEQ-ZcAB5VIAK172MMYDk+n0AlSBt6U5YVNRfVn-Hn3F6QA
π» Code
π Actual behavior
In case 4, accessing
A3.a
results in error "Object is possiblynull
orundefined
".Appears that the inferred type of
A3
is{a: 'A'} | null | undefined
. I suspect when we destructure, the return type offn
is inferred asR
instead of the narrowed generic type variableT
.π Expected behavior
Expect no errors. Should be able to access
A3.a
, similar cases 1, 2, and 3. Expectation is that destructuring assignment shouldn't modify inferred return type of function.Additional information about the issue
Current workarounds:
as const
appended to param (see case 3)const
with generic type variable in function signature (function fn<const T extends R>(t: T): T;
)The text was updated successfully, but these errors were encountered: