I have this type
type Cartesian = { kind: 'cartesian'; x: number; y: number; }
type Polar = { kind: 'polar'; angle: number; distance: number }
type Movement = { name: string } & (Cartesian | Polar);
that I can use like this
const move = (movement: Movement) => { /* whatever */ };
move({ name: 'top right', kind: 'cartesian', x: 10, y: 10 });
move({ name: 'left', kind: 'polar', angle: Math.PI, distance: 10 });
but for some reason, I can't use it like this
const unnamedMove = (unnamedMovement: Omit<Movement, 'name'>) => {
move({ name: 'default', ...unnamedMovement })
}
because TS throws a 2345:
Argument of type '{ kind: "cartesian" | "polar"; name: string; }' is not assignable to parameter of type 'Movement'.
Type '{ kind: "cartesian" | "polar"; name: string; }' is not assignable to type '{ name: string; } & Polar'.
Type '{ kind: "cartesian" | "polar"; name: string; }' is missing the following properties from type 'Polar': angle, distance
I don't understand this.
If I am not mistaken Omit<Movement, 'name'>
should be equivalent to the union type Cartesian | Polar
, which would make { name: 'default', ...unnamedMovement }
a Movement
, and all things should work.
However, it looks like TS infers Omit<Movement, 'name'>
as if it was the union type Cartesian & Polar
, thus the error.
Is it a bug or a mistake from my part?
You probably want Distributive conditional types. From the docs
When conditional types act on a generic type, they become distributive when given a union type.
This means that you can declare something like this
type DOmit<T, K extends string> = T extends unknown
? Omit<T, K>
: never;
and then use it like this
const unnamedMove = (unnamedMovement: DOmit<Movement, "name">) => {
move({ name: 'default', ...unnamedMovement })
}