Patrones
¿Cómo acepto `href` u `onClick` pero rechazo objetos que proporcionan ambos?
Une las dos formas y añade las claves únicas del otro lado como propiedades opcionales `never`.
La receta
type Without<T, U> = { [K in Exclude<keyof T, keyof U>]?: never } type XOR<T, U> = | (T & Without<U, T>) | (U & Without<T, U>) type LinkProps = XOR< { href: string }, { onClick: () => void } >
La compilación compila esto y comprueba cada resultado de abajo.
Cómo funciona
- 01
[K in Exclude<keyof T, keyof U>]?: never
Las claves únicas de la rama opuesta pueden faltar, pero nunca contener un valor.
- 02
| (U & Without<T, U>)
Cada miembro de la unión combina su forma obligatoria con una prohibición de la otra.
Lo que obtienes
{ href: string } extends LinkProps ? true : false→true{ onClick: () => void } extends LinkProps ? true : false→true{ href: string; onClick: () => void } extends LinkProps ? true : false→false
Dónde falla
Es más claro para dos formas pequeñas. Los tipos grandes y solapados producen diagnósticos ruidosos, y `exactOptionalPropertyTypes` cambia si un `undefined` explícito satisface un `never` opcional.
Conclusión
Usa `never` opcional para decir que una propiedad pertenece a la otra rama y no debe aparecer aquí.
