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
1 Answer
Reset to default 1You could improve your code by:
- Make a type to infer A and R from I interface
- 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