Skip to content

nested destructured tuple value is string | undefined when second tuple item is not accessed #42969

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

Open
G-Rath opened this issue Feb 25, 2021 · 3 comments · May be fixed by #56875
Open

nested destructured tuple value is string | undefined when second tuple item is not accessed #42969

G-Rath opened this issue Feb 25, 2021 · 3 comments · May be fixed by #56875
Labels
Bug A bug in TypeScript
Milestone

Comments

@G-Rath
Copy link

G-Rath commented Feb 25, 2021

Bug Report

When using noUncheckedIndexedAccess, the type inferred by my code is affected by whether I access the second item in a tuple or not.

🔎 Search Terms

tuple, undefined, type, omit, noUncheckedIndexedAccess, error, index, array, infer, nested

🕗 Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about noUncheckedIndexedAccess
  • I was unable to test this on prior versions because --noUncheckedIndexedAccess didn't exist before those versions

⏯ Playground Link

Playground link with relevant code

💻 Code

declare const fn: <T extends string[]>(t: T) => [T, string[]];

const [[x]] = fn(['1']);

console.log(x.toLowerCase());

It seems that the inferred type for fn is:

const fn: <string[]>(t: string[]) => [string[], string[]]

Since the first item is an array rather than a tuple, items in that array have type string | undefined.

If I change the assignment to be:

const [[x], []] = fn(['1']);

It gets inferred as:

const fn: <[string]>(t: [string]) => [[string], string[]]

Since the first item is a tuple rather than an array, items in that tuple have type string.

🙁 Actual behavior

x has type string | undefined

🙂 Expected behavior

x has type string.

@G-Rath
Copy link
Author

G-Rath commented Feb 25, 2021

It seems like I can resolve this by changing the extends type to be string[] | [string] without any changes in or loss of functionality.

It's very possible that this is The Correct Type I should be using for typing this the most accurately, but will leave this issue open for now as it still strikes me as a little odd that accessing the second item changes the inferred type of the first item, given it's redundant in JS (which is what got me here: eslint rules telling me it's not needed).

@RyanCavanaugh RyanCavanaugh added the Bug A bug in TypeScript label Mar 2, 2021
@RyanCavanaugh RyanCavanaugh added this to the Backlog milestone Mar 2, 2021
@RyanCavanaugh
Copy link
Member

That doesn't look right to me either. Contextual inference candidates from destructurings are weird, though, so it's hard to say.

@Andarist
Copy link
Contributor

Andarist commented Feb 5, 2023

It seems like I can resolve this by changing the extends type to be string[] | [string] without any changes in or loss of functionality.

This works because it hints the compiler to infer a tuple for T. So if you are aiming to infer a tuple there then this makes sense.

To hint at inferring a tuple I would use this though:

declare const fn: <T extends string[]>(t: [...T]) => [T, string[]];

Somewhat surprisingly - this one infers ["1"] whereas the one that you proposed infers [string].

It's definitely weird to me that adding a pattern for the second item in this destructuring makes it infer a tuple. But then... I'm not sure what the bug is. Should the tuple be inferred for const [[x]] = or should the tuple not be inferred for const [[x], []] =? 😅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript
Projects
None yet
3 participants