-
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
Fix 21732: "in" operator could widden type #39746
Conversation
Some questions would stop PR from merging in my thought. Have no idea about these for now, I would add new comments when idea comes.
|
src/compiler/checker.ts
Outdated
} | ||
return type; | ||
|
||
// I would be very glad to create a helper file like `nodeTests.ts` if feedback positive review. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, what about a helper file for Type to narrow their type?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are a lot of properties that the control flow code needs to maintain that this change will violate. A full change will involve quite a lot of change to the control flow code, not just the narrowing code.
The best place to start is to add tests for in
in complex control flows, like if or switch nested in while. Look at the existing control flow tests for examples.
Oh my..... Thanks a lot to the review and suggestion, I find a simple example that works wrongly. var tmp = { a: 1, b: "foo" }
tmp; // add "e" to the type.
if ("d" in tmp) {
tmp;
if ("e" in tmp) {
tmp;
}
tmp;
} I would have a try to change CFA, but it is great if there is any help to explain how does it works briefly, I am totally new on this area. -- edit: I would continue check, but it would still be very helpful if there is an error example to analytics on CFA. |
You can see |
@Kingwl Thanks for the kind help! And...... could I ask a question? ---- What do you think of the issue? Why this PR need to change CFA? |
@ShuiRuTian do you want to keep working on this? |
@sandersn Sure, but I think I need feedback -- Why this PR need to change control flow? And I do test some cases, it seems they work correctly. |
@weswigham can you take a look and give some direction on this? |
@weswigham Sorry for bothering, could you help this PR find direction when have spare time? It lost at the sea.... |
up! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The key issue we normally flag when thinking about this is branch reconciliation. Take something like this:
var tmp = { a: 1, b: "foo" };
if ("c" in tmp) {
tmp;
}
else if ("d" in tmp) {
tmp;
}
else {
return;
}
tmp; // should this be `{a: number, b: string}` or `{a: number, b: string, c: unknown} | {a: number, b: string, d: unknown}`
and then there's the question of what happens when you both discriminate and "expand":
declare var x: {type: "a", f1: string} | {type: "b", f2: string};
if (x.type === "a") {
if ("f2" in x) {
x; // is this `never`? is this `{type: "a", f1: string, f2: unknown}` ?
}
}
x; // needs to have gone back to `{type: "a", f1: string} | {type: "b", f2: string}` here, and not be something wonky like `({type: "a", f1: string, f2: unknown}) | {type: "b", f2: string}`
const members = createSymbolTable(); | ||
members.set(propName, newSymbol); | ||
if (widenedType.members) { | ||
mergeSymbolTable(members, widenedType.members); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This recursively merges the symbol table deeply (so overlapping symbols within the table get combined, and have a merge pointer at the new, combined symbol). You don't want that. Rather than most of this logic, you should probably just use getSpreadType
, rather than everything after the // if type is intersection
comment, since it already combines object types where appropriate, since you guarantee you're adding a new property in the second object anyway. (Re)Using it should also allow you to cut out a lot of the other structural changes around intersection construction in this PR, too.
Thanks for the review and suggestion! Here are the types this PR could get:
I was confused how to make choice between the commnets in the example code. It seems both are reasonable to some degree for me. And finally I decide to keep it just same with other narrow statements, at least it is not a bad decision in my thought, it keeps consistant. However, I am not sure whether it is good choice on earth. My concern might be simple, sometimes navie. What do you think? PS: need a little time to get |
@ShuiRuTian do you want to keep working on this? |
I'm going to close this PR to reduce clutter. Let me know if you want to restart work on it. |
Fixes #21732