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

Experimental support of Optional Chaining (at least static property access) via experimentalOptionalChaining #30167

Closed
5 tasks done
nodkz opened this issue Mar 1, 2019 · 23 comments
Labels
Duplicate An existing issue was already created

Comments

@nodkz
Copy link

nodkz commented Mar 1, 2019

Suggestion

Add experimentalOptionalChaining property to compilerOptions which will support at least static property access – the most required and expected feature.

Use Cases

For me, in 2019 the most important part of Optional Chaining is static property access. It will greatly improve DX for GraphQL client apps and make client code more robust and clean. 🚀

Current shortcomings:

  • if-check-hell 🤢
  • unchecked dottedPaths as string 💩
    • _.get(obj, ['some.nested.field']) with lodash.get
    • objectPath.get(obj, 'dottedPath') with object-path (900k weekly downloads 😱)

Examples

Assume we have following GraphQL response from the server on the client side:

interface IResponse {
  viewer?: {
    article?: {
      title?: string
    }
  }
}

const response: IResponse = await apolloClient.query(`query {
    viewer {
      article(id: 15) {
        title
      }
    }
}`);

On any level, graphql may return null, so before displaying title we must check viewer and article existence.

... with if-check-hell 🤢

if (response && response.viewer && response.viewer.article && response.viewer.article.title) {
  // ... display title
}

Pros: statically checked (if query changed, then we will be warned about un-existed props)
Cons: to much code

... with unchecked dottedPaths as string 💩

if (_.get(response, ['viewer.article.title'])) {
  // ... display title
}

Pros: quite clean code
Cons: statically UNchecked (if query changed, then we will NOT be warned about un-existed props checks)

... with Optional Chaining ❤️

if (response?.viewer?.article?.title) {
  // ... display title
}

Pros: very clean code & static checks
Cons: not implemented in TS, but already implemented about a year ago in Flow 0.74, Eslint, Babel.

Recap

We just have 3 levels deep query, but in most cases it's deeper.

