Description
TypeScript Version: 4.2.1
Search Terms: discriminated union optional property
Code
type DiscriminatorTrue = {
disc: true;
cb: (x: string) => void;
}
type DiscriminatorFalse = {
disc?: false;
cb: (x: number) => void;
}
type Props = DiscriminatorTrue | DiscriminatorFalse;
declare function f(options: DiscriminatorTrue | DiscriminatorFalse): any;
f({
disc: true,
cb: s => parseInt(s) // Inference works 👍
});
f({
disc: false,
cb: n => n.toFixed() // Inference works 👍
});
f({
cb: n => n.toFixed() // Implicit any error 👎
});
f({
cb: (n: string) => parseInt(n) // But errors correctly with incorrect type annotation 👍
});
Expected behavior:
No implicit any error on the third call site
Actual behavior:
Implicit any error on the callback parameter at the third call site
Related Issues: #31404 (comment) is this issue, but the OP there is different.
Notes:
This pattern sometimes appears in React component props, where convention is to make boolean properties optional and only pass them as true
(usually with the shorthand <MyComponent boolProp />
). I actually suggested using discriminated union props for React components in an old blog post, and noted this issue in a footnote, calling it a possible bug, but didn’t file it at the time because I had low confidence it wasn’t a duplicate or design limitation. It was later mentioned in #31404 (comment), but was probably ignored because it was assumed to be an instance of the OP’s issue, which was determined to be a design limitation. I dove into this again because someone tweeted at me asking about that footnote after reading the post.