-
Notifications
You must be signed in to change notification settings - Fork 12.9k
Fix discriminant property narrowing through optional chain with null #42503
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
Conversation
Need to accept baselines? https://github.com/microsoft/TypeScript/actions?query=workflow%3A%22Accept+Baselines+and+Fix+Lints%22 |
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.
Funny, I was looking at this issue and came up with exactly the same fix. I had planned to add the following tests, I suggest you include them in this PR:
type U = { kind: undefined, u: 'u' }
type N = { kind: null, n: 'n' }
type X = { kind: 'X', x: 'x' }
function f1(x: X | U | undefined) {
if (x?.kind === undefined) {
x;
}
else {
x;
}
}
function f2(x: X | N | undefined) {
if (x?.kind === undefined) {
x;
}
else {
x;
}
}
function f3(x: X | U | null) {
if (x?.kind === undefined) {
x;
}
else {
x;
}
}
function f4(x: X | N | null) {
if (x?.kind === undefined) {
x;
}
else {
x;
}
}
function f5(x: X | U | undefined) {
if (x?.kind === null) {
x;
}
else {
x;
}
}
function f6(x: X | N | undefined) {
if (x?.kind === null) {
x;
}
else {
x;
}
}
function f7(x: X | U | null) {
if (x?.kind === null) {
x;
}
else {
x;
}
}
function f8(x: X | N | null) {
if (x?.kind === null) {
x;
}
else {
x;
}
}
if (!propType) { | ||
return type; | ||
} | ||
propType = removeOptional ? getOptionalType(propType) : propType; | ||
propType = removeNullable ? getOptionalType(propType) : propType; |
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.
Optional chaining leverages an optionalType
marker type to indicate the type of an expression came from a ?.
. The optionalType
marker is essentially undefined
, but its presence has meaning to completions as well as within the checker itself. The code here (and from #42450), removes undefined
(regardless of source), then mixes back in undefinedType
. In functions like checkPropertyAccessChain
, I use the function propagateOptionalTypeMarker
to restore optionalType
to the result if it was found in the source type to carry that marker forward.
I'm not certain yet whether this (or #42450) will cause problems as a result of not restoring the optionalType
marker, as I need to consider if there is a test case that might trigger unwanted behavior.
@rbuckton it sounds like any potential problem is already in master, so I’m going to go ahead and merge this. Let me know if you have a suggestion for a follow-up and I’ll be happy to add to it. |
Fixes #42480
Got mixed up in #42450 thinking of
type
as the type of the discriminant property access, which would beundefined
even if the union type includednull
. Buttype
is the union, not the discriminant property, so we need to filter out bothnull
andundefined
, but then only mix back inundefined
to the discriminant property type to model the behavior of an optional chain.