I have a generic interface that takes a type argument that must extend another generic type. For example :
export interface IPoint<TX, TY>
{
x: TX;
y: TY;
}
export interface ISeries<TPoint extends IPoint>
{
points: Array<TPoint>;
}
Here, I must specify TX
and TY
for IPoint
.
My question is : is there a way to infer those types automatically, for example like this ?
export interface ISeries<TPoint extends IPoint<infer TX, infer TY>>
{
points: Array<TPoint>;
}
The only way to make this work I've found for now is to add TX
and TY
as type parameters for ISeries
, but it's quite impractical because then I have to specify the three types every time I use the interface.
I could also use IPoint<any, any>
, but then I lose the information about the real types of x
and y
.
EDIT : To add some clarification about what I want to achieve, let's consider the following example :
export interface ISeries<TPoint extends IPoint<infer TX, infer TY>>
{
points: Array<TPoint>;
transformYValues?: (yValue: TY) => number;
}
Here I would need TY
to strongly type transformYValues
.
Thanks for your help
EDIT 2 : Found a solution (thanks captain-yossarianfromUkraine).
export interface ISeries<TPoint extends IPoint<any, any>>
{
points: Array<TPoint>;
transformYValues?: (yValue: TPoint['y']) => number;
}
The key here is TPoint['y']
, I wasn't aware of this neat feature called Indexed Access Types (see https://www.typescriptlang.org/docs/handbook/2/indexed-access-types.html).
In summary => use any
typing in the generic type constraints, then use indexed access types to strongly type methods/properties inside the interface.
CodePudding user response:
Consider this example:
export interface Point<X, Y> {
x: X;
y: Y;
}
type Values = string | number
export interface Series<Points extends Point<Values, Values>[]> {
points: Points;
transformYValues: (yValue: Points[number]['y']) => number;
}
const inference = <
X extends Values,
Y extends Values,
Points extends Point<X, Y>[]
>(data: Series<[...Points]>) => void 0
inference({
points: [{ x: 1, y: 2 }, { x: 1, y: 9 }],
transformYValues: (arg) => {
arg // 2 | 9
return 42
}
})
I added Values
type as a constraint for allowed x
and y
data types.
Also, I have added a function inference
because type inference works with generic conditional types and function arguments.
To infer literal type x:1 y:2
I have used variadic tuple types, see this [...Points]