Skip to content
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

StrictNullChck with Object.assign anomaly (Object is possibly 'undefined') #43110

Closed
holdfenytolvaj opened this issue Mar 6, 2021 · 2 comments

Comments

@holdfenytolvaj
Copy link

holdfenytolvaj commented Mar 6, 2021

Bug Report

With the strict null check turned on

function test(options?: { fields?: string , strict?: boolean}) {
    options = Object.assign({ fields: null }, options);
    if (options.fields) {
       //^^^^^^^ Object is possibly 'undefined'.ts(2532)
    }
    const test2 = () => {
            if (options.strict) {
               //^^^^^^^ Object is possibly 'undefined'.ts(2532)
            }
    }
}

Any of the following changes fix the first (but not the second):

function test(options?: { fields?: string|null }) {
    options = Object.assign({ fields: '' }, options);
    options = Object.assign({ a:null }, options);

🔎 Search Terms

strictNullCheck, Object.assign

🕗 Version & Regression Information

4.1.3

⏯ Playground Link

https://www.typescriptlang.org/play?#code/GYVwdgxgLglg9mABFApgZygCjgB1gtAfgC5EBvRYGFAGwBMjSMAnGMAcwBpEWZoTEAIzhwaKAIZIAvgEpyAKERLEufGDSIAvIgDyggFYpoAOnFo0MdmEwUqtBqTAgaNRFO6r46mQG5FymGBEbDwvNGM7ejQ5Mn9lZQB6BIA9VLTk3QMjKEQYDRw4cxhBGgBPRABycDoUKjAUOgrjKDRMACYAVgBmNpk4t36IAhzUDDatYLlNAD4FePmAoJC1cN5oGP6FhaT0tMzDaFz8wosS8qqwGrqGppb27t7Nral+l6kgA

💻 Code

🙁 Actual behavior

Wrongly assumes the options can be undefined

🙂 Expected behavior

Should be no error

@jcalz
Copy link
Contributor

jcalz commented Mar 6, 2021

There's a number of issues mixed together here:

  • The value {fields: null} is not assignable to type {fields?: string}. Optional properties can be undefined, not null. Is this a mistake?
  • Object.assign() produces an intersection type, which is only an approximation of what happens when there is property overlap, see Bug Report + Proposal: Object.assign() #31982 for why.
  • Since Reduce intersections by discriminants #36696, the intersection that comes out of Object.assign() here, {fields: null} & ({ fields?: string , strict?: boolean} | undefined), will be reduced to never. Apparently the reduction to never here makes the compiler forget about any control flow narrowing for options, and it resets to the original type of options, which includes undefined in its domain. When you make any of the changes you mention above, this reduction to never doesn't happen, because there's no longer an impossible discriminant type.
  • Closures also reset any control flow narrowing that happen; see Trade-offs in Control Flow Analysis #9998 for why.

This is a combination of things working as intended and known design limitations. If you think there's something here that isn't explained by the above points, could you point it out?

@holdfenytolvaj
Copy link
Author

Thanks for the detailed answer

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants