Typescript generic field selection assumes type incorrectly
I was working on a generic lib-like api for something I'm working on, and got stumped by unexpectedly wrong typescript generic field selection.
E.g. if
<Foo extends Something>(args: Foo)
, then you'd think args.field
is typed as Foo['field']
, but it's not, it's simplified down instead.
Here's a minimal repro:
https://www.typescriptlang.org/play/?#code/GYVwdgxgLglg9mABFApgZygHgCqJQD1TABM1EBvRAQwC4LFg446MAnGMAc0QF9eA+ABRVWnOtgCUFAFCJErFFBCskIzgDoqAbmk9p0iAgyJiVKFUQBeZOiiDKteo2aIARK4A0iAEYi673l4JHUMwNDgAGxR1CLhOQVNzdV9WYOkgA
Is there known ways of getting around this without using as
? It feels so wrong. E.g. here's what i have to do to fix it in my real code:
TS Playground - An online editor for exploring TypeScript and JavaS...
The Playground lets you write TypeScript or JavaScript online in a safe and sharable way.
43 Replies
@Arduano you can annotate the return type of the function as
T['a']
so typescript keeps the wider definition as return type.
https://www.typescriptlang.org/play/?#code/GYVwdgxgLglg9mABFApgZygHgCqJQD1TABM1EBvRAQwC4LFg446MAnGMAc0QF9eA+ABRVWnOtgCU4gNoByKrIC6FAFCJErFFBCskIzgDoqAbhU8VKiAgyJiVKFUQBeZOiiDKteo2aIARH4ANIgARiJ0Aby8EqZWYGhwADYoBolwnIJ2DgZhrDEqQATS Playground - An online editor for exploring TypeScript and JavaS...
The Playground lets you write TypeScript or JavaScript online in a safe and sharable way.
yeah fair, what if it's something more complex, like the above code block. One sec I'll cook up an example
Here
https://www.typescriptlang.org/play/?#code/C4TwDgpgBAyg9gWwgYUWANhAHgFXBAHhwD4oBeKHKAMigG8oASASwHMA7OAJwgC4oAzsC7N2rKAF8A3ACgZAMwCu7AMbBmcdlABGi5ugAmqBBmxFiACgCGXVvxwBKfvCTHTufOfoyoUHsEUuLTofXygAOkibVgAaUN8WDm4+KAByVNDpGQk5JVV1TShgCCEiKGxi9gMBeigrfgZ5ODgGiRidG1bJSUto+wdvX39ArV19IzRMLAsQsKgmlp09Qzcp61twq3CFhzi57U6l8dXsddZN8IOuB0yHWRyZAHpnx6gAVVVMKy15Zix2xQCUTiAAGVgEIN4T1eeTUGi0xVKVAqECqNQY9VqC1a7SuXQkPTO-W8zzCwyCRxWk1OIVJc2xlImJjW0QuCzqNRwAG1UlZUgBdHkLAW7aFzDpcfhjKnM06srZXDmUHl8wWpK4isW+CR3MUPFSaIRQAxWYBWchFErAGZ1BpQUIMxrMCCGACM-HSknaoTxWOdhgATB7UpJsjrZAb2AI4Jhwug4KwLCazdtmtt-QZXbqgA
tldr I want to rely on type inference as much as physically possible
ok let me see
in my real code's context, the "buildComplex" is actually zod types
here's a full screenshot of my real code for context, if it's relevant
i mean you could still annotate it without losing any inference
as in, the return type?
yes?
it's especially difficult here because I'd have to create a zod schema manually using types, and then again using code
(the point of zod is to skip defining the types)
using
as
is the lesser of the two evils ig, I was just wondering if there is a real idiomatic solution to this because it feels kinda wrongwould you mind showing the simplify zod function?
oh right it's this cursed thing, because while I was prototyping, typescript was having even weirder deep issues that would be hard to explain. You can ignore this function, I was planning to remove it anyway
(removed it just then)
oh okay
@Arduano what if you created annotaed variables instead of annotating the return type like this?
https://www.typescriptlang.org/play/?#code/JYWwDg9gTgLgBAbzgLzgXzgMyhEcDkyEAJvgFBkwCeYApnAEICuwANsQCoQSsCCUAcwDOcALyIycOADsAhiFoAuOEJhRg0gZLjFZMWcoTapswUOXIAdAC0SAeQBGAK1oBjGAB4EaAHza0ZAHUdHAAInqy-MIeHAwcvHC0AB4wtNLEIsxsnNx8Zj5icLHxANr4uvr4ALplpsLVFJhM0u7AENJwILIA1rRcPOH6AMquABa0XTFxCcmp6Zks7P15wj4AFEhyCgA0OhHoysW8AJQSUq7tqnB15mERUUJT8QXiFbKWN9pQtDBMUB1WCDONwwDZYZqtdoAOXkShQllYwFSUFkrDWW1ox12N3Qx0CFAu0iuskKXV6y0GshG4y6YIxygARKlVAzdm9DNczBZLECXO46bDuap1Jo1qc0OLcZYwKYhLQ1gyGXipHAAPSquAAPQA-EA
TS Playground - An online editor for exploring TypeScript and JavaS...
The Playground lets you write TypeScript or JavaScript online in a safe and sharable way.
That's the same as doing
as
though right, still have to do TBTA['data']['args']
manuallyi think the important part is that it does not force the type on the attribute
like your code can be shortened to this
as far as I can tell
or this
yes you can also inline the type that does not make a difference
i personally don't like type assertions. i try to dodge them as much as i can
because in some way this forces an identifier or attribute to be of type x
while not necessarily being of that type
wait I just noticed, you can replace
as
with satisfies
, that's neat
not perfect but cleaneryes i think that would in fact be the same as annotating it
actually no it's not
doesn't type error here
that's interesting
i think the whole issue you are having is typescript narrowing the nested type of a generic too early
yeah basically
so you will need to widen it in any form
in what way? unless you mean manual casting
in a way that you tell typescript that
rec.a
is not actually of type { foo: string }
but of type T['a']
it's annoying because this works as expected
but this doesn't
hmm
yes because it's the nested type of your object that causes typescript to narrow too early
oh, wdym?
is there some specific property of the type where it decies to simplify/narrow, which I can force it to avoid?
i think deep inference of generic objects is what causes your issues
but there is no real way around that unless you were to split your object into multiple individually inferable function parameters
that is my guess
yeah that would make sense, though it's not really feasible in my context
I attempted it at some point though
yeah that's what i thought
i personally would just go with type annotated variables if that works
as
is more concise but equally type safe for now
if satisfies
doesn't catch it then type annotated variables definitely don't eithercatch what?
invalid casting
like here
wait is args generic?
yeah
you can see it here