Here is a simple illustration of what I am trying to convey:
type Shape = 'square' | 'circle';
type Params<S extends Shape> = S extends 'square' ? { side: number } : { radius: number };
function getArea<S extends Shape>(shape: S, params: Params<S>) {
switch (shape) {
// It is important to specify the type of Param here to prevent TypeScript complaints.
case 'square': return Math.pow((params as Params<'square'>).side, 2);
case 'circle': return (params as Params<<'circle'>).radius * Math.PI;
// Including a default case to address TypeScript error about all code paths not returning a value.
default: return -1;
}
}
// Interestingly, when calling this function, TypeScript correctly enforces the generic constraint. So an error occurs in this scenario:
getArea('square', { radius: 1 }) // This rightly indicates that { radius: 1 } cannot be assigned to { side: number }.
The necessity for a default case implies that <S extends Shape>
transforms the generic type S
into something more complex. When experimenting with calling getArea
using a shape parameter that is not part of Shape
, a correct error was returned stating the argument is not assignable to the Shape
type.
Externally, TypeScript identifies that the 'shape' parameter should adhere to the Shape
type, and 'params' should match the specified Params type. However, within the function, TypeScript does not seem to recognize that 'shape' is of type Shape
or 'params' is of type Params<typeof shape>
.