Tipos de unión de Typescript no excluidos
Tengo un problema con los tipos de unión y Exclude (enlace al playground):
type Foo = {
data: string;
otherData: number;
};
type Bar = Omit<Foo,'otherData'>
// type Bar = { data: string; }; // Even this is not working
type FooBar = Foo | Bar;
type ShouldBeFoo = Exclude<FooBar, Bar>; // Not working
// ShouldBeFoo = never
type ShouldBeBar = Exclude<FooBar, Foo>; // Somehow working
// ShouldBeBar = { data: string }
¿Me estoy perdiendo algo relacionado con los tipos union y/o Exclude?
También intenté con TS 4.4 y 4.2 con el mismo resultado.
Nota:
Descubrimos el problema usando guardianes de tipo, puedes verlo en el playground aquí.
Respuesta #1
Exclude excluye tipos que son coincidentes, no iguales.
Estás excluyendo Bar de FooBar. Bar coincide con Bar y Bar coincide con Foo. Así que excluyes ambos.
Podrías usar algo como
type ExcludeExact<T, U> = T extends U ? U extends T ? T : never : never
Respuesta #2
Los tipos utilitarios como Pick, Omit funcionan bien con interfaces o tipos con propiedades ya que el segundo argumento tiene el aspecto de keyof K.
Como el tuyo es un tipo unión, el compilador de TS no puede inferir el comportamiento correcto. Puedes intentar esto
type Foo = {
data: string;
otherData: number;
};
type Bar = Omit<Foo, 'otherData'>
type FooBar = Foo | Bar;
type Reduce<T, K> = T extends K ? K : never;
type ShouldBeFoo = Reduce<FooBar, Foo>;
type ShouldBeBar = Reduce<FooBar, Bar>;
