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

fix: optional-chaining should work correctly with ts non-null operator #10961

Conversation

macabeus
Copy link
Contributor

@macabeus macabeus commented Jan 4, 2020

Q                       A
Fixed Issues? Fixes #10959
Patch: Bug Fix? 👍 Yeah
Major: Breaking Change? 🧐 kind off
Minor: New Feature? 🙅‍♂️ nop
Tests Added + Pass? 👍
Any Dependency Changes? 🙅‍♂️ nop
License MIT

This PR fix when use ! operator within an optional chaining.

a?.b!.c

should be compiled to

(_a = a) === null || _a === void 0 ? void 0 : _a.b.c;

But previous this fix, it was compiling to

((_a2 = a) === null || _a2 === void 0 ? void 0 : _a2.b).c;

That could raise wrongly an exception if a is null or undefined.

The approach to fix this bug is unwrapping TSNonNullExpression on the OptionalMemberExpression.

@macabeus macabeus force-pushed the fix/optional-chaining-plus-non-null-expression branch from 49a4708 to 98c1799 Compare January 5, 2020 00:03
if (optionalPath.isTSNonNullExpression()) {
optionalPath.replaceWith(optionalPath.node.expression);

if (optionalPath.isOptionalMemberExpression()) {
Copy link
Contributor

@JLHwung JLHwung Jan 5, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should only unwrap TSNonNullExpression and leave the node.expression as-is. Because the non-null expression does not imply any runtime checks, which is done by the optional member.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, yeah. I tested now removing this if block and works fine. Okay, I'm updating this PR. Thank you.

@macabeus macabeus force-pushed the fix/optional-chaining-plus-non-null-expression branch from 98c1799 to 029b658 Compare January 5, 2020 00:24
Copy link
Member

@nicolo-ribaudo nicolo-ribaudo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also add a test with:

  1. Optional chaining transform and typescript syntax only
  2. Optional chaining syntax only and typescript transform

@macabeus
Copy link
Contributor Author

macabeus commented Jan 5, 2020

@nicolo-ribaudo could you explain for me these transforms and syntax, please? What it's and its purposes. I still didn't understand very well about that.

@JLHwung
Copy link
Contributor

JLHwung commented Jan 5, 2020

@macabeus The transforms are the plugins that desugar OptionalChaining so that it can run on other platforms, i.e. browsers does not have optional-chaining support. The syntax are the plugins that allow the parser to parse optional-chaining, so that it will not throw unexpected token.

Optional chaining transform and typescript syntax only

So this is to say, one wants to desugar optional-chaining but keep typescript syntax intact, in this case the NonNullExpression should be preserved.

Optional chaining syntax only and typescript transform

In this case one wants to remove TypeScript stuffs but leave optional-chaining as-is.

Copy link
Member

@jridgewell jridgewell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking this until we get confirmation from Typescript. @DanielRosenwasser?

Currently, Babel's transform matches Typescript's: http://www.typescriptlang.org/play/?ssl=1&ssc=1&pln=1&pc=8#code/IYfgdARghGDGQ

Note: I think we should change this, just need to confirm.

@macabeus
Copy link
Contributor Author

macabeus commented Jan 6, 2020

I agree that we should follow the output in tsc, so I opened an issue in TypeScript repository saying about this problem.

@alex-kinokon
Copy link

@jridgewell It’s labeled as a bug in the TypeScript repo now. I think we’re good to go.

@macabeus
Copy link
Contributor Author

macabeus commented Jan 6, 2020

Wait, no merger yet. We need to add the tests that @nicolo-ribaudo said.
I'll do it tonight.

@macabeus macabeus force-pushed the fix/optional-chaining-plus-non-null-expression branch from 029b658 to f56f54b Compare January 7, 2020 14:47
@macabeus
Copy link
Contributor Author

macabeus commented Jan 7, 2020

@nicolo-ribaudo Done. I added the two tests that you requested.
Maybe we could remove 10959-proposal-optional-chaining-and-preset-typescript, because it's redundant since there is the 10959-proposal-optional-chaining-and-typescript-syntax... What do you think?

@existentialism existentialism added area: typescript PR: Bug Fix 🐛 A type of pull request used for our changelog categories labels Jan 7, 2020
@macabeus
Copy link
Contributor Author

macabeus commented Jan 7, 2020

Some random testes failed... but it's sounds like a false negative, because I just added more tests that works fine locally. I didn't changed anymore.

@JLHwung
Copy link
Contributor

JLHwung commented Jan 7, 2020

@macabeus The CI error is not related. I will fix that later.

{
"plugins": [
"../../../../../babel-plugin-syntax-optional-chaining",
"../../../../../babel-plugin-transform-typescript"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use

Suggested change
"../../../../../babel-plugin-transform-typescript"
"transform-typescript"

directly (and also for the other options.json files)

@@ -0,0 +1,3 @@
var _a;

(_a = a) === null || _a === void 0 ? void 0 : _a.b.c;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we aren't transforming TypeScript, this should remain

Suggested change
(_a = a) === null || _a === void 0 ? void 0 : _a.b.c;
(_a = a) === null || _a === void 0 ? void 0 : _a.b!.c;

However, I think that no one will ever find this bug so if it's too hard to fix I'm ok with opening a new issue and not fixing it in this PR.

@macabeus
Copy link
Contributor Author

The fix was just merged on TS: microsoft/TypeScript#36539


// unwrap a TSNonNullExpression if need
if (optionalPath.isTSNonNullExpression()) {
optionalPath.replaceWith(optionalPath.node.expression);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure that this will correctly fix the bug. We may be able to see it more in test cases like a?.b!.c.d and a?.b.c!.d

@nicolo-ribaudo nicolo-ribaudo force-pushed the fix/optional-chaining-plus-non-null-expression branch from f56f54b to d490b96 Compare April 7, 2020 21:01
Copy link
Member

@nicolo-ribaudo nicolo-ribaudo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rebased and added a lot of tests!

@nicolo-ribaudo
Copy link
Member

This is technically a breaking change, because it drops support for TypeScript 3.8. However, without merging this PR we can't support TypeScript 3.9.

I don't think we should wait for Babel 8: does anyone thinks that it would be better to merge this in a minor rather than in the next patch?

@macabeus
Copy link
Contributor Author

macabeus commented Apr 8, 2020

@nicolo-ribaudo Well... TS already merged that on a minor release: 3.8 -> 3.9
Maybe we could follow the same approach?

@existentialism
Copy link
Member

This is technically a breaking change, because it drops support for TypeScript 3.8. However, without merging this PR we can't support TypeScript 3.9.

I don't think we should wait for Babel 8: does anyone thinks that it would be better to merge this in a minor rather than in the next patch?

I'm good with a minor.

@nicolo-ribaudo nicolo-ribaudo added this to the v7.10.0 milestone Apr 8, 2020
@nicolo-ribaudo nicolo-ribaudo added the PR: Ready to be Merged A pull request with already two approvals, but waiting for the next minor release label Apr 8, 2020
@nicolo-ribaudo nicolo-ribaudo merged commit 75a6530 into babel:master May 24, 2020
@macabeus macabeus deleted the fix/optional-chaining-plus-non-null-expression branch May 24, 2020 21:03
@github-actions github-actions bot added the outdated A closed issue/PR that is archived due to age. Recommended to make a new issue label Aug 24, 2020
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 24, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area: typescript outdated A closed issue/PR that is archived due to age. Recommended to make a new issue PR: Bug Fix 🐛 A type of pull request used for our changelog categories PR: Ready to be Merged A pull request with already two approvals, but waiting for the next minor release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

a?.b.c() produces different result than a?.b!.c()
6 participants