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

Optional Chaining Operator (Stage 1) #5813

Merged
merged 33 commits into from
Jun 27, 2017
Merged

Optional Chaining Operator (Stage 1) #5813

merged 33 commits into from
Jun 27, 2017

Conversation

jridgewell
Copy link
Member

@jridgewell jridgewell commented Jun 2, 2017

Example: https://babeljs.io/repl/build/master#?babili=false&browsers=&build=&builtIns=false&code_lz=IYfgdARg3AUDqTAYzAE1vcFnnXBA2gOQREC6shEFmYAFAJRRA&debug=false&circleciRepo=&evaluate=false&lineWrap=false&presets=stage-0&prettier=false&targets=&version=7.0.0-beta.2


Q A
Patch: Bug Fix? No
Major: Breaking Change? No
Minor: New Feature? Yes
Deprecations?
Spec Compliancy?
Tests Added/Pass? Yes
Fixed Tickets Closes #5786
License MIT
Doc PR
Dependency Changes

This is another implementation of Null Propagation Operators, competing with #5786. This one takes a different, top-down approach to the transform that avoids the WeakSet state. It also implements optional CallExpressions and NewExpressions.

This has a few todos:

  • Split transform into two plugins
    1. Syntax enabler (the manipulateOptions stuff)
    2. The transform
  • There's a "fixer" for the WIP parser.
    • Optional CallExpressions don't have a callee
    • Optional NewExpressions have a callee that's a CallExpression. That callee's callee and arguments should be the NewExpressions callee and arguments
  • Update babel-generator printer

@jridgewell jridgewell added es-proposal area: experimental PR: New Feature 🚀 A type of pull request used for our changelog categories labels Jun 2, 2017
@jridgewell jridgewell changed the title Pr/5786 Null Propagation Operators Jun 2, 2017
Copy link
Member

@hzoo hzoo left a comment

Choose a reason for hiding this comment

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

  • readme update with example and link to proposal
  • does babel-types need changing?
  • do we want some simple exec tests? (could be basis for test262 as well)

@jridgewell
Copy link
Member Author

jridgewell commented Jun 4, 2017

@hzoo: done. Please advise whether I have all the necessary LHS parents we need to guard.

@xtuc
Copy link
Member

xtuc commented Jun 5, 2017

Nice job @jridgewell!

  • In the spec, they are talking about a "special Reference" called nil. Could we explicitly use it in the plugin's code? Like in my plugin here.

I'm ok with closing my PR.

Link to parser PR babel/babylon#545.

@hzoo hzoo requested review from gisenberg and xtuc June 5, 2017 16:47
@jridgewell
Copy link
Member Author

Done.

Sorry about the duplicate PRs. I usually open one against the original PR (like the parser one), but this implementation was significantly different.

@hzoo hzoo mentioned this pull request Jun 5, 2017
2 tasks
@hzoo hzoo changed the title Null Propagation Operators Optional Chaining Operator (Stage 1) Jun 5, 2017
const baz = obj?.foo?.bar?.baz(); // 42

const safe = obj?.qux?.baz(); // undefined
const safe2 = obj?.foo.bar.qux?.(); // undefined
Copy link
Member

Choose a reason for hiding this comment

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

can we add an example like obj?.foo.bar.bazz() that throws?

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.


const baz = new obj?.foo?.bar?.baz(); // baz instance

const safe = new obj?.qux?.baz(); // undefined
Copy link
Member

Choose a reason for hiding this comment

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

I don't think this should return undefined (obv that depends on the proposal itself).

I think that if you want the new not to throw when it gets a non-function, either the new or the () must have the conditional operator attached to it.

In other words, new obj?.qux?.baz() should be identical to const foo = obj?.qux?.baz; new foo().

Copy link
Member Author

Choose a reason for hiding this comment

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

optionals.push(node);
}

objectPath = objectPath.get("object");
Copy link
Member

Choose a reason for hiding this comment

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

for (let objectPath = path.get(key); objectPath.isMemberExpression(); objectPath = objectPath.get("object"))

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

objectPath = objectPath.get("object");
}

for (let i = optionals.length - 1; i >= 0; i--) {
Copy link
Member

Choose a reason for hiding this comment

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

for (const [i, node] of optionals.entries())

or

optionals.forEach((node, i) => {})

Copy link
Member Author

Choose a reason for hiding this comment

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

Can't, I have to reverse iterate.

Copy link
Member

Choose a reason for hiding this comment

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

Oh, right. .reverse() could do the trick, but the i would have to be subtracted from the length 🤔

if (atCall && t.isMemberExpression(chain)) {
if (loose) {
// To avoid a Function#call, we can instead re-grab the property from the context object.
// `a.?b.?()` translates roughly to `_a.b != null && _a.b()`
Copy link
Member

Choose a reason for hiding this comment

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

That would invoke a getter twice though...

Copy link
Member Author

Choose a reason for hiding this comment

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

Yup, that's why it's loose. 😉

@@ -416,7 +416,7 @@ defineType("LogicalExpression", {
});

defineType("MemberExpression", {
builder: ["object", "property", "computed"],
builder: ["object", "property", "computed", "optional"],
Copy link
Member

Choose a reason for hiding this comment

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

Does this need to be added to NewExpression / CallExpression as well?

Copy link
Member Author

Choose a reason for hiding this comment

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

Damn, missed that.

@hzoo
Copy link
Member

hzoo commented Jun 6, 2017

Copy link
Member

@gisenberg gisenberg left a comment

Choose a reason for hiding this comment

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

Looks good to me, thanks so much for the contribution! I'll work towards updating the TC39 proposal to cover some of the comments in this PR.

@Mouvedia
Copy link

Does this mean that it has been added to the preset-stage-1?

@jridgewell
Copy link
Member Author

It's in there now.

"babel-plugin-syntax-optional-chaining": "7.0.0-alpha.13"
},
"keywords": [
"babel-plugin"
Copy link
Member

Choose a reason for hiding this comment

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

We can more keyword here:

  • optional chaining
  • null propagator
  • elvis

Copy link
Contributor

Choose a reason for hiding this comment

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

Choose a reason for hiding this comment

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

Cyclops-Elvis operator

Copy link
Member

@xtuc xtuc left a comment

Choose a reason for hiding this comment

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

Nice work guys 👍

@hzoo hzoo merged commit 89d8f70 into babel:7.0 Jun 27, 2017
@hzoo
Copy link
Member

hzoo commented Jun 27, 2017

Great job @jridgewell and @xtuc, thanks for the review @gisenberg!!

@pronebird
Copy link

This is awesome! Can this be released on NPM please? I was not able to install syntax-optional-chaining plugin :/

@mastilver
Copy link

@pronebird see #5905

@hzoo
Copy link
Member

hzoo commented Jul 12, 2017

You want transform not syntax - https://github.com/babel/babel/releases/tag/v7.0.0-alpha.15, it's part of stage-1 too

https://babeljs.io/7/

@chenjigeng
Copy link

This is awesome! Can this support destructuring?
ex:
const { foo?: { bar } } = obj;

@Mouvedia
Copy link

@chenjigeng tc39/proposal-optional-chaining#19

@jridgewell jridgewell deleted the pr/5786 branch June 7, 2019 09:53
@lock lock bot added the outdated A closed issue/PR that is archived due to age. Recommended to make a new issue label Oct 3, 2019
@lock lock bot locked as resolved and limited conversation to collaborators Oct 3, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
outdated A closed issue/PR that is archived due to age. Recommended to make a new issue PR: New Feature 🚀 A type of pull request used for our changelog categories Spec: Optional Chaining
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet