TypeScript - Cum să reprezinți o semnătură de index ca tip generic


Semnăturile de index în TypeScript sunt definite astfel:

Dicționar

[key: string]: T

Tablou

[index: number]: T

Acestea ar putea fi încapsulate în câteva tipuri simple, reutilizabile:

type DictionaryIndex<T> = {
    [key: string]: T
}

type ArrayIndex<T> = {
    [index: number]: T
}

Acum vreau să împachetez acestea într-un singur tip. Am încercat asta:

type Index<TKey extends string|number, TValue> = {
    [key: TKey]: TValue
}

Acest lucru nu se compilează din cauza următoarei erori:

Un parametru de semnătură de index trebuie să fie de tip 'string' sau 'number'.

Acest lucru nu este posibil?

De ce naiba?

Pentru că

foo(obj: Index<string, Bar>)
foo(obj: Index<string, Bar> & Fooable<string>)

arată mai îngrijit decât

foo(obj: { [key: string]: Bar })
foo(obj: { [key: string]: Bar, canFoo: (foo: string) => Bar })

Răspuns #1

Întrebare interesantă!
Cred că motivul este că acesta este într-adevăr un caz-limită pentru compilator, care probabil nu a fost implementat pentru că nu merită efortul.

Cheile proprietăților de index pot fi doar numere, asta e evident, iar cheile proprietăților obiectului pot fi doar șiruri sau numere. Deci, la final, constrângerea ta doar afirmă ceea ce este de fapt și ceea ce ar trebui gestionat de compilator printr-un caz special.
Deci, din nou, presupun că Sfântul Anders a renunțat la acest efort ;-)

dar de ce să nu faci asta

type StrIndex<TValue> = {
    [key: string]: TValue
}

type NumIndex<TValue> = {
    [key: number]: TValue
}

foo(obj: StrIndex<Bar>)
foo(obj: StrIndex<Bar> & Fooable<string>)

Nu este la fel de ordonat ca soluția ta, dar ca un compromis pare OK, mai ales deoarece setul de tipuri XXXIndex este limitat la 2

Răspunsul #2

O soluție alternativă este să folosiți utilitarul Record.

foo(obj: Record<string, Bar>)

https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type