¿Cómo sobrescribir una propiedad para un tipo de intersección en Typescript?
Digamos que tengo estos tipos:
type BaseAnimal = {
species: string
owner: boolean
}
type Cat = BaseAnimal & {
species: 'cat'
hasTail: boolean
}
type Dog = BaseAnimal & {
species: 'dog'
likesWalks: boolean
}
type Animal = Cat | Dog
Y quiero crear un tipo llamado AnimalParams, que sea idéntico a Animal excepto la propiedad owner, que es una cadena.
No puedo hacer ninguno de los dos siguientes.
// This seems to keep the owner property from Animal instead of overwriting
// So it raises an error if I try to specify owner as a string
type AnimalParams = Animal & {
owner: string
}
// This strips away properties unique to Cat or Dog
// So it raises an error if I try to specify hasTail or likesWalks
type AnimalParams = Omit<Animal, 'owner'> & {
owner: string
}
Ahora, la única solución alternativa que se me ocurre es hacer como se indica a continuación, pero esto parece innecesariamente repetitivo. ¿Hay una forma más limpia y concisa?
type CatParams = Omit<Cat, 'owner'> & {
owner: string
}
type DogParams = Omit<Dog, 'owner'> & {
owner: string
}
type AnimalParams = CatParams | DogParams
Leí algunos hilos de SO sobre tipos utilitarios (como Sobrescribir el tipo de propiedad de una interfaz definido en el archivo d.ts de Typescript, que era para interfaces), pero no pude encontrar lo que necesitaba. ¡Gracias de antemano por cualquier respuesta!
Respuesta #1
En lugar de omitir manualmente la propiedad owner de cada tipo, puedes usar un tipo condicional distributivo:
type OmitOwner<T = Animal> = T extends BaseAnimal ? Omit<T, 'owner'> : never;
type AnimalParams = OmitOwner & {
owner: string
};
Lo cual es equivalente a:
(Omit<Cat, 'owner'> & { owner: string; })
| (Omit<Dog, 'owner'> & { owner: string; })
Eso se debe a la distribución automática sobre los tipos de unión
La instanciación de
T extends U ? X : Ycon el argumento de tipoA | B | CparaTse resuelve como(A extends U ? X : Y) | (B extends U ? X : Y) | (C extends U ? X : Y)
Patio de recreo
¿Por qué el intento original no funciona?
La unión de keyof produce la intersección de las claves de los tipos en la unión, así que
type AnimalKeys = keyof Animal // is "species" | "owner"
Y la implementación de Omit es:
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
Respuesta #2
He estado usando este genérico para sobrescribir props en React con éxito:
type Overwrite<T, NewT> = Omit<T, keyof NewT> & NewT;
export type TouchableAvatarPropTypes = Overwrite<AvatarPropTypes, {
/** Function executed when avatar is pressed. */
onPress: () => void;
/** Size of the avatar */
size: number;
}>
Puedes hacer algunas líneas únicas bastante limpias para exportar.
