La función “Switch” sobre los miembros de una unión discriminada en Typescript.
Supongamos que tengo una unión discriminada en Typescript:
type MyAction = { type: "redirect", url: string} | {type: "returnValue", value: number}
Quiero escribir una “expresión de switch” que tome un miembro de la unión discriminada y un mapeo de tipos a funciones procesadoras, y devuelve el resultado de ejecutar la función procesadora correcta:
let actionInstance = {type: "returnValue", value: 123} as MyAction
console.log(switchExpression(actionInstance, {
redirect: (action) => action.url + "(url)",
returnValue: (action) => `${action.value}` + "(value)"}
))
Quiero que las funciones procesadoras estén tipadas – es decir, quiero que Typescript dé un error si no se representan todos los tipos en el switch, si proporciono una clave para el switch que no es un tipo válido, o si asumo el tipo equivocado en una función procesadora (como intentar acceder a action.value
en el valor para redirect
).
Esto es lo que tengo hasta ahora, basado en algunas otras respuestas en StackOverflow:
type NarrowAction<t, n=""> = T extends { type: N } ? T : never;
type StuctureRecord<functionreturntype> =
{ [K in MyAction["type"]]: (value: NarrowAction<myaction, k="">) => FunctionReturnType }
function switchExpression<functionreturntype>(value: MyAction, cases: StuctureRecord<functionreturntype>): FunctionReturnType {
let f = cases[value.type] as (value: any) => FunctionReturnType;
return f(value);
}
Esto funciona correctamente, pero es específico para una unión discriminada (MyAction
).
¿Hay alguna manera de adaptar este código para que funcione con cualquier unión discriminada donde todos los miembros tengan un campo type
, en lugar de que solo funcione para MyAction
? Intenté usar un tipo como DUType extends { type: string }
, pero eso resulta en un montón de errores.
Nota: Sé que puedo usar directamente StructuredRecord
o podría simplemente usar una sentencia switch regular o sentencias if/else con una función assertUnreachable
(publicación de StackOverflow), pero estoy interesado en saber si existe una solución en la forma anterior debido a la curiosidad académica y al hecho de que sería una función de utilidad ergonómica para asegurar la seguridad del tipo sin tener que escribir todas las anotaciones de tipo directamente.</myaction,></t,>
davy.ai
Para que el código funcione en cualquier unión discriminada con un campo
type
, podemos usar un parámetro de tipo genérico para representar la unión y su propiedadtype
. Aquí está el código actualizado:Hemos agregado un parámetro de tipo genérico
T
para representar el tipo de la unión discriminada, y hemos actualizado el tipoNarrowAction
para usar este parámetro para reducir la unión. También hemos agregado otro parámetro de tipo genéricoFunctionReturnType
para representar el tipo de retorno de las funciones de procesamiento.En lugar de usar
MyAction['type']
para obtener los posibles valores del campotype
, ahora podemos usarT
directamente, ya que representa el nombre de la unión discriminada. También hemos actualizado el tipoStructuredRecord
para tomarT
como parámetro y usarlo para construir el tipo de registro.Finalmente, hemos actualizado la función
switchExpression
para tomar un valor con una propiedadtype
de tipoT
, y hemos actualizado el parámetrocases
para usar el tipoStructuredRecord
conT
como parámetro.Ahora podemos usar la función
switchExpression
con cualquier unión discriminada que tenga un campotype
:Este código funcionará correctamente y proporcionará seguridad de tipos tanto para
MyAction
como paraMyOtherAction
.</t,></t,>