-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Fix #38608 #38610
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 #38608 #38610
Conversation
@typescript-bot test this |
Heya @jack-williams, I've started to run the extended test suite on this PR at 2609521. You can monitor the build here. |
Heya @jack-williams, I've started to run the parallelized community code test suite on this PR at 2609521. You can monitor the build here. |
The user suite test run you requested has finished and failed. I've opened a PR with the baseline diff from master. |
Baseline changes: VSCode fails on: function improveError(err: Error): Error {
if ('errno' in err && err['errno'] === 'ENOENT' && 'path' in err && typeof err['path'] === 'string') {
return new Error(nls.localize('ext.term.app.not.found', "can't find terminal application '{0}'", err['path']));
}
return err;
} Previously function improveError(err: Error & { errno?: unknown, path?: unknown }): Error {
// .... Other diff looks unrelated. |
src/compiler/checker.ts
Outdated
@@ -20691,7 +20691,7 @@ namespace ts { | |||
} | |||
|
|||
function narrowByInKeyword(type: Type, literal: LiteralExpression, assumeTrue: boolean) { | |||
if (type.flags & (TypeFlags.Union | TypeFlags.Object) || isThisTypeParameter(type)) { | |||
if (type.flags & TypeFlags.Union || type.flags & TypeFlags.Object && declaredType.flags & TypeFlags.Union || isThisTypeParameter(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.
Rather than the declared type, shouldn't this just be looking at the input type? If we look at the declared type, it precludes things like
declare var x: unknown;
if (isAOrB(x)) {
if ("aProp" in x) {
x; // A
}
else if ("bProp" in x) {
x; // B
}
else if ("cProp" in x) {
x; // never
}
}
since I'm pretty sure we just merged a fix to a lot of cases like that.
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 fix that just went in was for discriminant property narrowing, which solved examples that are similar to yours, but it's a disjoint code path. Changing declaredType
to type
would revert to the original behaviour, but also have the correct behaviour here.
I've made a slight modification that narrows singletons if the current type is not the same as the declared type. This is abit of a hack, but in general working out the intent does not seem easy.
I added some discussion to the linked issue. The best thing might be to make no change at all.
@weswigham what do you think? should we take this or not? |
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.
Sure, this looks like both a pretty small change, and a pretty agreeable one now.
I'm happy to refresh this - just wondering whether to look into Anders' suggestion here? Maybe merge this and review that option later? |
🤷 I don't think we'll take this for the 4.1 RC this week, so there a few weeks you could experiment during until we're ready to start merging things for 4.2. |
Master is open for 4.2 PRs now. |
so @jack-williams you feel strongly one way or another with this now? |
…g-for-declared-singleton-case
@weswigham I merged master on this and opened another PR that tried the Perf looked ok - some changes in the binder but I think they are unrelated. My preference for There are some subtleties which arise because As it is now, the following behave the same (with and without my changes): type AOrB = { aProp: number } | { bProp: number };
declare function isAOrB(x: unknown): x is AOrB;
declare var x: unknown;
declare var y: AOrB;
if (isAOrB(x)) {
if ("aProp" in x) {
x.aProp;
}
else if ("bProp" in x) {
x.bProp;
}
else if ("cProp" in x) {
const _never: never = x;
}
}
if ("aProp" in y) {
y.aProp;
}
else if ("bProp" in y) {
y.bProp;
}
else if ("cProp" in y) {
const _never: never = y;
} But it is not the case that similar discrimant code behaves the same: type A = { kind: 'a' }
type B = { kind: 'b' }
type AorB = A | B;
declare const x: unknown;
declare const y: AorB;
declare function isAorB(x: any): x is AorB;
if(isAorB(x)) {
if(x.kind === "a") {
}
else if (x.kind === "b") {
x;
}
else {
x // B;
}
}
if(y.kind === "a") {
}
else if (y.kind === "b") {
}
else {
y; // never;
} When you consider having declare const w: AOrB;
if ("aProp" in w) {
w // A
}
else if ("bProp" in w) {
w // B
}
// w is never from here
else if ("cProp" in w) {
w
}
else {
w
} but check declare const w: AOrB;
if ("aProp" in w) {
w // A
}
else if ("cProp" in w) {
w // w is { bProp: number } & Record<"cProp", unknown>
}
else if ("bProp" in w) {
w // B
}
else {
w // never
}
I think the second option is reasonable but I haven't been able to check user tests yet. |
Narrowing for |
Would this be a way forward? The current PR is ready and the other PR (#41478) is nearly ready but needs some tests, to scan through the user tests, and to write up a description of the implemented behavior. I can do that work this week. At that point, it probably comes down to a discussion within the team to see if they want to consider moving forward with #41478, otherwise this can be merged. Merging this would not preclude #41478 at a later date, I think. In most cases this PR just changes some |
I agree, let's merge this one now. I'll add #41478 to the design meeting agenda. |
Fixes #38608
Narrowing an object to
never
usingin
when the value was not declared as a union seems like undesirable behaviour.