¿Puede TypeScript inferir el tipo de una unión discriminada mediante la lógica booleana "extraída"?
He estado usando uniones discriminadas (DU) con más frecuencia y he llegado a amarlas. Sin embargo, tengo un problema que no consigo superar. Si incluyes directamente una comprobación booleana para la DU, puedes confiar en que TypeScript (TS) infiera automáticamente el tipo por ti. Sin embargo, si extraes la comprobación booleana, TS ya no puede limitar al subtipo específico de la DU. Soy consciente de los type guards, pero me gustaría saber por qué el compilador no soporta comprobaciones extraídas en línea, específicamente.
¿Es esta una limitación conocida? ¿Debería presentar un informe de error o una solicitud de función?
Ejemplo aquí (con enlace a TypeScript Playground):
type A = { type: "A"; foo: number };
type B = { type: "B"; bar: string };
type DU = A | B;
const checkIt = (it: DU) => {
const extractedCheck = it.type === "A";
if (extractedCheck) {
// it does not get narrowed
console.log(it.foo); // Error: Property 'foo' does not exist on type 'DU'.
}
if (it.type === "A") {
// but this is fine
console.log(it.foo);
}
};
Respuesta #1
ACTUALIZACIÓN PARA TS4.4:
TypeScript 4.4 introducirá soporte para guardar los resultados de los type guards en un const, como se implementa en microsoft/TypeScript#44730. En este punto, tu ejemplo de código simplemente funcionará:
const checkIt = (it: DU) => {
const extractedCheck = it.type === "A";
if (extractedCheck) {
console.log(it.foo); // okay
}
};
Enlace de Playground al código
RESPUESTA PARA TS4.3 Y VERSIONES ANTERIORES:
Hay una solicitud de función existente en microsoft/TypeScript#12184 para permitir que los resultados de dicho guardia de tipo se "guarden" en un valor con nombre para ser utilizado más tarde. La solicitud está abierta pero se ha listado como "revisar" porque no hay una manera obvia de implementarlo de forma eficiente. La palabra del arquitecto principal del lenguaje es:
Esto requeriría que rastreemos qué efectos implica un valor particular para una variable sobre otras variables, lo que añadiría una buena cantidad de complejidad (y una penalización de rendimiento asociada) al analizador de flujo de control. Aun así, lo mantendremos como una sugerencia.
Así que, desafortunadamente, no esperaría ver una característica así en el lenguaje pronto.
Mi sugerencia es simplemente continuar usando tu verificación de tipo en línea. Si tienes una situación de guardia de tipo más compleja, puede valer la pena crear una función de guardia de tipo definida por el usuario, pero no veo que eso sea una mejora para el caso en tu código de ejemplo.
Está bien, espero que eso ayude; ¡buena suerte!
Respuesta #2
Actualmente no, como mencionó @jcalz, sin embargo, hay una solución mínima que siempre puedes aplicar para superar este problema.
El truco es hacer que extractCheck sea una función que devuelva predicado de tipo.
(Consulta https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards)
El siguiente es un ejemplo funcional:
type A = { type: "A"; foo: number };
type B = { type: "B"; bar: string };
type DU = A | B;
const checkIt = (it: DU) => {
// Make `extractCheck` a function
const extractedCheck = (it: DU): it is A => it.type === "A";
if (extractedCheck(it)) {
console.log(it.foo); // No error
}
};
