TypeScript: propiedades seleccionadas opcionales con Parcial y Omitir
Cuando usa la utilidad Partial
en un tipo, hará que todas las propiedades del tipo sean opcionales. Veamos cómo podemos combinar esta utilidad con otra, Omit
, para hacer opcionales solo ciertas propiedades de este tipo. Finalmente, crearemos un tipo de utilidad usando genéricos de TypeScript para aplicar esto en cualquier tipo que queramos.
Hacer que todas las propiedades de un tipo sean opcionales
Las utilidades Partial
y Omit
son bien conocidas en el ámbito de TypeScript porque son muy útiles para aplicarlas rápidamente a un tipo y obtener lo que es básicamente un tipo nuevo. Partial<T>
convertirá todas las claves de un tipo en opcionales. Si por ejemplo tienes este tipo:
interface Band {
lead: string
guitar: string
bass: string
drums: string
keyboard: string
}
todas las propiedades son requeridas cuando algo tiene este tipo. Si desea asignar un objeto como este:
const FooFighters: Band = {
lead: 'Dave Grohl',
guitar: 'Pat Smears',
bass: 'Nate Mendel',
drums: 'Taylor Williams',
}
fallará porque le falta la keyboard
propiedad. Ahora, podríamos marcar esta propiedad como opcional y TypeScript nos permite hacerlo fácilmente. La forma más básica de hacerlo es agregando un signo de interrogación a su nombre. En este caso, si es posible editar el tipo original, puedes ir a él y modificarlo así:
interface Band {
lead: string
guitar: string
bass: string
drums: string
keyboard?: string
}
Excelente. Ahora la asignación anterior tendrá éxito porque keyboard
ya no es necesaria. Pero, ¿y si no es posible cambiar el tipo original por cualquier motivo? Lo que puedes hacer rápidamente en estos casos es aplicar Partial
a este tipo:
type NotAllTheBand = Partial
const FooFighters: NotAllTheBand = {
lead: 'Dave Grohl',
guitar: 'Pat Smears',
bass: 'Nate Mendel',
drums: 'Taylor Williams',
}
Esto deja de aplicar todas las propiedades, haciéndolas todas opcionales. Tal vez a veces queramos esto, pero ¿qué pasa si solo queremos hacer que solo una propiedad sea opcional y evitar modificar el tipo original?
Quitar propiedades de un tipo
Antes de pasar a ella y ver cómo funciona, veamos otra utilidad que nos ayudará con esto: Omit
. Este tipo de utilidad elimina propiedades del tipo donde se aplica. Si bien Partial
no toma otros argumentos además del tipo, Omit
toma el tipo más las propiedades que desea eliminar: Omit<T, K>
.
No es que Partial
eso los haga opcionales: Omit
los borrará por completo del tipo. Cuando lo usas Partial<Band>
es como si hubieras hecho esto:
interface Band {
lead?: string
guitar?: string
bass?: string
drums?: string
keyboard?: string
}
Sin embargo, cuando usas Omit<Band, 'keyboard'>
, es como si hubieras hecho esto:
interface Band {
lead: string
guitar: string
bass: string
drums: string
}
Esto ahora está empezando a tener sentido, ¿verdad? ¿Te imaginas lo que podríamos hacer en TypeScript si tenemos un tipo que tiene todas las propiedades del original configurado como opcional y otro tipo que tiene solo las propiedades que queremos requerir? Si tan solo hubiera una manera de… cruzar estos tipos, ¿verdad?
Tipos de intersección
Sí, hay una manera en TypeScript usando el ampersand &
, el operador de intersección de tipos. Este operador, dados dos tipos, construye uno nuevo con las propiedades propias de ambos tipos:
interface SomeType {
propA: string
propB: number
}
interface AnotherType {
propC: boolean
propD: Array
}
type IntersectedType = SomeType & AnotherType
Probablemente ya lo haya descubierto: vamos a cruzar un tipo parcializado y un tipo del que eliminamos algunas de sus propiedades.
Hacer que las propiedades seleccionadas de un tipo sean opcionales
Tratemos de darle sentido. Al aprender TypeScript, es importante racionalizar lo que sucede con nuestros tipos.
Vamos a tomar un tipo que tenga todas sus propiedades configuradas como opcionales gracias a que Partial
se le aplicó. A continuación, vamos a cruzarlo con un tipo que tiene propiedades seleccionadas eliminadas por Omit
. Veamos un ejemplo:
interface SomeType {
propA: string
propB: number
}
type OptionalPropB = Partial & Omit
Ahora tenemos un nuevo tipo, sin modificar el tipo original, que se parece a lo siguiente:
type OptionalPropB = {
propA: string
propB?: string
}
Esto sucede porque tomamos propiedades propA?
y propB?
del tipo producido por Partial
y las cruzamos con propA
el tipo producido por Omit
.
Utilidades genéricas de TypeScript
Bien, ahora nuestro nuevo tipo funciona y solo podemos tener propiedades seleccionadas marcadas como opcionales sin modificar el tipo original. Sin embargo, esta línea
type OptionalPropB = Partial & Omit
Es demasiado prolijo, bastante feo y tedioso para escribir cada vez. Más importante aún, no funcionará para un tipo diferente, tendremos que escribirlo nuevamente cada vez. ¿Podemos hacerlo más corto y más bonito, y que funcione para cualquier tipo? Sí, TypeScript nos permite usar genéricos aquí y crear nuestro propio tipo de utilidad:
type Optional = Partial & Omit
Y ahora tenemos nuestro propio tipo de utilidad TypeScript implementado con genéricos que podemos usar para hacer que las propiedades de tipo seleccionadas sean opcionales en cualquier tipo. Podemos usarlo así:
type TypeWithOptionalProp = Optional
Notará que la firma es similar a Omitir: esta utilidad toma el tipo para operar y las propiedades que se harán opcionales. Si tiene más, puede usar el operador de unión:
type TypeWithOptionalProps = Optional
palabras de cierre
Ahora puede guardar este tipo de utilidad genérica en un archivo y exportarlo para usarlo en toda su aplicación. Aquí hay un enlace a un área de juegos de TypeScript donde agregué el código de este artículo y lo comenté para que pueda ver lo que está pasando, mirando Partial
, Omit
y nuestro tipo de utilidad genérica.
El mundo de los genéricos y las utilidades es fantástico en TypeScript. ¡Anímate y empieza a experimentar!