最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

typescript - How to infer generic types from an Interface in a generic class constructor? - Stack Overflow

programmeradmin2浏览0评论

Playground

interface I<A, R> {
  a: A;
  r: R;
}

class C implements I<number, string> {
  a: number;
  r: string;
}

class D<C extends I<A, R>, A, R> {
  constructor(c: C, a: NoInfer<A>) {}

  getResult(): R {
    return void 0 as unknown as R;
  }
}

const c = new C();

const d1 = new D(c, 1); // expected: d1 should have type D<C<number, string>>, but it is inferred as unknown
const d2 = new D(c, "1"); // expected error because "1" is not a number, but no error is reported
const r1 = d1.getResult(); // expected: r1 should be a string, but inferred as unknown
const array: D<C>[] = []; // I want D to infer A and R directly from the first argument

I am attempting to design a generic class D where the types A and R should be inferred from the first parameter (an instance of some implementation of the interface I<A, R>).

class D<
  C extends I<A, R>,
  A extends C extends I<infer A, infer R> ? A : never,
  R extends C extends I<infer A, infer R> ? R : never,
> {}

I expected TypeScript to automatically infer A and R from the first parameter—so that if C implements I<number, string>, then A would be number and R would be string.

Instead, I encountered errors about circular constraints on type parameters A and R ("Type parameter 'A' has a circular constraint.(2313)" and "Type parameter 'R' has a circular constraint.(2313)").

Any guidance or suggestions would be greatly appreciated. Thank you!


Edit:

A big thanks to @Alexander Nenashev for the help that led to this solution!

type inferC<C> = (C extends I<infer A, infer R> ? { A: A; R: R } : never);
type inferA<C> = inferC<C>['A'];
type inferR<C> = inferC<C>['R'];

class D<C extends I<any, any>, A extends inferA<C> = inferA<C>, R extends inferR<C> = inferR<C>> {
  constructor(c: C, a: NoInfer<A>) { }

  getResult(): R {
    return void 0 as unknown as R;
  }
}

Playground

interface I<A, R> {
  a: A;
  r: R;
}

class C implements I<number, string> {
  a: number;
  r: string;
}

class D<C extends I<A, R>, A, R> {
  constructor(c: C, a: NoInfer<A>) {}

  getResult(): R {
    return void 0 as unknown as R;
  }
}

const c = new C();

const d1 = new D(c, 1); // expected: d1 should have type D<C<number, string>>, but it is inferred as unknown
const d2 = new D(c, "1"); // expected error because "1" is not a number, but no error is reported
const r1 = d1.getResult(); // expected: r1 should be a string, but inferred as unknown
const array: D<C>[] = []; // I want D to infer A and R directly from the first argument

I am attempting to design a generic class D where the types A and R should be inferred from the first parameter (an instance of some implementation of the interface I<A, R>).

class D<
  C extends I<A, R>,
  A extends C extends I<infer A, infer R> ? A : never,
  R extends C extends I<infer A, infer R> ? R : never,
> {}

I expected TypeScript to automatically infer A and R from the first parameter—so that if C implements I<number, string>, then A would be number and R would be string.

Instead, I encountered errors about circular constraints on type parameters A and R ("Type parameter 'A' has a circular constraint.(2313)" and "Type parameter 'R' has a circular constraint.(2313)").

Any guidance or suggestions would be greatly appreciated. Thank you!


Edit:

A big thanks to @Alexander Nenashev for the help that led to this solution!

type inferC<C> = (C extends I<infer A, infer R> ? { A: A; R: R } : never);
type inferA<C> = inferC<C>['A'];
type inferR<C> = inferC<C>['R'];

class D<C extends I<any, any>, A extends inferA<C> = inferA<C>, R extends inferR<C> = inferR<C>> {
  constructor(c: C, a: NoInfer<A>) { }

  getResult(): R {
    return void 0 as unknown as R;
  }
}
Share Improve this question edited Mar 19 at 17:38 Megaman asked Mar 17 at 17:27 MegamanMegaman 111 silver badge2 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 1

You could improve your code by:

  1. Make a type to infer A and R from I interface
  2. Infer A and R with the type above for D:

Playground

You could optimize it further by inferring A and R separately if needed.

interface I<A, R> {
  a: A,
  r: R
}

interface C extends I<number, string> {}
class C  {
  
}

type InferI<C extends I<any, any>> = C extends I<infer A, infer R> ? {A: A, R: R} : never;

class D<C extends I<any, any>, A extends InferI<C>['A'] = InferI<C>['A'], R extends InferI<C>['R'] = InferI<C>['R']> {
  constructor(c: C, a: A) {}

  getResult(): R {
    return void 0 as unknown as R;
  }
}

const c = new C();

const d1 = new D(c, 1);
const d2 = new D(c, "1"); // error, c.a is a number;
const r1 = d1.getResult();
const array: D<C>[] = [];

const result = array[0].getResult(); // const result: string
发布评论

评论列表(0)

  1. 暂无评论