Búsqueda de sitios web

typescript - transformar tupla nombrada en objeto


¿hay alguna manera de transformar una tupla nombrada/etiquetada?

type Tup = [a: string, b: number];

objetar

type Obj = {
     a: string;
     b: number;
}

el último intento no funciona

type Map1<Container extends unknown[]> = {
     [K in keyof Container]: Container[K];
};

type Map2<Container extends Record<PropertyKey, any>, Replace> = {
     [K in keyof Container]: Replace;
};

let c1 : Map1<Tup> = {
     'a' : 's',
     'b' : 1,
};

let c2 : Map2<Tup> = {
     'a' : 's',
     'b' : 1,
};

gracias

Respuesta #1

El soporte de TypeScript para elementos de tupla con etiquetas, tal como se implementa en microsoft/TypeScript#38234, intencionalmente no proporciona ninguna forma de observar estas etiquetas en el sistema de tipos. Desde la solicitud de incorporación de cambios que implementa:

Los nombres no afectan la asignabilidad de ninguna manera, y existen solo para documentación e información rápida.

También vea este comentario en un problema relacionado donde el líder de desarrollo del equipo de TS dice:

[Las etiquetas de tupla] son únicamente para fines de visualización (similar a los nombres de parámetros en los tipos de función)

El tipo de tupla [a: string, b: number] y el tipo de tupla [c: string, d: number] son indistinguibles desde el punto de vista del sistema de tipos. La única diferencia entre ellos es cómo se muestran en un IDE o se presentan como documentación. Son más como comentarios que como código.

Si [a: string, b: number] y [c: string, d: number] son del mismo tipo, entonces cualquier función de tipo TupleToObject<T> que transforme [a: string, b: number] en {a: string, b: number} también necesitaría transformar [c: string, d: number] en {a: string, b: number}.

Así que realmente no hay esperanza de implementar lo que estás buscando. Las etiquetas de tupla están intencionalmente ocultas del sistema de tipos. Simplemente no hay conexión entre las etiquetas de tupla y los tipos de literales de cadena.


En cambio, la única manera posible de avanzar es que especifiques manualmente las claves literales de cadena que deseas usar para cada elemento de la tupla de entrada. Así que el inviable TupleToObject<[a: string, b: number]> se convierte en algo como TupleToObject<[a: string, b: number], ["a", "b"]>. Puedes ver cómo esto ya no se preocupa por las etiquetas de la tupla; TupleToObject<[foo: string, bar: number], ["a", "b"]> necesariamente produciría el mismo tipo.

Está bien, entonces ¿cómo alguien implementaría TupleToObject? Aquí hay una forma potencial:

type TupleToObject<T extends readonly any[],
  M extends Record<Exclude<keyof T, keyof any[]>, PropertyKey>> =
  { [K in Exclude<keyof T, keyof any[]> as M[K]]: T[K] };

Toma dos parámetros de tipo: T está destinado a ser el tipo de tupla, mientras que M está destinado a ser un tipo de tupla de al menos la misma longitud cuyos valores son claves. Las restricciones genéricas extends readonly any[] y extends Record<Exclude<keyof T, keyof any[]>, PropertyKey>> más o menos hacen cumplir esto.

Luego, para cada clave literal que parezca numérica del tuple T, usamos el remapeo de claves para reemplazar esa clave con la entrada correspondiente de M. La única parte extraña es que en lugar de iterar sobre keyof T, usamos Exclude<keyof T, keyof any[]>. Los tipos tuple son arrays, y contendrán nombres de métodos de array como "map". No queremos preocuparnos por "map", así que el tipo utilitario Exclude<T, U> nos permite decir "todas esas claves de T que no son claves generales de array". Para los tuples, esto tiende a dejar solo "0", "1", etc.


Vamos a probarlo:

type Tup = [a: string, b: number];

type Obj = TupleToObject<Tup, ["a", "b"]>
/* type Obj = {
    a: string;
    b: number;
} */

¡Se ve bien!

Enlace de Playground al código

Artículos relacionados