Why is `{ foo: never }` not assignable to `never` in TypeScript?

  Kiến thức lập trình

I’m using distributive conditional types to produce a union type from a union type. I’m transforming each union member into an object type, and only some elements of the original union correspond to this object type; I want to exclude the others from the union.

It looks something like this:

type ExtractFoo<T> = T extends { a: infer A; b: infer B } ? [A, B] : never;
type ExtractBar<T> = T extends { c: infer C; d: infer D } ? [C, D] : never;
type Transform<T> = T extends unknown ? { foo: ExtractFoo<T>; bar: ExtractBar<T> } : never;

I know that TypeScript automatically removes never from union types, so I was surprised when I saw that TypeScript fails to simplify the output of this invocation of Transform:

type T = Transform<{ a: 1; b: 2; c: 3; d: 4 } | { a: 'a'; b: 'b' } | { a: true; c: false }>
// Expected: { foo: [1, 2]; bar: [3, 4] }
// Actual: type T = {
//     foo: [1, 2];
//     bar: [3, 4];
// } | {
//     foo: ["a", "b"];
//     bar: never;
// } | {
//     foo: never;
//     bar: never;
// }

My understanding is that an object type with a field of type never is exactly equivalent to never, as both are uninhabited. As such, I was surprised that TypeScript doesn’t simplify { foo: ["a", "b"]; bar: never } out of the union the same way it does with other ways of phrasing the never type.

Digging into it further, I found that surprisingly, TypeScript always treats { foo: never } as different from never, even though they are the exact same type:

(x: { foo: never }): never => x;
// Error: Type '{ foo: never; }' is not assignable to type 'never'.

Why does TypeScript treat { foo: never } as different from never?

LEAVE A COMMENT