Al usar typeof para restringir el tipo de unión, me sorprende que bajo la premisa de que bajo la condición if obtenemos el tipo particular de value de campo pero no podemos inferir el tipo de campo target correctamente. ¿Por qué no se puede inferir que el tipo de objetivo es HTMLInputElement cuando se sabe que el valor es una cadena?
type UserTextEvent = { value: string, target: HTMLInputElement }; type UserMouseEvent = { value: [number, number], target: HTMLElement }; type UserEvent = UserTextEvent | UserMouseEvent function handle(event: UserEvent) { if (typeof event.value === 'string') { event.value // string //--------------------------------------------------------------- event.target // HTMLInputElement | HTMLElement but not HTMLInputElement, why ? // return } }De alguna manera, podría rehidratar los tipos originales con un predicado de tipo :
interface UserTextEvent { value: string, target: HTMLInputElement }; interface UserMouseEvent { value: [number, number], target: HTMLElement }; type UserEvent = UserTextEvent | UserMouseEvent function handle(event: UserEvent) { if (isUserTextEvent(event)) { event.value // string //--------------------------------------------------------------- event.target // HTMLInputElement // return } else { event.value // [number, number] event.target // HTMLElement } } function isUserTextEvent(userEvent: UserEvent): userEvent is UserTextEvent { return typeof userEvent === 'string'; }Creo que el tipo de estrechamiento que está buscando se incluye en la categoría uniones discriminadas .
🚩 Digo "creo" porque no estoy 100% seguro de la definición de "sindicatos discriminados". Sé que los valores de una propiedad compartida por todos los tipos de miembros de una unión de tipos actúan como discriminantes (por ejemplo
o.kind: 'circle'vso.kind: 'square'). No estoy seguro de si la inferencia de tipos basada en los tipos de propiedades compartidas (por ejemplotypeof o.value === 'string'vsArray.isArray(o.value)) se considera igual o diferente. Pero si tanto los valores como los tipos de propiedad se consideran discriminantes con respecto a las "uniones discriminadas", mi explicación a continuación responde a su pregunta.
Estos son los tipos de propiedades discriminantes admitidas cuando se agregó compatibilidad con uniones discriminadas en Typescript 2.0 :
Una protección de tipo de propiedad discriminante es una expresión de la forma
xp == v,xp === v,xp != voxp !== v, dondepyvson una propiedad y una expresión de un tipo literal de cadena o una unión de tipos de literales de cadena. La protección del tipo de propiedad discriminante reduce el tipo de x a aquellos tipos constituyentes de x que tienen una propiedad discriminante p con uno de los posibles valores de v.Tenga en cuenta que actualmente solo admitimos propiedades discriminantes de tipos de literales de cadena. Tenemos la intención de agregar soporte para tipos literales booleanos y numéricos más adelante.
Typescript 3.2 amplió ese soporte :
Las propiedades comunes de las uniones ahora se consideran discriminantes siempre que contengan algún tipo único (por ejemplo, una cadena literal, nulo o indefinido) y no contengan genéricos.
Como resultado, TypeScript 3.2 considera que la propiedad de error del siguiente ejemplo es un discriminante, mientras que antes no lo sería, ya que Error no es un tipo singleton. Gracias a esto, el estrechamiento funciona correctamente en el cuerpo de la función de desenvolver.
type Result<T> = { error: Error; data: null } | { error: null; data: T }; function unwrap<T>(result: Result<T>) { if (result.error) { // Here 'error' is non-null throw result.error; } // Now 'data' is non-null return result.data; }
TypeScript 4.5 agregó soporte para "Tipos de cadena de plantilla como discriminantes" .
TypeScript 4.5 ahora puede limitar los valores que tienen tipos de cadena de plantilla y también reconoce los tipos de cadena de plantilla como discriminantes.
Como ejemplo, lo siguiente solía fallar, pero ahora se verifica correctamente en TypeScript 4.5.
export interface Success { type: `${string}Success`; body: string; } export interface Error { type: `${string}Error`; message: string } export function handler(r: Success | Error) { if (r.type === "HttpSuccess") { const token = r.body; (parameter) r: Success } }Try
En todos los casos, es el valor de la propiedad discriminante lo que se usa para discriminar, no su tipo .
En otras palabras, a partir de la versión actual de Typescript, 4.5, creo que no tienes suerte. Simplemente no es compatible (todavía).
🌶 Busqué Problemas mecanografiados y no pude encontrar una mención. ¿Quizás abrir un nuevo problema que apunte a esta pregunta SO?