podemos llegar a... ??
type Provider = \\\"PROVIDER A\\\" | \\\"PROVIDER B\\\";type ProviderAOpts = { ... };type ProviderBOpts = { ... };function connect( ...[provider, options]: | [\\\"PROVIDER A\\\", ProviderAOpts] | [\\\"PROVIDER B\\\", ProviderBOpts]) { switch (provider) { case \\\"PROVIDER A\\\": // options is ProviderAOpts ✅ case \\\"PROVIDER B\\\": // options is ProviderBOpts ✅ ... }}
connect(\\\"PROVIDER A\\\", { ... });connect(\\\"PROVIDER B\\\", { ... }); ^ autocomplete works ✅
Entonces la cuestión es que estamos desestructurando una tupla (matriz) con los tipos exactos que queremos.
El único inconveniente, si somos exigentes, agregar más pares a la tupla... podemos extraer un tipo genérico aquí:
type Provider = \\\"PROVIDER A\\\" | \\\"PROVIDER B\\\";type ProviderAOpts = { ... };type ProviderBOpts = { ... };type ProviderOpts = { \\\"PROVIDER A\\\": ProviderAOpts; \\\"PROVIDER B\\\": ProviderBOpts;};// solves to // [\\\"PROVIDER A\\\", ProviderAOpts] | [\\\"PROVIDER B\\\", ProviderBOpts]type ConnectOptions = { [K in keyof ProviderOpts]: [K, ProviderOpts[K]];}[keyof ProviderOpts]; function connect(...[provider, options]: ConnectOptions) { switch (provider) { case \\\"PROVIDER A\\\": // options is ProviderAOpts ✅ case \\\"PROVIDER B\\\": // options is ProviderBOpts ✅ ... }}
connect(\\\"PROVIDER A\\\", { ... });connect(\\\"PROVIDER B\\\", { ... }); ^ autocomplete works ✅
type Provider = \\\"PROVIDER A\\\" | \\\"PROVIDER B\\\";type ProviderAOpts = { ... };type ProviderBOpts = { ... };type ProviderOpts = { \\\"PROVIDER A\\\": ProviderAOpts; \\\"PROVIDER B\\\": ProviderBOpts;};// aux type to extract the key and the options from ProviderOptstype KeyOpts= { [K in keyof T]: [K, T[K]];}[keyof T];function connect(...[provider, options]: KeyOpts ) { switch (provider) { case \\\"PROVIDER A\\\": // options is ProviderAOpts ✅ case \\\"PROVIDER B\\\": // options is ProviderBOpts ✅ ... }}
connect(\\\"PROVIDER A\\\", { ... });connect(\\\"PROVIDER B\\\", { ... }); ^ autocomplete works ✅
¿Gracias a Mateusz y Lenz por la ayuda?.
gracias por leer?.
","image":"http://www.luping.net/uploads/20241104/17307234156728be5752a02.jpg","datePublished":"2024-11-07T10:48:39+08:00","dateModified":"2024-11-07T10:48:39+08:00","author":{"@type":"Person","name":"luping.net","url":"https://www.luping.net/articlelist/0_1.html"}}Hay un escenario en el que este sentimiento es particularmente intenso: cuando una función toma un parámetro que depende de qué "modo" está activo.
más claro con algún código de ejemplo:
type Provider = "PROVIDER A" | "PROVIDER B"; type ProviderAOpts = { ... }; type ProviderBOpts = { ... }; function connect(provider: Provider, options: ProviderAOpts | ProviderBOpts) { switch (provider) { case "PROVIDER A": // options is ProviderAOpts case "PROVIDER B": // options is ProviderBOpts } }
(Traté de usar nombres más realistas en lugar de foo, goo, dog y cat).
Si ha pasado algún tiempo con TypeScript, puede sospechar que solíamos manejar esto como ProviderAOpts, como ProviderBOpts.
Pero hay un momento en que golpeas la mesa con el puño y dices: "¡No más!"
Lo primero que siempre me viene a la mente en estos casos es utilizar sobrecarga de funciones:
function connect(provider: "PROVIDER A", options: ProviderAOpts): void; function connect(provider: "PROVIDER B", options: ProviderBOpts): void; function connect(provider: Provider, options: ProviderAOpts | ProviderBOpts) { switch (provider) { case "PROVIDER A": // (options as ProviderAOpts) ❌ case "PROVIDER B": // (options as ProviderBOpts) ❌ } }
Lo cual no funciona. La firma de la función no se infiere correctamente. El parámetro de opciones siempre es ProviderAOpts | ProveedorBOpts. que resolverá a la unión común.
Ts no vincula ambos parámetros correctamente.
La siguiente herramienta que pruebo es Escribir predicados:
type ConnectOptions = ProviderAOpts | ProviderBOpts; function isAOptions(options: ConnectOptions): options is ProviderAOpts { return (options as ProviderAOpts).$$$ !== undefined; } function isBOptions(options: ConnectOptions): options is ProviderBOpts { return (options as ProviderBOpts).$$$ !== undefined; } function connect(provider: Provider, options: ConnectOptions) { switch (provider) { case "PROVIDER A": if (isAOptions(options)) { ... } case "PROVIDER B": if (isBOptions(options)) { ... } } ... }
Pero, sinceramente, no solucionamos nada. ¿Acabamos de mover el as debajo de la alfombra? Se introdujeron condiciones adicionales y todavía no vinculamos los parámetros.
Genéricos. Intenté usar genéricos para vincular los parámetros. No funciona:
function connect( provider: T, options: T extends "PROVIDER A" ? ProviderAOpts : ProviderBOpts ) { switch (provider) { case "PROVIDER A": // (options as ProviderAOpts) ❌ case "PROVIDER B": // (options as ProviderBOpts) ❌ } }
Me esforcé mucho y llegué tan lejos
Pero al final ni siquiera importa
Tuve que caer para perderlo todo
Pero al final ni siquiera importa
??
Modificar los parámetros de opciones y agregar el tipo de proveedor funciona:
type Provider = "PROVIDER A" | "PROVIDER B"; type ProviderOptsBase = { provider: Provider; } type ProviderAOpts = ProviderOptsBase & { provider: "PROVIDER A"; ...; }; type ProviderBOpts = ProviderOptsBase & { provider: "PROVIDER B"; ...; }; function connect(options: ConnectOptions) { switch (options.provider) { case "PROVIDER A": // options is ProviderAOpts ✅ case "PROVIDER B": // options is ProviderBOpts ✅ } }
Esta es la solución más común, pero no siempre es posible cambiar la firma de la función. O tal vez simplemente no quieres. ¿Cuestión de principios?.
Gracias a Mateusz Burzyński (@AndaristRake) y Lenz Weber (@phry)
podemos llegar a... ??
type Provider = "PROVIDER A" | "PROVIDER B"; type ProviderAOpts = { ... }; type ProviderBOpts = { ... }; function connect( ...[provider, options]: | ["PROVIDER A", ProviderAOpts] | ["PROVIDER B", ProviderBOpts] ) { switch (provider) { case "PROVIDER A": // options is ProviderAOpts ✅ case "PROVIDER B": // options is ProviderBOpts ✅ ... } }
connect("PROVIDER A", { ... }); connect("PROVIDER B", { ... }); ^ autocomplete works ✅
Entonces la cuestión es que estamos desestructurando una tupla (matriz) con los tipos exactos que queremos.
El único inconveniente, si somos exigentes, agregar más pares a la tupla... podemos extraer un tipo genérico aquí:
type Provider = "PROVIDER A" | "PROVIDER B"; type ProviderAOpts = { ... }; type ProviderBOpts = { ... }; type ProviderOpts = { "PROVIDER A": ProviderAOpts; "PROVIDER B": ProviderBOpts; }; // solves to // ["PROVIDER A", ProviderAOpts] | ["PROVIDER B", ProviderBOpts] type ConnectOptions = { [K in keyof ProviderOpts]: [K, ProviderOpts[K]]; }[keyof ProviderOpts]; function connect(...[provider, options]: ConnectOptions) { switch (provider) { case "PROVIDER A": // options is ProviderAOpts ✅ case "PROVIDER B": // options is ProviderBOpts ✅ ... } }
connect("PROVIDER A", { ... }); connect("PROVIDER B", { ... }); ^ autocomplete works ✅
type Provider = "PROVIDER A" | "PROVIDER B"; type ProviderAOpts = { ... }; type ProviderBOpts = { ... }; type ProviderOpts = { "PROVIDER A": ProviderAOpts; "PROVIDER B": ProviderBOpts; }; // aux type to extract the key and the options from ProviderOpts type KeyOpts= { [K in keyof T]: [K, T[K]]; }[keyof T]; function connect(...[provider, options]: KeyOpts ) { switch (provider) { case "PROVIDER A": // options is ProviderAOpts ✅ case "PROVIDER B": // options is ProviderBOpts ✅ ... } }
connect("PROVIDER A", { ... }); connect("PROVIDER B", { ... }); ^ autocomplete works ✅
¿Gracias a Mateusz y Lenz por la ayuda?.
gracias por leer?.
Descargo de responsabilidad: Todos los recursos proporcionados provienen en parte de Internet. Si existe alguna infracción de sus derechos de autor u otros derechos e intereses, explique los motivos detallados y proporcione pruebas de los derechos de autor o derechos e intereses y luego envíelos al correo electrónico: [email protected]. Lo manejaremos por usted lo antes posible.
Copyright© 2022 湘ICP备2022001581号-3