Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does TS allow calling `forEach` on something that could be `Record<string, never>`?

Tags:

typescript

Take this code: playground here

declare const slice: Record<string, never> | string[];
slice.forEach(foo => console.log(foo))

Slice could be a Record<string, never>, which as I understand it, is basically an empty object. So why does TS allow me to call .forEach on it? It seems like at runtime this could result in a forEach is not a function error.

like image 204
dipea Avatar asked Oct 14 '25 15:10

dipea


1 Answers

Consider this example:

declare let slice: Record<string, never> | string[];

Union type returns you most common safe type. See docs

TypeScript will only allow an operation if it is valid for every member of the union. For example, if you have the union string | number, you can’t use methods that are only available on string.

Hence, TS allows you to use methods from string[] only because string in Record represents more general type. It means that string in Record allows you to use any string as a key, whereas string[] allows you to use only array methods. So, forEach method is safe to use for both string[] and Record<string, never>.

As for the value. never is assignable to every type in typescript, whereas every type in typescript is not assignable to never. See assignability table in docs

declare let never: never;
declare let fn: () => void

never = fn // error
fn = never  // ok

Hence, never is always clashed in the union with other types. See this:

type Union = never | string

Here, in PR you will find an explanation of this behavior:

Because never is a subtype of every type, it is always omitted from union types and it is ignored in function return type inference as long as there are other types being returned.

So, if you have a union of Array.prototype.method with never - {forEach:(...args:any)=>any} | never, it always ommits never.

This is why you are not getting an error.

like image 178
captain-yossarian Avatar answered Oct 20 '25 18:10

captain-yossarian