TS seems to think that the type of the interpolated prop value is as follows:
{ href: `#234${undefined}2213` }
are not strings (when they are used in distinguishable unions)?
The third p3
instance below loses type inference for the ev
field, but only if the href is an interpolated esliteral string.
type BiomePlainLinkProps = { href: string; onClick?: (event: string) => void; } type BiomeButtonProps = { href?: never; onClick?: (event: number) => void; } export type ClickableDiscriminatedUnion = | BiomePlainLinkProps | BiomeButtonProps; const p1: ClickableDiscriminatedUnion = { href: '2332132', onClick: (ev) => console.log('@@@@', ev), // ev is string here } const p2: ClickableDiscriminatedUnion = { onClick: (ev) => console.log('@@@@', ev), // ev is number here } const p3: ClickableDiscriminatedUnion = { href: `2${undefined}332132`, onClick: (ev) => console.log('@@@@', ev), // ev is any (not string) here }
TS sandbox complete reproduction
P粉6175971732024-03-31 00:33:04
Update TS5.2
This bug has been fixed in microsoft/TypeScript#53907, so as of TypeScript 5.2, the code in the question will work as expected without any changes.
Use the Playground link of TS 5.2.0-dev.20230516
Original answer for TS5.1-
It turns outYes, this is a bug in TypeScript, as microsoft/TypeScript#53888, which I submitted in response to a Stack Overflow question. The compiler seems to recognize that the discrimination attribute href
is of type string
rather than undefined
, but this happens too late Enter onClick
callback parameters based on context. Presumably template literal interpolation is enough to delay things causing problems.
GitHub issue is in the Backlog, which means the TS team doesn't plan to fix it anytime soon... but therefore it's also marked as Needs Help a>, which means They will accept pull requests from community members; therefore, anyone looking to resolve this issue as soon as possible should consider contributing the fix themselves.
Meanwhile, the workaround I suggest here is to do the string interpolation ahead of time and store it in const
so that its type is known ahead of time:
const theHref = `2${undefined}332132`; const p4: ClickableDiscriminatedUnion = { href: theHref, onClick: (ev) => ev.toUpperCase() // okay }
It's annoying (probably even more annoying for JSX), but at least it works!