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

Type narrowed too much #57880

Closed
fjolliton opened this issue Mar 21, 2024 · 6 comments Β· May be fixed by #58729
Closed

Type narrowed too much #57880

fjolliton opened this issue Mar 21, 2024 · 6 comments Β· May be fixed by #58729
Labels
Duplicate An existing issue was already created

Comments

@fjolliton
Copy link

πŸ”Ž Search Terms

type narrowing local variable

πŸ•— Version & Regression Information

I don't see anything related in the FAQ. But I acknowledge that this seem too common to be unknown. Every version seem affected however.

⏯ Playground Link

https://www.typescriptlang.org/play?ts=5.4.2#code/MYewdgzgLgBMCGAbRMC8MAUAzAXJglGgHwwBuIAlgCaGolYb4DcAUC6JLFAKbRoHEYAbxYwxMRN1jw8AVzBVuWCmG5UYAHxhhZAWwBG3AE795i5aqqtxMAPS2YAFQCeAB17AjFV7AjduuhAwUCAwhnAguq6SPIjOMPLwAO7wRtzBABbwXBnppEiy3KLi9jAgWDAABvCVcPBgcFlgAObpKpnpWCDIIEkqzXXIAHTFYgjIGIyCQjDw-ACMTDAAvsyjdg4AggodaQA0MBS+UBTImSoA1pnZVTV1DeBx66WuIBAQFPpxYemVZkoqNSVEY2CgVDBzACEqHQ-wsakIIhsNlKUAyFCCiEBMFasDSACtuMAeFR1jZ4EMQgBlKBeFqMaziZYsZasIA

πŸ’» Code

const call = (f: () => void) => f();

const test = () => {
    let a: undefined | number = undefined;
    // Typescript seems to be completely unaware that the value
    // of `a` can change in the following call.
    call(() => { a = 1; });
    // And there, it still think that `a` can only
    // possibly be `undefined`.
    if (a !== undefined) {
        // this line get rejected
        a.toString();
    }
};

πŸ™ Actual behavior

It seems that Typescript is not considering a function call to affect local variable. It proceed to narrow the type from this limitation, and thus reject valid code.

Not assigning undefined to a causes the error to disappear BUT this is still a problem, because I might want a default value here (could be a number initially, that later get changed to undefined for example).

For example, and this is worse:

const call = (f: () => void) => f();

const test = () => {
    let a: undefined | number = 42;
    call(() => {
        a = undefined;
    });
    a.toString();
};

This will compile fine, but it will fail at runtime!

πŸ™‚ Expected behavior

The type should not be narrowed in this case.

Typescript should detect that a closure is referencing a local variable, and thus, should avoid narrowing its type.

Additional information about the issue

No response

@MartinJohns
Copy link
Contributor

Another duplicate of #9998.

@fjolliton
Copy link
Author

Is there anything to prevent to narrow such types? (a configuration option or a directive to put somewhere)

@MartinJohns
Copy link
Contributor

No. It's all written in that issue. You can use a type assertion to prevent the initial narrowing: 42 as undefined | number.

@RyanCavanaugh RyanCavanaugh added the Duplicate An existing issue was already created label Mar 21, 2024
@craigphicks
Copy link

craigphicks commented Mar 23, 2024

Note: Typescript is aware that the value of a can change in a synchronous IIFE

const test = () => {
    let a: undefined | number = undefined;
    // Typescript is aware that the value of `a` can change in a synchronous IIFE
    (() => { a = 1; })();
    a; // number
};
test();

@fatcerberus
Copy link

fatcerberus commented Mar 23, 2024

@craigphicks Yes, IIFEs are special-cased: since the compiler can readily see from the syntax that the function will always be called immediately (and exactly once), it's treated the same as if the code was written inline.

@typescript-bot
Copy link
Collaborator

This issue has been marked as "Duplicate" and has seen no recent activity. It has been automatically closed for house-keeping purposes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants