Open manbearwiz opened 5 hours ago
There's no structural inference site of T
when trying to infer from SpecialBox
to Box<T>
, so inferring unknown
is expected in that case.
I did stumble on that when investigating this, but I wasn't sure how that all worked. So if I'm understanding correctly,
__uncommenting_fixes__: T
prop, TS can figure it out via structural inference.Box<T>
is used instead of SpecialBox
, TS can figure it out via instantiation-based inference.Based on that, I tried a new box definition, const PlainBox = Box<Stuff>
, and the inference does work with that.
class Box<T extends Record<string, unknown>> {
// Uncomment the below to include a reference to T in the generic class and fix return type inference
// private __uncommenting_fixes__?: T;
// some fns that return T[K]
}
type Stuff = { foo: { a: number }; bar: { b: string }; };
class SpecialBox extends Box<Stuff> {}
const PlainBox = Box<Stuff>;
function unbox<
Target extends Box<Record<string, unknown>>,
K extends Target extends Box<infer Y> ? keyof Y : never,
U extends Target extends Box<infer Z> ? Z[K] : never,
>(obj: Target, key: K): U {
// todo
return null as U;
}
// unbox<Box<Stuff>> and unbox<PlainBox> works fine, but unbox<SpecialBox> fails
console.log(unbox(new Box<Stuff>(), 'foo').a, unbox(new SpecialBox(), 'foo').a, unbox(new PlainBox(), 'foo').a)
console.log(unbox(new Box<Stuff>(), 'bar').b, unbox(new SpecialBox(), 'bar').b, unbox(new PlainBox(), 'bar').b)
🔎 Search Terms
"generic type inference"
🕗 Version & Regression Information
⏯ Playground Link
https://www.typescriptlang.org/play/?ts=5.6.3#code/MYGwhgzhAEBCD2APAPAFWgU0QFwwOwBMYAlDYeAJwOQmwoEs8BzAGmgFc8BrPeAdzwA+QdADeAKGjQA9NOgBVPOQC2y-NmjYAFhmgAjDCH6b40RqHYFdYaBQwAzDHaW7sp9I007oTfE-rA0KCQMGCE0Pb0iLYY2OwUeJoAngAOuoyOzsAYkjJyKQwAbmC40AD6ZZwqanjYjExlkYgYEBUA-ABc0KgA3OK5vtgAIiVgAGIU8MoAylMYfDp2AKIgEBjIANKYOPhE0FwYSfD23YIAFAdJXRsAlF2oANobALpiuVLkeBDwIBgAdEYmGcAOSXYFsS43PpSKSyMzKFK-GrYd4xOIJaB4dggEAAQmh0AAvuJieJsKldNM4vYTgBeN5SezweBdUTQMBdLHKAwUIkEvRgCis-RdWgMZh8kl9YJQaDTNLAehgEAIaJYXCEGCq5BU9g0kSiYmfWjQCAKpUgaD0vDzOXm5Wqs5Q8TGjR6JBWzG27W6-VOvriexVOrwRJ8ChgFIAcViuAoyFyqEFg22Gr22tI5CoNDo9TYnB4-CEghYuS26t2MCTFBTFc1cCQyAyTmgAE0RG19odjm3oJyMIUnKWpPJU5XusnYmP69rm7yAFod6Dzp6vfuDiil87wPQAK3uk+wEMO1zuCgZaPiiR3u7+gxG2HGkxmcwWTgwKzWF0ON3ZMHkfSknC96jBMUyzGob7LKs1jhOGkYxtgcbIAAwqGj6ME4Ih8JQXARJhbB6OwGjwdGsZODq9oqkgIj2GA9CrC6obfL8ALwEC7qIHesQPk+4GvosH4wSCTLwMCNx-GAbCkYhcZnJxbDAqJ4mSdJEZkUhThnGaZAWopykSWAzrGj8-yAvJSDccMoHPhB8yCZ+GAggKFAqXoakIeRFAWYgikuW5HkaXJOmKsqfmCm5zpAA
💻 Code
🙁 Actual behavior
In the playground, you will see that ts cannot infer the return type of
wrapGetter
when passedspecial
that was instantiated withspecial = new SpecialBox()
. It flags it as an error withObject is of type 'unknown'
.However,
wrapGetter(box, 'foo').a
has no issue inferring the return type since box is instantiated likebox = new Box<Stuff>()
.Uncommenting
private __uncommenting_fixes__?: T
allows both calls to successfully infer the return type.🙂 Expected behavior
I would expect
wrapGetter
to work the same arguments of typeBox<Stuff>
orSpecialBox extends Box<Stuff>
.Additional information about the issue
No response