Estoy usando Zod y tengo una matriz que contiene diferentes objetos usando una unión . Después de analizarlo, quiero recorrer cada elemento y extraer su tipo "real"/cortar los otros tipos.
Al verificar las propiedades de un objeto específico, el siguiente código funciona bien:
const objectWithNumber = zod.object({ num: zod.number() }); const objectWithBoolean = zod.object({ isTruthy: zod.boolean() }); const myArray = zod.array(zod.union([objectWithNumber, objectWithBoolean])); const parsedArray = myArray.parse([{ isTruthy: true }, { num: 3 }]); parsedArray.forEach((item) => { if ("num" in item) { console.info('objectWithNumber:', item); // TS knows about it => syntax support for objectWithNumber } else if ("isTruthy" in item) { console.info('objectWithBoolean:', item); // TS knows about it => syntax support for objectWithBoolean } else { console.error('unknown'); } });Una alternativa sería utilizar sindicatos discriminados para ello.
const objectWithNumber = zod.object({ type: zod.literal("objectWithNumber"), num: zod.number() }); const objectWithBoolean = zod.object({ type: zod.literal("objectWithBoolean"), isTruthy: zod.boolean() }); const myArray = zod.array(zod.discriminatedUnion("type", [ objectWithNumber, objectWithBoolean ])); const parsedArray = myArray.parse([{ type: "objectWithBoolean", isTruthy: true }, { type: "objectWithNumber", num: 3 }]); parsedArray.forEach(item => { if (item.type === "objectWithNumber") { console.info('objectWithNumber:', item); // TS knows about it => syntax support for objectWithNumber } else if (item.type === "objectWithBoolean") { console.info('objectWithBoolean:', item); // TS knows about it => syntax support for objectWithBoolean } else { console.error('unknown'); } });pero creo que no entendí bien este concepto porque solo hay más código para escribir (siempre puedo agregar una propiedad compartida e inspeccionarla). Cualquier ayuda en esto es muy apreciada :)
¿Hay mejores formas de identificar un esquema específico?
Si lo entendí correctamente, su pregunta se reduce a "¿Por qué uno debería usar uniones discriminadas en lugar de campos compartidos combinados con campos opcionales". ( zod.js simplemente eleva este concepto al tiempo de ejecución proporcionando funcionalidad de validación). En su ejemplo, de hecho, no hay razón para usar un discriminador (propiedad de type ), porque cada objeto tiene solo una propiedad no opcional que es mutuamente excluyente entre tipos y se puede usar fácilmente para distinguir tipos. Sin embargo, el problema principal con este código es que los nombres de los objetos (o formas/estructuras) no comunican ninguna intención , por eso es difícil ver los beneficios de las uniones discriminadas.
Puede pensar en la unión discriminada como un tipo que describe una familia de objetos y proporciona un mecanismo unificado (en forma de propiedad discriminatoria) para identificarlos. Este enfoque es menos frágil que verificar la existencia de algunas propiedades seleccionadas manualmente (como, por ejemplo, la propiedad num ). ¿Qué pasa si num por alguna razón se vuelve opcional? Entonces se romperá su cheque por la existencia de num. Otro argumento a favor de la unión discriminada es disminuir el número de propiedades opcionales. Compare dos ejemplos siguientes:
// shared and optional fields instead of discriminated union type Vehicle = { name: string; combustionEngine?: PetrolEngine | DieselEngine; tankCapacity?: number; electricEngine?: ElectricEngine; batteryCapacity?: number; } type Vehicle = | GasolineCar | ElectricCar type = GasolineCar { kind: "gasolineCar"; name: string; engine: PetrolEngine | DieselEngine; tankCapacity: number; } type ElectricCar = { kind: "electricCar" name: string; engine: ElecticEngine; batteryCapacity: number; } El ejemplo con unión discriminada produce un código mucho más descriptivo. No tiene que agregar varias comprobaciones para los campos opcionales; en su lugar, simplemente identifique (lo antes posible) el tipo por discriminador y pase el objeto a una función/método que acepte el tipo más limitado ( GasolineCar o ElectricCar en lugar de Vehicle ).