¿Cómo utilizar `(K | V)[][]` como `ReadonlyArray<[K, V]>` en un mapa?

CorePress2024-01-16  12

Me gustaría usar un (K | V)[][] como ReadonlyArray<[K, V]> en un constructor de mapas. Digamos que nuestra V es un IItem de interfaz y K un número básico.

interface IItem {
    key: number;
    val: string;
}

const items = [[1, {key: 1, val: "a"}]] 
const ze_map = new Map<number, IItem>(items);
console.log(JSON.stringify([...ze_map]));

De forma predeterminada, Typecript percibirá el tipo de elementos como: (número | IItem)[][]. Esto no funcionará y arrojará un error:

El argumento de tipo '(número | { clave: número; val: cadena; })[][]' no se puede asignar al parámetro de tipo 'ReadonlyArray<[{}, {}]>'. Al tipo '(número | { clave: número; val: cadena; })[]' le faltan las siguientes propiedades del tipo '[{}, {}]': 0, 1

Afortunadamente para nosotros, podemos forzar el tipo a Array<[number, IItem]> para complacer al Mapa.

const items2 : Array<[number, IItem]> = [[1, {key: 1, val: "a"}]] 
const ze_map2 = new Map<number, IItem>(items2);
console.log(JSON.stringify([...ze_map]));

Esto funciona como se esperaba. Muy bien, pasemos a mi problema. ¿Qué pasa si no podemos forzar el tipo?

const arr = [
    { key: 3, val: 'bar' },
    { key: 5, val: 'world' }
];
const result: Array<[number, IItem]> = arr.map(i => ([i.key, i]));

const ze_map3 = new Map(result);
console.log(JSON.stringify([...ze_map]));

Esto no funcionaráporque aquí (número | { clave: número; val: cadena; })[][] no es Array<[número, IItem]>. Entonces, ¿cómo usar (K | V)[][] como ReadonlyArray<[K, V]> en un mapa?

Puedes probar todo el código online

Leí ambos Cómo definir un mapa con correlación entre un tipo de clave y un tipo de valor, mientras que ambos son uniones y Typescript Map<enum, set<enum>> "Ninguna sobrecarga coincide con esta llamada", pero no entiendo por qué. sin encontrar la solución. Puede que me haya perdido la solución.

También leí la entrada de MDN en Map que dice que debería funcionar como se esperaba y si lo intentas en Vanilla JS funciona como se esperaba:

var arr = [
    { key: 3, val: 'bar' },
    { key: 5, val: 'world' }
];

var result = arr.map(i => [i.key, i]);
const ze_map = new Map(result);

console.log(JSON.stringify([...ze_map])); 

¡Pruébalo online!



------------------------------------

El problema es el cuerpo de la devolución de llamada .map. Siempre que una función devuelve una matriz, Typecript interpretará el tipo de retorno como un tipo de matriz normal en lugar de un tipo de tupla.

evitar como

La afirmación as funcionará, pero puede ser peligrosa porque le dice a Typecript "considere este tipo como [número, IItem] incluso si realmente no lo es". en lugar de decirle a Typescript "Espero que este tipo sea [número, IItem], así que asegúrese de que así sea". En su situación, el as no es necesario porque realmente es del tipo [número, IItem]. Sólo necesitamos obtener un texto mecanografiado para interpretarlo de esa manera.

Establecer un genérico

La forma más sencilla de hacerlo es configurando el parámetro de tipo genérico en la función .map. La variable genérica aquí establece eltipo de retorno de la devolución de llamada, por lo que lo configuramos en [número, IItem].

const result = arr.map<[number, IItem]>(i => [i.key, i]); // result has type [number, IItem][]

const ze_map = new Map(result); // no errors :)
Externalizar la devolución de llamada

Otro enfoque es definir la devolución de llamada de su mapa i => [i.key, i] como su propia función para que pueda anotar el tipo de retorno.

const toTuple = (i: IItem): [number, IItem] => [i.key, i];

const result = arr.map(toTuple); // result has type [number, IItem][]

const ze_map = new Map(result); // no errors :)

Enlace al patio de juegos mecanografiado



------------------------------------

Creo que puede haber una mejor manera de lograr esto, pero puedes forzar el tipo lanzando la rutina del mapa:

const arr = [
    { key: 3, val: 'bar' },
    { key: 5, val: 'world' }
];
const result = arr.map(i => ([i.key, i] as [number, IItem])); // here come the trick
const ze_map3 = new Map(result);
console.log(JSON.stringify([...ze_map3]));

La palabra clave as es una afirmación de tipo que le dice a tsc que considere el objeto como otro tipo (es decir, nuestro [K, V]) distinto del tipo que el compilador infiere que es el objeto (es decir, (K | V)[]).

La buena noticia es que as [K, V] es totalmente transparente en la salida js:

"use strict";
const arr = [
    { key: 3, val: 'bar' },
    { key: 5, val: 'world' }
];
const result = arr.map(i => [i.key, i]);
const ze_map3 = new Map(result);
console.log(JSON.stringify([...ze_map3]));

Pruébelo en línea

fuente

Su guía para un futuro mejor - libreflare
Su guía para un futuro mejor - libreflare