-
Notifications
You must be signed in to change notification settings - Fork 12.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Omitted property does not narrow discriminated union contextual type #41759
Comments
In this case, I think this'd be fixable by adjusting |
Tagging backlog since this is a longstanding shortcoming, but we can take it earlier if someone's eager to take a stab. |
This is same issue as this one right? type Props = {
mode?: boolean;
nodes?: string;
}
type B =
| { enabled?: false; }
| { enabled: true; foo: string; };
// no error UNEXPECTED ❌
export const a: Props & B = {
mode: true,
nodes: ",",
foo: "",
};
type B2 =
| { enabled: false; }
| { enabled: true; foo: string; };
// compiles as expected ✅
export const a2: Props & B2 = {
mode: true,
nodes: ",",
enabled: true,
foo: "",
};
// err as expected ✅
export const a3: Props & B2 = {
mode: true,
nodes: ",",
foo: "",
};
// err as expected ✅
export const a4: Props & B2 = {
mode: true,
nodes: ",",
enabled: false,
foo: "",
}; Output// no error UNEXPECTED ❌
export const a = {
mode: true,
nodes: ",",
foo: "",
};
// compiles as expected ✅
export const a2 = {
mode: true,
nodes: ",",
enabled: true,
foo: "",
};
// err as expected ✅
export const a3 = {
mode: true,
nodes: ",",
foo: "",
};
// err as expected ✅
export const a4 = {
mode: true,
nodes: ",",
enabled: false,
foo: "",
}; Compiler Options{
"compilerOptions": {
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictPropertyInitialization": true,
"strictBindCallApply": true,
"noImplicitThis": true,
"noImplicitReturns": true,
"alwaysStrict": true,
"esModuleInterop": true,
"declaration": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"moduleResolution": 2,
"target": "ES2017",
"jsx": "React",
"module": "ESNext"
}
} Playground Link: Provided |
Sort of a "solution" is to do this: // Solution is to specify all props from the non optional `enabled` as optional undefined fields
type B3 =
| { enabled?: false; foo?: undefined}
| { enabled: true; foo: string; };
// err as expected ✅
export const a5: Props & B3 = {
mode: true,
nodes: ",",
}; but it's not very nice. (I came across this when I was trying to use discriminated unions for react prop types) |
This diff extends the types checked by discriminateContextualTypeByObjectMembers and discriminateContextualTypeByJSXAttributes to also include any optional components in the type union. fixes #41759 although it doesn't address the better error reporting for their last repro, which I'm not sure how to address.
In short, the fix I submitted looked at the union ofproperties, but it really should have looked at the intersection. Two sytlistic notes. I couldn't find the best way to get the unique strings of an array like `[...new Set()]` would, so I created a small helper function, but didn't put it in a great place. Also, before the second concatenated array of discriminators at least matched the first in complexity, but now it's much worse. I don't think that section is particularly easy to read, but I also don't see a significantly reusable part. fixes #41759
@andrewbranch Is this supposed to be still open? This doesn't error in 4.6, see playground |
Huh, the fix was reverted, so I’m not sure what happened. |
👋 Hi, I'm the Repro bot. I can help narrow down and track compiler bugs across releases! This comment reflects the current state of the repro in the issue body running against the nightly TypeScript. Issue body code block by @andrewbranch ❌ Failed: -
Historical Information
|
Anyone know the state of this? Is it worth a new issue? Looks to exist in 4.9.5 |
The OP's issue is still fixed, so if you have something that seems similar, definitely file a new bug. |
TypeScript Version: 4.2.1
Search Terms: discriminated union optional property
Code
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
Playground Link
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.The text was updated successfully, but these errors were encountered: