Skip to content

Empty array in union with tuple doesn't infer undefined on indexed access #47531

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
amakhrov opened this issue Jan 20, 2022 · 6 comments
Open
Labels
In Discussion Not yet reached consensus Suggestion An idea for TypeScript

Comments

@amakhrov
Copy link

Bug Report

declare const tuple: never[] | [string];

tuple[0].length // no compile-time error :(

Playground
Note that noUncheckedIndexedAccess is enabled - but doesn't make any difference in this case.

The reason it is a problem is that [] is inferred as never[] in many scenarios, thus allowing type-unsafe code. Example:

const data = ['A'] as const;

// inferred as `never[] | ['A']
const a = Math.random() > 0.5 ? [] : data;

a[0].length; // can be undefined, but no compiler error

🔎 Search Terms

Tuple, union, empty array, infer, noUncheckedIndexedAccess

🕗 Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about never, tuple, array

⏯ Playground Link

Playground link with relevant code

💻 Code

declare const tuple: never[] | [string];

tuple[0].length // no compile-time error :(

🙁 Actual behavior

Compiles well, breaks at runtime

🙂 Expected behavior

Compile-time error that tuple[0] is possibly undefined

@amakhrov amakhrov changed the title Empty tuple in union doesn't infer undefined on indexed access Empty array in union doesn't infer undefined on indexed access Jan 20, 2022
@amakhrov
Copy link
Author

This would not be a problem if an empty array was inferred as [] (empty tuple) instead of never[]

@amakhrov amakhrov changed the title Empty array in union doesn't infer undefined on indexed access Empty array in union with tuple doesn't infer undefined on indexed access Jan 20, 2022
@fatcerberus
Copy link

Workaround:

const data = ['A'] as const;
const a = Math.random() > 0.5 ? [] as const : data;

a[0].trim();  // TS2532: possibly undefined

@amakhrov
Copy link
Author

@fatcerberus yup.

From the practical standpoint, if I know I need this workaround here and now, it means I anticipate possible type unsafety and have double checked my code already. However, if I'm unaware I'm doing something wrong, it would also not occur to me to do as const.

So my point is that this should be as automated (inferred) as possible.

@RyanCavanaugh RyanCavanaugh added In Discussion Not yet reached consensus Suggestion An idea for TypeScript labels Jan 20, 2022
@RyanCavanaugh
Copy link
Member

This is pretty messed up; we shouldn't be presenting a never[] under normal circumstances.

@fatcerberus
Copy link

fatcerberus commented Jan 22, 2022

@RyanCavanaugh A surefire way to get a never[] is to simply return [] from a function:

function fooey() { return []; }
const foo = fooey();
//    ^? const foo: never[]

@jcalz
Copy link
Contributor

jcalz commented Aug 28, 2022

#50474 was marked as a duplicate of this, but if so, then the problem doesn't seem to have much to do with never[], and has more to do with propagation of the undefined (when --noUncheckedIndexedAccess is enabled) when reading properties from unions where some union members have index signatures and some do not. Like, what do we make of this behavior:

const nums: { [k: string]: number } = Math.random() < 0.5 ? { a: 1 } : { b: 2 };
const str = { a: "hello" }

// with --noUncheckedIndexedAccess on
const hmm = (Math.random() < 0.5 ? nums.a : str.a) // string | number | undefined
const wha = (Math.random() < 0.5 ? nums : str).a // string | number <-- 😕

Playground link

Is this a bug? If so, does it belong here in #47531 or somewhere else?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
In Discussion Not yet reached consensus Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

4 participants