typescript - härleda generisk från egenskaper


Låt oss ha ett gränssnitt som beskriver ett betygsformulär med exempelresultat för att förhandsgranska hur det kommer att se ut efter att alla deltagare har skickat in formuläret:

interface Rating {
  maxRating: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10;
  exampleResults: number[]; // E.g. [0, 0, 0, 2, 5] = 2 votes for 4*, 5 votes for 5*.
}

Denna naiva implementation kontrollerar inte om exampleResultss längd är densamma som värdet av maxRating. Så istället försökte jag:

interface Rating<T extends 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10> {
  maxRating: T;
  exampleResults: Tuple<number, T>; // Touple definition is omitted for simplification
}

men när jag försöker använda det:

const rating: Rating = {
  maxRating: 5,
  exampleResults: [0, 0, 0, 1, 4],
}

Jag får det här felet:

Generisk typ 'Rating' kräver 1 typargument.

Men uppenbarligen kan TypeScript härleda typ genom att läsa värdet av maxRating.

Nu förstår jag om TypeScript inte stöder sådan härledning idag, men finns det ett annat sätt jag kan använda för att begränsa typen av en egenskap baserat på värdet av en annan?

Tack för alla förslag!

Svar #1

För att göra det behöver du härleda/veta maxRating i förväg. Tänk på detta exempel:

type Max = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10;

type Tuple<Length extends Max, Result extends number[] = []> =
  Result['length'] extends Length
  ? Result
  : Tuple<Length, [...Result, number]>

interface Rating<M extends Max> {
  maxRating: M
  exampleResults: Tuple<M>
}

const rating: Rating<5> = {
  maxRating: 5,
  exampleResults: [1, 2, 3, 4, 5] // ok
}

const rating2: Rating<5> = {
  maxRating: 5,
  exampleResults: [1, 2, 3, 4, 5, 6] // expected error
}

Lekplats

TypeScript kan inte härleda maxRating från objektet, som du gjorde.

Det finns ett annat sätt att härleda maxRating. Du kan använda en funktion:

const handler = <N extends Max>(r: Rating<N>) => r

handler({ maxRating: 10, exampleResults: [1, 2, 3, 4, 5, 0, 0, 0, 0, 0] }) // ok