typescript - keyof array-typen i TS är konstig


interface PPP {
  1: number
  2: string
  4: boolean
}

type Map1<T extends any[]> = {[K in keyof T]: T[K] extends keyof PPP ? PPP[T[K]] : never}
type Map2<T extends any[], U extends keyof T> = {[K in U]: T[K] extends keyof PPP ? PPP[T[K]] : never}

type Y1 = Map1<[1, 2]> // type Y1 = [number, string]
type Y2 = Map2<[1, 2], keyof [1, 2]> // type Y2 is not same as Y1
// type Y2 = {
//     [x: number]: string | number;
//     0: number;
//     1: string;
//     length: string;
//     toString: never;
//     toLocaleString: never;
//     pop: never;
//     push: never;
//     concat: never;
//     join: never;
//     reverse: never;
//     shift: never;
//     slice: never;
//     sort: never;
//     ... 20 more ...;
//     flat: never;
// }

Som jag vet om TS skulle jag tro att Map1 är motsvarande Map2, men Y1 är inte samma som Y2, så det måste finnas något som jag inte vet.

Fråga: vad är orsaken som orsakar denna skillnad.

Svar #1

Typescript kör typkontrollen vid byggtid, inte vid körtid. Tänk på följande:

const a: (1 | 2 | 4)[] = [1, 1, 1, 1, 1]
const b: (1 | 2 | 4)[] = [1, 2, 4]

Både a och b är giltiga (eftersom de är arrayer som innehåller element som antingen är 1, 2 eller 4).

keyof T i TypeScript listar alla egenskaper av typen T. För en array kan du också köra array.toString() eller array.push(). Därför visas toString och push som giltiga typer.

Typescript kommer aldrig att kunna lista, vid kompileringstid, objekten i arrayen som din typ.


Uppdatera

Faktiskt, det finns en bugg i TypeScript angående det olika beteendet mellan keyof T som kommer från type argument och keyof T i typvärdet. Bugglänk

Du kan dock kringgå detta problem med en Exclude

Länk till lekplatsen

interface PPP {
  1: number
  2: string
  4: boolean
}

type Map1 < T extends any[] > = {
  [K in keyof T]: T[K] extends keyof PPP ? PPP[T[K]] : never
}
type Map2 < T extends any[], U extends keyof T > = {
  [K in U]: T[K] extends keyof PPP ? PPP[T[K]] : never
}

type Map3 < T extends any[], U extends keyof T > = {
  [K in Exclude < U, keyof any[] > ]: T[K] extends keyof PPP ? PPP[T[K]] : never
}




type Y1 = Map1 < [1, 2] > // type Y1 = [number, string]
  type Y2 = Map2 < [1, 2], keyof[1, 2] > // type Y2 is not same as Y1
  type Y3 = Map3 < [1, 2], keyof[1, 2] > // Same as Y3

Relaterade artiklar