typescript - Generics: TS2345 Argument av typen 'T[keyof T]' kan ikke tildeles parameteren av typen 'Date' hvor
Jeg må ofte sortere objekter etter egenskaper, så jeg skrev følgende enkle hjelpefunksjon:
static sort = <T extends {}>(list: Array<T>, field: keyof T, descending: boolean = true) =>
list.sort((a, b) => {
if (a[field] > b[field]) {
return descending ? -1 : 1;
}
if (a[field] < b[field]) {
return descending ? -1 : 1;
}
return 0;
});
Jeg må ofte sortere objekter etter en datolignende egenskap på objektet, men datoene jeg mottar er gjort om til strenger i formatet "01-Apr-2018", så jeg må først konvertere dem til en dato.
Så, jeg utvidet det ovenstående med:
static byDate = <T extends {}>(list: Array<T>, field: keyof T, descending: boolean = true) =>
list.sort((a, b) => {
if (new Date(a[field]).getTime() > new Date(b[field]).getTime()) {
return descending ? -1 : 1;
}
if (a[field] < b[field]) {
return descending ? 1 : -1;
}
return 0;
});
som resulterer i
TS2345: Argument av type 'T[keyof T]' kan ikke tildeles parameter av type 'Date'
på a[field] og b[field].
Når jeg sporet WebStorms/TypeScript-tjenestes implementering av Date som ble valgt, la jeg merke til at i new Date("hello"), velger den følgende grensesnitt fra lib.es5.d.ts:
interface DateConstructor {
new(): Date;
new(value: number): Date;
new(value: string): Date; // THIS ONE (which I expect)
new(year: number, month: number, date?: number, hours?: number, minutes?: number, seconds?: number, ms?: number): Date;
(): string;
readonly prototype: Date;
parse(s: string): number;
UTC(year: number, month: number, date?: number, hours?: number, minutes?: number, seconds?: number, ms?: number): number;
now(): number;
}
Men i min sorteringskomparator velger den grensesnittet fra lib.es2015.core.d.ts, som bare har:
interface DateConstructor {
new (value: Date): Date;
}
Forsøk på å påstå med new Date(a[field] as string) eller as Date har ingen effekt.
Jeg bruker TypeScript 2.7.2 (påkrevd av mitt nåværende Angular 6.0.9-prosjekt og kan ikke endres).
Så :
Hvorfor velger
tscden ene fremfor den andre i disse kontekstene?Hvordan kan jeg fastslå hvilken jeg ønsker at den skal bruke, eller i det minste få koden min til å kompilere samtidig som jeg beholder
field: keyof T-parameteren?
Svar #1
Jeg kan ikke teste på 2.7.2 akkurat nå, men hovedproblemet er at TypeScript ikke tror at a[field] nødvendigvis er en string. Og det gir mening fordi a er av generisk type T, og field er av typen keyof T, så alt den vet er at a[field] er T[keyof T]. Det kan være string, eller det kan være noe som du ikke kan kalle new Date() på.
Hvis du vet sikkert at du bare vil kalle byDate på noe der a[field] er en string, kan du endre generics slik:
static byDate = <K extends keyof any, T extends Record<K, string>>(
list: Array<T>,
field: K,
descending: boolean = true
) => { ... }
Det burde få feilen til å forsvinne... den vet nå at a[field] er typen T[K], hvor T utvider Record<K, string>, også kjent som {[P in K]: string}, eller «et objekt hvis nøkler er i K og hvis verdier er string». Så T[K] utvider string og det er fornøyd.
Ovenstående bør fungere på TypeScript 2.7.2, men gi meg beskjed hvis du støter på et problem.
Håper det hjelper. Lykke til!
