Hur man skriver ett objekt med generiska nycklar av olika typer i TypeScript
Hur skulle du skriva dessa objekt i TypeScript?
Jag har en speciell "datetime"-nyckel som är ett datum, resten av nycklarna är nummer. Men jag vet inte i förväg vilka nycklar som kommer att vara inställda på varje objekt. Exempelvärden:
type Metrics = ??????
const example1: Metrics = {
datetime: new Date(),
activity: 12.34,
min: 12.34,
max: 12.34,
}
const example2: Metrics = {
datetime: new Date(),
avg: 12.34,
}
Här är vad jag försökte:
type Metrics = {
datetime: Date,
[key: string]: number,
}
// ERROR on the type definition:
// Property 'datetime' of type 'Date' is not assignable to string index type 'number'
type Metrics = {
datetime: Date
} & {
[key: string]: number
}
// ERROR on the variable assignment:
// Type '{ datetime: Date; activity: number; min: number; max: number; }' is not assignable to type 'Metrics'.
// Type '{ datetime: Date; activity: number; min: number; max: number; }' is not assignable to type '{ [key: string]: number | null; }'.
// Property 'datetime' is incompatible with index signature.
// Type 'Date' is not assignable to type 'number'.
Svar #1
Du kommer att vilja ha något som använder Record<Exclude<K,"datetime">, number> för att mappa alla nuvarande nycklar till typen number men utesluta nyckeln "datetime". Eftersom detta inte nödvändigtvis är möjligt med en vanlig indextyp är det troligen bäst att använda en hjälpfunktion som begränsar typen som en generisk:
type Metrics<K extends keyof any> = { datetime: Date } & Record<Exclude<K, "datetime">, number>;
function makeMetrics<T extends Metrics<keyof T>>(metrics: T): T {
return metrics;
}
const example1 = makeMetrics({
datetime: new Date(),
activity: 12.34,
min: 12.34,
max: 12.34,
});
const example2 = makeMetrics({
datetime: new Date(),
avg: 12.34,
});
Detta innebär att båda exemplen endast kommer att tillåta de nycklar som är närvarande när det definieras, att lägga till extra nycklar senare skulle inte fungera, för att tillåta det skulle du behöva något som "alla nycklar utom datetime är siffror" vilket inte är möjligt såvitt jag vet.
Svar #2
Det är lite hackigt men jag hittade ett sätt att göra detta för att lägga till en typ baserat på ditt objekt. Detta gör att du kan exportera ett typat objekt.
- Definiera ditt objekt utan en typ
const example1 = {
datetime: new Date(),
activity: 12.34,
min: 12.34,
max: 12.34,
}
- Skapa en typ från det objektet men exkludera de specifika nycklar du vill ändra
type AllKeys<T> = {
[Property in keyof T]: number;
};
- Skapa en intersection-typ med typen från 2 och uteslut din datetime-nyckel. Observera att "typeof example1" ger dig alla nycklar från example1.
type NewType = AllKeys<Omit<typeof example1, "datetime">> & { datetime: Date};
- exportera det typade objektet
export const example: NewType = example1;
