Al mezclar tipos de TypeScript con interfaces en una intersección, parece que pierdo el comportamiento más estricto de las interfaces en mi código. Quiero poder componer tipos usando intersecciones (de modo que pueda restringir un tipo en instancias específicas), pero quiero poder mantener la exactitud de las interfaces.
Toma este código:
type Vehicle = { name: string properties: Record<string, unknown> } interface CarProperties { electric?: boolean } type Car = Vehicle & { name: 'car' properties: CarProperties } const car: Car = { name: 'car', properties: { electric: false, someOther: 'bs' // <= I want this to throw a TS Error } } Como CarProperties es una interfaz, espero que no permita la inclusión de la someOther clave.
¿Por qué no es este el caso? ¿Y de qué otra manera se podría lograr lo mismo?
El problema es el tipo de intersección Car . Esto lleva a que properties del automóvil sean un Record<string, unknown> & CarProperties y, por lo tanto, permite las claves de cadena.
Para lograr lo que desea, puede agregar un genérico a su tipo de vehículo:
type Vehicle<T extends object> = { name: string properties: T } type GenericVehicle = Vehicle<Record<string, unknown>>; interface CarProperties { electric?: boolean } interface Car extends Vehicle<CarProperties> { name: 'car' }PD: Me di cuenta de que llamas a esto un "tipo de unión", que no lo es . Los tipos de intersección comparten las propiedades de los tipos intersecados, mientras que los tipos de unión pueden ser cualquiera de los tipos sindicalizados.
Puede hacer que la propiedad electric sea obligatoria y usar el tipo de object en lugar de Record<string, unknown> .
type Vehicle = { name: string properties: object, // <---- change } type CarProperties = { electric: boolean // is required now } type Car = Vehicle & { name: 'car' properties: CarProperties } // ok const car2: Car = { name: 'car', properties: { electric: false, } } // expected error const car4: Car = { name: 'car', properties: [] } // expected error const car5: Car = { name: 'car', } // error const car3: Car = { name: 'car', properties: {} } // error const car: Car = { name: 'car', properties: { electric: false, someOther: 'bs' // error } } Lo sé, usar el tipo de object es un poco controvertido debido a esta regla, pero puede encontrar más información sobre los pros y los contras en esta respuesta.
Si tiene más variantes de Vehicle , no sólo un Car , es posible que desee eliminar properties del tipo de Vehicle y crear una unión discriminada de Vehicles permitidos. Es solo mi suposición, ya que no conozco ningún otro requisito.
También vale la pena saber sobre los cheques de propiedad en exceso . No le permitirá proporcionar ninguna otra propiedad adicional para el argumento literal.
Si pone todos sus requisitos y casos de prueba en su pregunta, creo que obtendrá lo que está buscando