Búsqueda de sitios web

typescript - Usando un tipo mapeado en una tupla


¿Cómo escribiría esto en TypeScript:

function arrayToObject(array, mapper) {
    const result = {};
    for(const item of array) {
        const [key, value] = mapper(item);
        result[key] = value;
    }
    return result;
}

// returns { '1': 2, '2': 4, '3': 6 }
arrayToObject([1,2,3], v => [v, v * 2])

Específicamente, quiero que TypeScript se asegure de que el resultado de la tupla de mapper sea una clave y un valor válidos. Esto no funciona:

function arrayToObject<TItem, TObject>(
    array: TItem[],
    mapper: (t: TItem) => [P in keyof TObject, TObject[P]]
)

Respuesta #1

Suponiendo TS3.4 y usando las aserciones const, (la técnica puede modificarse para soportar TS3.3 y no requerir tales aserciones, pero es tedioso), la tipificación más fuerte que se me ocurre es:

function arrayToObject<A, K extends keyof any, V>
  (array: readonly A[], mapper: (x: A) => readonly [K, V]): { [P in K]: V } {
  const result = {} as { [P in K]: V };
  for (const item of array) {
    const [key, value] = mapper(item);
    result[key] = value;
  }
  return result;
}

const obj1 = arrayToObject([1, 2, 3] as const, v => [v, v * 2] as const)
// const obj1: {1: number, 2: number, 3: number}

const obj2 = arrayToObject(["a", "b", "c"] as const, k => [k, 0] as const);
// const obj2: {a: 0, b: 0, c: 0}

Eso al menos preservará cualquier valor de clave literal que el compilador conozca en el tipo de retorno del mapeador. El sistema de tipos no tiene mucho soporte para tipos de rango superior fuera de algunas mejoras específicas de funciones genéricas, así que me resulta difícil pensar en alguna manera para que el compilador entienda que, por ejemplo,

const obj3 = arrayToObject(["a","b","c"] as const, k => [k, k] as const)

terminará siendo del tipo {a: "a", b: "b", c: "c"} en lugar del tipo todavía correcto pero no muy específico {a: "a"|"b"|"c", b: "a"|"b"|"c", c: "a"|"b"|"c"}. Bueno, ni modo.

¡Espero que eso ayude; buena suerte!