Let's have an interface that describes a rating form with example results to preview how it will look after all participants have submitted the form:
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*.
}
This naive implementation doesn't check whether exampleResults
's length is the same as the value of maxRating
. So instead I tried:
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
}
but when I try to use it:
const rating: Rating = {
maxRating: 5,
exampleResults: [0, 0, 0, 1, 4],
}
I get this error:
Generic type 'Rating' requires 1 type argument(s).
But obviously TypeScript can infer type by reading the value of maxRating
.
Now I get it if TypeScript doesn't support such inference today, but is there another way I can use to restrict type of one property based on value of another?
Thanks for any suggestions!
CodePudding user response:
In order to do that, you need to infer/to know maxRating
upfront. Consider this example:
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
}
TypeScript is unable to infer maxRating
from the object, like you did.
There is another way to infer maxRating
. You can use a function:
const handler = <N extends Max>(r: Rating<N>) => r
handler({ maxRating: 10, exampleResults: [1, 2, 3, 4, 5, 0, 0, 0, 0, 0] }) // ok
CodePudding user response:
You can achieve this by using identity function:
const createRating = <T extends 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10>(rating: Rating<T>) => rating
const rating = createRating({
maxRating: 5,
exampleResults: [0, 0, 0, 1, 4],
})