Tengo una función que definí para el mapeo de valores de una ITERABLE, muy similar a Array.prototype.map(), pero lo he simplificado para que este ejemplo sea básicamente equivalente, ya que esos detalles son irrelevantes:

function select <T, U> (array: T[], selectFn: (value: T, index: number, array: T[]) => U, thisArg?: unknown): U[] {
  return array.map(selectKeyOrFn, thisArg);
}

select(['foo', 'bar'], s => s.length);

Hasta aquí todo bien. Todo compila. Ahora quiero aceptar una función, o un keyof T para el segundo argumento. No estoy seguro de la mejor manera de definir los parámetros de tipo para esta firma de sobrecarga, pero aquí fue mi enfoque fallido:

type KeyOfValue<T, U extends T[keyof T]> = U extends T[infer K] ? K : never;

function select <T, U extends T[keyof T]> (array: T[], selectKey: KeyOfValue<T, U>): U[]
function select <T, U> (array: T[], selectFn: (value: T, index: number, array: T[]) => U, thisArg?: unknown): U[]
function select <T, U> (array: T[], selectKeyOrFn: (U extends T[keyof T] ? KeyOfValue<T, U> : never) | ((value: T, index: number, array: T[]) => U), thisArg?: unknown): U[] {
  return typeof selectKeyOrFn === 'function'
    ? array.map(selectKeyOrFn, thisArg)
    : array.map(value => value[selectKeyOrFn]);
}

select(['foo', 'bar'], 'length');
select(['foo', 'bar'], s => s.length);

El problema principal parece ser que mi tipo KeyOfValue no permite la sintaxis que estoy tratando de usar en infer K como un índice de T:

El tipo 'k' no se puede utilizar para indexar el tipo 't'.

¿Cómo puedo prototipo con éxito esta función de sobrecarga? Preferiría no recurrir a un tipo de ayudante como KeyOfValue, pero en este momento no puedo pensar en un camino alrededor de esto.

-1
Patrick Roberts 13 jul. 2019 a las 21:35

1 respuesta

La mejor respuesta

Creo que estás sobreplicando las cosas aquí con los tipos condicionales. Cabullir un parámetro de tipo para la clave y usar una consulta de tipo funcionará mejor. También para la firma de implementación, puede tener una amalgamación de las dos firmas sobrecargadas, sin necesidad de usar el mismo U como un tipo de devolución:

function select <T, K extends keyof T> (array: T[], selectKey: K): T[K][]
function select <T, U> (array: T[], selectFn: (value: T, index: number, array: T[]) => U, thisArg?: unknown): U[]
function select <T, U, K extends keyof T> (array: T[], selectKeyOrFn: K | ((value: T, index: number, array: T[]) => U), thisArg?: unknown): T[K][] | U[] {
  return typeof selectKeyOrFn === 'function'
    ? array.map(selectKeyOrFn, thisArg)
    : array.map(value => value[selectKeyOrFn]);
}

select(['foo', 'bar'], 'length');
select(['foo', 'bar'], s => s.length);
1
Titian Cernicova-Dragomir 13 jul. 2019 a las 19:00