Búsqueda de sitios web

¿TypeScript admite tipos mutuamente exclusivos?


Tengo un método que toma un parámetro. Me gustaría que TypeScript verificara que el objeto que se pasa (en tiempo de compilación, entiendo que el tiempo de ejecución es otro tema) solo cumpla con una de las interfaces permitidas.

Ejemplo :

interface Person {ethnicity: string;}
interface Pet {breed: string;}
function getOrigin(value: Person ^ Pet){...}

getOrigin({}); //Error
getOrigin({ethnicity: 'abc'}); //OK
getOrigin({breed: 'def'}); //OK
getOrigin({ethnicity: 'abc', breed: 'def'});//Error

Me doy cuenta de que Person ^Pet no es TypeScript válido, pero es lo primero que se me ocurrió intentar y parecía razonable.

Respuesta #1

Como se propone en este tema, podrías usar tipos condicionales para escribir un tipo XOR:

type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };
type XOR<T, U> = (T | U) extends object ? (Without<T, U> & U) | (Without<U, T> & T) : T | U;

Y ahora tu ejemplo funciona:

interface Person {ethnicity: string;}
interface Pet {breed: string;}
function getOrigin(value: XOR<Person, Pet>) { /* ... */}

getOrigin({}); //Error
getOrigin({ethnicity: 'abc'}); //OK
getOrigin({breed: 'def'}); //OK
getOrigin({ethnicity: 'abc', breed: 'def'});//Error

Respuesta #2

Puedes usar el pequeño paquete npm ts-xor que fue creado específicamente para abordar este problema.

Con él puedes hacer lo siguiente:

import { XOR } from 'ts-xor'
 
interface A {
  a: string
}
 
interface B {
  b: string
}
 
let A_XOR_B: XOR<A, B>
 
A_XOR_B = { a: 'a' }          // OK
A_XOR_B = { b: 'b' }          // OK
A_XOR_B = { a: 'a', b: 'b' }  // fails
A_XOR_B = {}                  // fails

Incluso puedes hacer XOR con más de un tipo así:

XOR<A, B, C, D, E, F> // supports XORing up to 200 type arguments

Divulgación completa: Soy el autor de ts-xor. Descubrí que necesitaba implementar el tipo XOR de repositorio en repositorio todo el tiempo. Así que lo publiqué para la comunidad y para mí, y de esta manera, también podría agregar pruebas y documentarlo correctamente con un README y anotaciones de JSDoc. La implementación es lo que @Guilherme Agostinelli compartió de la comunidad.

Artículos relacionados