Typescript: infer generic types in generic type constraint

Typescript: infer generic types in generic type constraint

Problem Description:

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.

Solution – 1

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
  }
})

PLayground

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]

Rate this post
We use cookies in order to give you the best possible experience on our website. By continuing to use this site, you agree to our use of cookies.
Accept
Reject