if (response && response.viewer && response.viewer.article && response.viewer.article.title) {
if (_.get(response, ['viewer.article.title'])) {
if (response?.viewer?.article?.title) {

Definitely, OptionalChaining is the winner 🥇

Suggestion: repeat once more

Just add static property access from optional chaining proposal under experimentalOptionalChaining flag. This part is stable in the proposal and it is the most required feature in 2019.

Let's keep problematic optional chaining for function calling, short-circuiting, grouping, deletion, template literals – out of the scope of this issue.

Please do not reference this issue as a duplicate for issue #16 from the 2014 year, here are 2019 and requirements changed from that time.

Search Terms

Optional Chaining, null propagation.

Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.

If a miracle will happen 😂

If you like the fact that optional chaining was shaken off the dust from #16 and may even be implemented in the near future – send a SWAG from your company to the following address

050010, Kazakhstan, Almaty,
Kabanbay Batyra 21 apt 3,
Mobile +7707234004
Pavel Chertorogov

or just give a 👍

@nodkz
Copy link
Author

nodkz commented Mar 1, 2019

I'm prepared a simple video demo with Flow and Optional Chaining: https://youtu.be/NzlDE3cWnDc

Again: I'm telling about optional chaining for reading properties from objects.

PS. Let's keep problematic optional chaining for function calling, short-circuiting, grouping, deletion, template literals – out of the scope of this issue.

@lifeart
Copy link

lifeart commented Mar 1, 2019

Why this:

const value = this?.foo?.bar?.baz !== undefined ? this.foo.bar.baz : defaultValue;

Better than this?

const value = get(this, 'foo.bar.baz', defaultValue)

@nodkz
Copy link
Author

nodkz commented Mar 1, 2019

@lifeart please watch the video above when I breaking response interface. Such kind of things usually love to do backenders 😜

And I want to get strongly typed code with static analysis for such kind of things, rather than old-school dot-path in a string, which not understands TypeScript.

With optional chaining, TypeScript will help to catch such changes. 👍

@lifeart
Copy link

lifeart commented Mar 1, 2019

@nodkz this can be statically analyzed:

const value = get(get(get(this, 'some'), 'nested'), 'key', defaultValue);

@nodkz
Copy link
Author

nodkz commented Mar 1, 2019

@lifeart wow! I think that it cannot be used as a good solution 😜

In such case better to use if-check-hell 😈

@everdimension
Copy link

I don't think that a feature like this belongs in the language.

  • Having a long chain of unknown fields should be an unlikely pattern.
  • A typed language encourages writing out typed schemas so that you don't get into situations like this.
    • A schema where each field is unknown to be present doesn't usually make much sense.
    • ...But those cases where it does make sense the problem should be explicit. But having a syntax to help you hide the unreliability of a schema will encourage creating unreliable schemas. It should be the other way around.
  • Accessing the data through long chains is likely to be a violation of the Principle of Least Knowledge

@Voronar
Copy link

Voronar commented Mar 1, 2019

Duplicate of #16?

@RyanCavanaugh RyanCavanaugh added the Duplicate An existing issue was already created label Mar 1, 2019
@RyanCavanaugh
Copy link
Member

Where did this get brigaded from?

@john-dulaney
Copy link

@RyanCavanaugh HackerNews

@nodkz
Copy link
Author

nodkz commented Mar 1, 2019

@RyanCavanaugh from twitter https://twitter.com/nodkz/status/1101432188883603456?s=19 and GraphQL community. Need to wait while US wake up for bigger feedback ;)

@pygy
Copy link

pygy commented Mar 1, 2019

Also https://news.ycombinator.com/item?id=19283238

FWIW, I'm against this, I'd rather have proper recursive checking for R.path(['foo', 'bar'], {foo: {bar: 5}}).

On top of that, optional chaining is still a stage 1 proposal.

@dusave
Copy link

dusave commented Mar 1, 2019

I will be seeking Stage 2 for Optional Chaining at the next TC-39 meetup in March which, based on the outlook coming out of our November meetup, it should reach.

@RyanCavanaugh
Copy link
Member

RyanCavanaugh commented Mar 2, 2019

I'm going to leave this open only because people get tend to get salty when they click on a link and see we closed something, but the definitive issue tracking this is #16 and we are (still!) committed to waiting for TC39 to decide what the semantics of this feature would be.

After being burned multiple times by adding features to TS only to have the semantic rug pulled out from under us at the last second, there is seriously no number of upvotes that would have us adding a feature that could potentially drastically change runtime behavior at some point in the future.

Please do not reference this issue as a duplicate for issue #16 from the 2014 year, here are 2019 and requirements changed from that time

Nothing in that thread is not still true.

@tlackemann
Copy link

We have been waiting on optional chaining for years. What semantics are honestly going to change? I'm not asking in a snarky way or in an insincere way, I just honestly don't understand what could possible change from 3-years ago to now to whenever this gets implemented.

@RyanCavanaugh
Copy link
Member

What semantics are honestly going to change?

There are currently 29 open issues in the proposal repo, any one of which represents possible semantic changes, plus any new changes the committee might come up with.

From tc39/proposal-optional-chaining#2 / tc39/proposal-optional-chaining#10

  • Is undefined?.a.b an exception or undefined ?
  • Is (undefined?.a).b an exception or undefined ?
  • Is undefined?.a() an exception or undefined ?
  • What's behavior of a?.b() when b is undefined?

From tc39/proposal-optional-chaining#5

  • What's the syntax?

From tc39/proposal-optional-chaining#28

  • How, if at all, does it work with private fields?

From tc39/proposal-optional-chaining#40

  • How does it work with the delete operator?

tc39/proposal-optional-chaining#51

  • 65 comments and hundreds of votes on something like a dozen different syntax options, including a clarification that the "obvious" solution is "not available"

From tc39/proposal-optional-chaining#61

  • Wait, is it a.?b or a?.b ?

From tc39/proposal-optional-chaining#69

  • Will null?.prop result in undefined or null ?

@dusave
Copy link

dusave commented Mar 2, 2019

I completely concur with @RyanCavanaugh's assessment. We've come a long way and we're closer than ever, but part of the reason it's been such a long running proposal is the complexity around JavaScript, the semantics that exist and need to continue to work, and the readability/understandability of the code proposed.

There are currently 29 open issues in the proposal repo...

One of the first things I plan to do once we reach Stage 2 is to go and clean out issues. As a semi-OCD person, this is going to scratch an itch I've had for awhile 😆. We simply didn't want to shut down discussions until we were a bit firmer in our approach, and Stage 2 will be that point.

I can also answer some of the above questions:

  1. What's the syntax?
  • The syntax that we've largely reached consensus on (which will become official once it reaches Stage 2) is:
    • a?.b?.c for static property lookup
    • a?.[b] for dynamic property lookup
    • a?.() for optional function execution
    • delete a?.b for optional deletion
  • Not everyone is thrilled about these (trust me, that's part of why this proposal has taken as long as it has), but believe me, these are the best options that we have. If you've thought of something else, feel free to search in the issues because it's more than likely been discussed.
  1. How, if at all, does it work with private fields?
  • This one isn't baked into the proposal yet, simply because private fields themselves aren't baked yet. So we don't want to hold up this proposal if that one happens to stall out. Once that one has reached Stage 4, we will address it then.
  1. How does it work with the delete operator?
  • See Answer 1
  1. Will null?.prop result in undefined or null ?
  • This will result in undefined because by that logic, you are looking for prop, which is not defined. You can be more verbose in your code if you care about getting the null.

I know most if not all of you are as excited as I am for this proposal. It's been a long road and I ask you wait just a little longer. These things take time, and since JavaScript is forever (once a proposal reaches Stage 4 and is officially part of the language, it can't be removed save for a security flaw), we want to make sure we've covered all the angles and considered all possibilities.

@wintercounter
Copy link

Maybe this is the wrong place to discuss the proposal but why it simply couldn't be ?foo.bar.baz.buz

@RyanCavanaugh
Copy link
Member

This is the wrong place to discuss the proposal 😉

@nodkz
Copy link
Author

nodkz commented Mar 4, 2019

@dustinsavery will be great if Optional Chaining will be split into several parts (if possible).

For example static property access may become Stage-3 or even more Stage-4, this gives legitimacy to TS team for implementing this feature in near time.

And other non-cozy parts like dynamic lookup, function execution, delete operator – may stay at Stage-1 or 2.

PS. Divide and conquer complex and loooong parts. 😉

@dusave
Copy link

dusave commented Mar 4, 2019

@nodkz Trust me when I say we have tried many different angles to get this through. The fact that it hasn’t advanced isn’t for lack of trying or brainstorming new ideas or approaches. This is one that I tried during our last meeting, but that was voted down. We have a plan that has buyoff, now we just require patience from the community.

@typescript-bot
Copy link
Collaborator

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

@nodkz
Copy link
Author

nodkz commented Mar 7, 2019

@typescript-bot holy life, you will close issues from us??? And we – the bags of blood and bones are nothing for you... Realy???

@RyanCavanaugh @dustinsavery please alive this issue! and remove duplicate to relax the typescripts's bot.

I understand that complex questions better to keep closed by the bot... But it's too early to allow bots to take decisions in 2019...

#diversity #humanity_please_live #no_bots

@microsoft microsoft locked as resolved and limited conversation to collaborators Mar 7, 2019
@RyanCavanaugh
Copy link
Member

As a human, I approve the bot's behavior.

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

No branches or pull requests