Suggestion: "safe navigation operator", i.e. x?.y #16

Open
RyanCavanaugh opened this Issue Jul 15, 2014 · 151 comments

Comments

Projects
None yet
@RyanCavanaugh
Member

RyanCavanaugh commented Jul 15, 2014

C# and other languages have syntax sugar for accessing property chains where null (or in our case, undefined) might be encountered at any point in the object hierarchy.

var x = { y: { z: null, q: undefined } };
console.log(x?.y?.z?.foo); // Should print 'null'
console.log(x?.baz); // Still an error
console.log(x.y.q?.bar); // Should print 'undefined'

Need proposal on what exactly we should codegen, keeping in mind side effects of accessors.


Edit by @DanielRosenwasser February 27, 2018: This proposal is also called the "null propagation" operator.

@JsonFreeman

This comment has been minimized.

Show comment
Hide comment
@JsonFreeman

JsonFreeman Jul 15, 2014

Contributor

So in the first example, we might emit it like the following:

x && x.y && x.y.z && x.y.z.foo

But then we'd have to somehow make x, y, z, and foo each evaluate at most once.

Contributor

JsonFreeman commented Jul 15, 2014

So in the first example, we might emit it like the following:

x && x.y && x.y.z && x.y.z.foo

But then we'd have to somehow make x, y, z, and foo each evaluate at most once.

@DanielRosenwasser

This comment has been minimized.

Show comment
Hide comment
@DanielRosenwasser

DanielRosenwasser Jul 15, 2014

Member

You also really can't do && in many cases because truthiness becomes a bit of a problem for primitives.

For example:

"     "?.trim()?.indexOf("hello")

gives "".

So you need to do some explicit comparisons to null using == for the general case, unless we leverage the type system (which would be fairly cool to see us do).

We could possibly emit a monadic-bind function (not pretty for the JS output), or use some transformation on ternary operators (closer to typical JS equivalent). I'm clearly a little biased towards the latter.

Member

DanielRosenwasser commented Jul 15, 2014

You also really can't do && in many cases because truthiness becomes a bit of a problem for primitives.

For example:

"     "?.trim()?.indexOf("hello")

gives "".

So you need to do some explicit comparisons to null using == for the general case, unless we leverage the type system (which would be fairly cool to see us do).

We could possibly emit a monadic-bind function (not pretty for the JS output), or use some transformation on ternary operators (closer to typical JS equivalent). I'm clearly a little biased towards the latter.

@philipbulley

This comment has been minimized.

Show comment
Hide comment
@philipbulley

philipbulley Sep 1, 2014

Contributor

👍

Contributor

philipbulley commented Sep 1, 2014

👍

@RyanCavanaugh

This comment has been minimized.

Show comment
Hide comment
@RyanCavanaugh

RyanCavanaugh Sep 2, 2014

Member

Ideally we'd have ES7 (or ES8 or ES9 or ...) implement this first since there'd probably be some disagreement about the exact semantics about whether or not to actually use 0/"" as falsy primitives for the purposes of any operator here.

Member

RyanCavanaugh commented Sep 2, 2014

Ideally we'd have ES7 (or ES8 or ES9 or ...) implement this first since there'd probably be some disagreement about the exact semantics about whether or not to actually use 0/"" as falsy primitives for the purposes of any operator here.

@NoelAbrahams

This comment has been minimized.

Show comment
Hide comment
@NoelAbrahams

NoelAbrahams Sep 11, 2014

👍 I'd like to see TypeScript gets this in first without having to wait for ESxx.

👍 I'd like to see TypeScript gets this in first without having to wait for ESxx.

@brian428

This comment has been minimized.

Show comment
Hide comment
@brian428

brian428 Oct 1, 2014

The fact that simple and insanely useful null-safety operators like "?." and "?:" AREN'T in the ES6 spec means the people putting together the ES6 spec should be hanging their heads in shame. This is such a simple and obvious thing that to not incorporate it would frankly be insane. There's a reason most modern languages support these: they're indispensable.

I realize this would be a deviation from the current spec (since the current spec is so short-sighted as to omit this). But it's so ridiculously useful that I think this single deviation would be justified. The vast (VAST) majority of TS developers wouldn't be affected by minor changes to the implementation, if or when this finally gets added to an ES specification. The huge benefits this would offer is worth the potential future impact to a tiny fraction of developers. And given the laughably slow ES spec process, this wouldn't even matter at all for several years (at minimum).

brian428 commented Oct 1, 2014

The fact that simple and insanely useful null-safety operators like "?." and "?:" AREN'T in the ES6 spec means the people putting together the ES6 spec should be hanging their heads in shame. This is such a simple and obvious thing that to not incorporate it would frankly be insane. There's a reason most modern languages support these: they're indispensable.

I realize this would be a deviation from the current spec (since the current spec is so short-sighted as to omit this). But it's so ridiculously useful that I think this single deviation would be justified. The vast (VAST) majority of TS developers wouldn't be affected by minor changes to the implementation, if or when this finally gets added to an ES specification. The huge benefits this would offer is worth the potential future impact to a tiny fraction of developers. And given the laughably slow ES spec process, this wouldn't even matter at all for several years (at minimum).

@djarekg

This comment has been minimized.

Show comment
Hide comment
@djarekg

djarekg Oct 1, 2014

I totally agree with brain428

djarekg commented Oct 1, 2014

I totally agree with brain428

@fdecampredon

This comment has been minimized.

Show comment
Hide comment
@fdecampredon

fdecampredon Oct 2, 2014

@brian428 the problem here is that that operator maybe implemented in ES7 so if typescript go with a specification that ends up differing from the final ES7 one, nobody will be happy.

@brian428 the problem here is that that operator maybe implemented in ES7 so if typescript go with a specification that ends up differing from the final ES7 one, nobody will be happy.

@NoelAbrahams

This comment has been minimized.

Show comment
Hide comment
@NoelAbrahams

NoelAbrahams Oct 2, 2014

the problem here is that that operator maybe implemented in ES7 so if typescript go with a specification that ends up differing from the final ES7 one, nobody will be happy.

I think it is a more positive approach for TypeScript to implement features that may _potentially_ (or may not) make it into a future ES version, because it will be a useful testbed for influencing ES direction.

Here is an example of ES discussion being influenced by TypeScript:

The TypeScript... option to declare and initialize via a private prefix on one of constructor's parameters would be helpful to many developers

Furthermore, it's certainly possible for ES to adopt a feature that is already present in TypeScript, but with different semantics (for example, around how modules work).

the problem here is that that operator maybe implemented in ES7 so if typescript go with a specification that ends up differing from the final ES7 one, nobody will be happy.

I think it is a more positive approach for TypeScript to implement features that may _potentially_ (or may not) make it into a future ES version, because it will be a useful testbed for influencing ES direction.

Here is an example of ES discussion being influenced by TypeScript:

The TypeScript... option to declare and initialize via a private prefix on one of constructor's parameters would be helpful to many developers

Furthermore, it's certainly possible for ES to adopt a feature that is already present in TypeScript, but with different semantics (for example, around how modules work).

@RyanCavanaugh

This comment has been minimized.

Show comment
Hide comment
@RyanCavanaugh

RyanCavanaugh Oct 2, 2014

Member

it's certainly possible for ES to adopt a feature that is already present in TypeScript, but with different semantics

I should note that we broadly consider this to be a worst-case scenario. We really wanted modules in ES6 to be finalized before we declared TypeScript 1.0, but the committee's schedule delays prevented that. This is something to be avoided, not repeated. We'd really like to hit features that have either a ~0% chance of making it into ES7+ (e.g. type annotations), or have a ~100% chance of making it in with easily-defined semantics (e.g. where fat arrow was two years ago). New operators are likely to fall in the awkward middle.

Member

RyanCavanaugh commented Oct 2, 2014

it's certainly possible for ES to adopt a feature that is already present in TypeScript, but with different semantics

I should note that we broadly consider this to be a worst-case scenario. We really wanted modules in ES6 to be finalized before we declared TypeScript 1.0, but the committee's schedule delays prevented that. This is something to be avoided, not repeated. We'd really like to hit features that have either a ~0% chance of making it into ES7+ (e.g. type annotations), or have a ~100% chance of making it in with easily-defined semantics (e.g. where fat arrow was two years ago). New operators are likely to fall in the awkward middle.

@philipbulley

This comment has been minimized.

Show comment
Hide comment
@philipbulley

philipbulley Nov 19, 2014

Contributor

In the worst case, if ES7 does differ, could a compiler flag support the legacy TS implementation, thus offering a grace period? This coupled with clear migration documentation should offer developers a straightforward route to any new standard.

Ultimately, use of any such feature—although insanely useful—isn't essential by developers. TS should make potential future implications of it's usage abundantly clear from day one. Don't like the idea of a potential managed refactor, don't use it. Perhaps an opt-in compiler flag to enforce this message?

TS shouldn't go wild with wanting to influence ES, but in small isolated cases such as this, it'd be a shame if TS were to completely shy away.

Contributor

philipbulley commented Nov 19, 2014

In the worst case, if ES7 does differ, could a compiler flag support the legacy TS implementation, thus offering a grace period? This coupled with clear migration documentation should offer developers a straightforward route to any new standard.

Ultimately, use of any such feature—although insanely useful—isn't essential by developers. TS should make potential future implications of it's usage abundantly clear from day one. Don't like the idea of a potential managed refactor, don't use it. Perhaps an opt-in compiler flag to enforce this message?

TS shouldn't go wild with wanting to influence ES, but in small isolated cases such as this, it'd be a shame if TS were to completely shy away.

@kevinbarabash

This comment has been minimized.

Show comment
Hide comment
@kevinbarabash

kevinbarabash Nov 29, 2014

Maybe we could put together a strawman proposal for this and then have a reference implementation behind a --harmony flag (or something like that). That way we can drive ES7 development of this feature.

Maybe we could put together a strawman proposal for this and then have a reference implementation behind a --harmony flag (or something like that). That way we can drive ES7 development of this feature.

@metaweta

This comment has been minimized.

Show comment
Hide comment
@metaweta

metaweta Dec 13, 2014

To prevent side-effects due to repeated look-ups, the compiler will either have to output temporary variables:

($tmp0 = x, $tmp0 === void 0 ? void 0 : 
    ($tmp1=$tmp0.y,  $tmp1 === void 0 ? void 0 : 
        ($tmp2 = $tmp1.z,  $tmp2 === void 0 ? void 0 : $tmp2)))

or use a memoizing membrane based on Proxy.

From a categorical point of view, this is just the maybe monad applied to property lookup, so it's a very natural feature for a language where all property lookups may return undefined. I'd be surprised if ES7 adopted any semantics other than the one described by the code above.

To prevent side-effects due to repeated look-ups, the compiler will either have to output temporary variables:

($tmp0 = x, $tmp0 === void 0 ? void 0 : 
    ($tmp1=$tmp0.y,  $tmp1 === void 0 ? void 0 : 
        ($tmp2 = $tmp1.z,  $tmp2 === void 0 ? void 0 : $tmp2)))

or use a memoizing membrane based on Proxy.

From a categorical point of view, this is just the maybe monad applied to property lookup, so it's a very natural feature for a language where all property lookups may return undefined. I'd be surprised if ES7 adopted any semantics other than the one described by the code above.

@basarat

This comment has been minimized.

Show comment
Hide comment
@basarat

basarat Feb 12, 2015

Contributor

The codeplex issue had quite a number of votes (61)

I really badly need this to ease the pain of using atom for atom-typescript.

It is very idiomatic in coffescript code (although I would like it not to be as popular as determinism is better than a fudgy ?). Open any coffescript file, especially one that works with the DOM directly like space-pen (where functions can run after the view is destroyed or before the view is attached) and you will find a gazillion ? usages. e.g. this file has 16 https://github.com/atom-community/autocomplete-plus/blob/f17659ad4fecbd69855dfaf00c11856572ad26e7/lib/suggestion-list-element.coffee

Again I don't like that I need this, but its the state of JavaScript, and I'd rather ? than a million if( && fest ) { then }

But I really really need it to keep my code readable. Its also very common to need this when you are waiting for an XHR to complete and angular runs its digest loop.

Contributor

basarat commented Feb 12, 2015

The codeplex issue had quite a number of votes (61)

I really badly need this to ease the pain of using atom for atom-typescript.

It is very idiomatic in coffescript code (although I would like it not to be as popular as determinism is better than a fudgy ?). Open any coffescript file, especially one that works with the DOM directly like space-pen (where functions can run after the view is destroyed or before the view is attached) and you will find a gazillion ? usages. e.g. this file has 16 https://github.com/atom-community/autocomplete-plus/blob/f17659ad4fecbd69855dfaf00c11856572ad26e7/lib/suggestion-list-element.coffee

Again I don't like that I need this, but its the state of JavaScript, and I'd rather ? than a million if( && fest ) { then }

But I really really need it to keep my code readable. Its also very common to need this when you are waiting for an XHR to complete and angular runs its digest loop.

@basarat

This comment has been minimized.

Show comment
Hide comment
@basarat

basarat Feb 12, 2015

Contributor

Okay now I have read the thread and see why we are waiting. I understand sigh.

Contributor

basarat commented Feb 12, 2015

Okay now I have read the thread and see why we are waiting. I understand sigh.

@RyanCavanaugh RyanCavanaugh changed the title from Suggestion: "safe navigation operator", i.e. x.?y to Suggestion: "safe navigation operator", i.e. x?.y Feb 12, 2015

@basarat

This comment has been minimized.

Show comment
Hide comment
@basarat

basarat Feb 12, 2015

Contributor

we'd have to somehow make x, y, z, and foo each evaluate at most once.

coffeescript does do some optimizations e.g. stores intermediate access results:

typeof foo !== "undefined" && foo !== null ? (ref = foo.bar) != null ? ref.baz() : void 0 : void 0;

(I strongly feel the undefined check is unnecessary for typescript : as we should have a var init typechecked by typescript)

Contributor

basarat commented Feb 12, 2015

we'd have to somehow make x, y, z, and foo each evaluate at most once.

coffeescript does do some optimizations e.g. stores intermediate access results:

typeof foo !== "undefined" && foo !== null ? (ref = foo.bar) != null ? ref.baz() : void 0 : void 0;

(I strongly feel the undefined check is unnecessary for typescript : as we should have a var init typechecked by typescript)

@rayshan

This comment has been minimized.

Show comment
Hide comment

rayshan commented Jun 18, 2015

+1

@basarat

This comment has been minimized.

Show comment
Hide comment
@basarat

basarat Jun 18, 2015

Contributor

In news today, Dart is getting official support for it : https://github.com/gbracha/nullAwareOperators/blob/master/proposal.md

Contributor

basarat commented Jun 18, 2015

In news today, Dart is getting official support for it : https://github.com/gbracha/nullAwareOperators/blob/master/proposal.md

@ghost

This comment has been minimized.

Show comment
Hide comment
@ghost

ghost Jul 1, 2015

Very important feature.

Possibly an off-the-wall idea but the codegen for this feature could be done very easily without side effects if everyone decided that it would be OK to handle the feature with keyed property access:

if (aaa?.bbb?.ccc) {}

Could compile to

if (__chain(aaa, "bbb", "ccc")) {}

A __chain function would have to be emitted similar to __extends. The __chain function could just iterate through the arguments array, returning null when the upcoming member is not instanceof Object or does not contain the member name. Function calls could be handled by passing in an array as a parameter (and using .apply() under the covers), so ...

if (aaa?.bbb?.ccc?(1, 2, 3)) {}

Could compile to

if (__chain(aaa, "bbb", "ccc", [1, 2, 3])) {}

This would also keep the generated JS reasonably idiomatic, even for long chains.

Needs refinement obviously ... but maybe there is something here?

ghost commented Jul 1, 2015

Very important feature.

Possibly an off-the-wall idea but the codegen for this feature could be done very easily without side effects if everyone decided that it would be OK to handle the feature with keyed property access:

if (aaa?.bbb?.ccc) {}

Could compile to

if (__chain(aaa, "bbb", "ccc")) {}

A __chain function would have to be emitted similar to __extends. The __chain function could just iterate through the arguments array, returning null when the upcoming member is not instanceof Object or does not contain the member name. Function calls could be handled by passing in an array as a parameter (and using .apply() under the covers), so ...

if (aaa?.bbb?.ccc?(1, 2, 3)) {}

Could compile to

if (__chain(aaa, "bbb", "ccc", [1, 2, 3])) {}

This would also keep the generated JS reasonably idiomatic, even for long chains.

Needs refinement obviously ... but maybe there is something here?

@kevinbarabash

This comment has been minimized.

Show comment
Hide comment
@kevinbarabash

kevinbarabash Jul 1, 2015

If aaa?.bbb?.ccc? returns the value of a.b.c if all props exist and it happens to be a function, then couldn't

if (aaa?.bbb?.ccc?(1, 2, 3)) {}

compile to

if (__chain(aaa, "bbb", "ccc")(1, 2, 3)) {}

?

If aaa?.bbb?.ccc? returns the value of a.b.c if all props exist and it happens to be a function, then couldn't

if (aaa?.bbb?.ccc?(1, 2, 3)) {}

compile to

if (__chain(aaa, "bbb", "ccc")(1, 2, 3)) {}

?

@ghost

This comment has been minimized.

Show comment
Hide comment
@ghost

ghost Jul 1, 2015

@metaweta: Your solution is only checking for void 0 ... doesn't that kind of defeat the purpose of the feature?

What about this:

var result = one?.two?.three;

Generates:

var $a, $b, $c;
var result = $a = one, $b = $a ? $a.two : void 0, $b ? $b.three : void 0;

I'm pretty sure this handles all the cases. Function calls would probably need an instanceof Function check.

(Minor downside here of unexpected local variables being emitted ... could be wrapped in an IIFE maybe)

ghost commented Jul 1, 2015

@metaweta: Your solution is only checking for void 0 ... doesn't that kind of defeat the purpose of the feature?

What about this:

var result = one?.two?.three;

Generates:

var $a, $b, $c;
var result = $a = one, $b = $a ? $a.two : void 0, $b ? $b.three : void 0;

I'm pretty sure this handles all the cases. Function calls would probably need an instanceof Function check.

(Minor downside here of unexpected local variables being emitted ... could be wrapped in an IIFE maybe)

@ghost

This comment has been minimized.

Show comment
Hide comment
@ghost

ghost Jul 1, 2015

@KevinB7: What happens if ccc isn't a function? With the code you've described, __chain would always have to return a valid function, or a TypeError would be emitted.

ghost commented Jul 1, 2015

@KevinB7: What happens if ccc isn't a function? With the code you've described, __chain would always have to return a valid function, or a TypeError would be emitted.

@metaweta

This comment has been minimized.

Show comment
Hide comment
@metaweta

metaweta Jul 1, 2015

@Back-io When a property is absent, a lookup returns undefined === void 0. Your solution fails for looking up properties of falsey values like empty string and zero.

metaweta commented Jul 1, 2015

@Back-io When a property is absent, a lookup returns undefined === void 0. Your solution fails for looking up properties of falsey values like empty string and zero.

@ghost

This comment has been minimized.

Show comment
Hide comment
@ghost

This comment has been minimized.

Show comment
Hide comment
@ghost

ghost Jul 1, 2015

Also, I would imagine the TS team isn't crazy about using loose equality due to the fact that some people's linters warn against it.

ghost commented Jul 1, 2015

Also, I would imagine the TS team isn't crazy about using loose equality due to the fact that some people's linters warn against it.

@metaweta

This comment has been minimized.

Show comment
Hide comment
@metaweta

This comment has been minimized.

Show comment
Hide comment
@metaweta

metaweta Jul 1, 2015

@Back-io Regarding null, I'm not sure what the intended semantics of a?.b is. If it's "If the property b is defined then use it", then my code is almost correct. The only way you'd get null is if it's assigned to be null, because lookups of nonexistent properties return undefined. It does fail to catch the case where the property exists but is set to undefined. To be completely correct, it would check with Object.hasOwnProperty() instead of comparing to void 0 === undefined.

If the semantics are "if the property b is truthy then use it", your code is fine and to some extent matches JS idiom.

metaweta commented Jul 1, 2015

@Back-io Regarding null, I'm not sure what the intended semantics of a?.b is. If it's "If the property b is defined then use it", then my code is almost correct. The only way you'd get null is if it's assigned to be null, because lookups of nonexistent properties return undefined. It does fail to catch the case where the property exists but is set to undefined. To be completely correct, it would check with Object.hasOwnProperty() instead of comparing to void 0 === undefined.

If the semantics are "if the property b is truthy then use it", your code is fine and to some extent matches JS idiom.

@ghost

This comment has been minimized.

Show comment
Hide comment
@ghost

ghost Jul 1, 2015

Umm ... unless I'm missing something ... the changes you made to the fiddle just further prove me right ... var result is still undefined in all 3 cases. And I'm not sure what case you're trying to bring forward by extending the primitive prototypes ...

ghost commented Jul 1, 2015

Umm ... unless I'm missing something ... the changes you made to the fiddle just further prove me right ... var result is still undefined in all 3 cases. And I'm not sure what case you're trying to bring forward by extending the primitive prototypes ...

@ghost

This comment has been minimized.

Show comment
Hide comment
@ghost

ghost Jul 1, 2015

I'm fairly certain that the behavior of the feature would be to short circuit with void 0 instead of generating an error. (I like your idea above that it should always return void 0 in the case of a missing member).

ghost commented Jul 1, 2015

I'm fairly certain that the behavior of the feature would be to short circuit with void 0 instead of generating an error. (I like your idea above that it should always return void 0 in the case of a missing member).

@RyanCavanaugh

This comment has been minimized.

Show comment
Hide comment
@RyanCavanaugh

RyanCavanaugh Sep 7, 2017

Member

This is not the thread to rehash the TC39 discussion

If you want to weigh on in the proposal, go comment on it at the appropriate place. I have opinions too but dropping them in this thread isn't going to do anything.

Member

RyanCavanaugh commented Sep 7, 2017

This is not the thread to rehash the TC39 discussion

If you want to weigh on in the proposal, go comment on it at the appropriate place. I have opinions too but dropping them in this thread isn't going to do anything.

@mrpmorris

This comment has been minimized.

Show comment
Hide comment
@mrpmorris

mrpmorris Sep 13, 2017

I created something simple that meets my current needs. It will only work on a chain where each link is a property name, so accessing an element in an array (for example) isn't supported.

Implementing a really simple Elvis Operator in TypeScript

I created something simple that meets my current needs. It will only work on a chain where each link is a property name, so accessing an element in an array (for example) isn't supported.

Implementing a really simple Elvis Operator in TypeScript

@tolgaek

This comment has been minimized.

Show comment
Hide comment
@tolgaek

tolgaek Sep 13, 2017

Also if you have lodash/underscore, you can already use _.get(Book, 'author.name.firstName') which will do what this wants

Edit: Appearently this is bad advice because of type issues with _.get() method. See comment below

tolgaek commented Sep 13, 2017

Also if you have lodash/underscore, you can already use _.get(Book, 'author.name.firstName') which will do what this wants

Edit: Appearently this is bad advice because of type issues with _.get() method. See comment below

@srghma

This comment has been minimized.

Show comment
Hide comment
@srghma

srghma Sep 13, 2017

@tolgaek , _.get has bad typings, even with this better typings (not yet merged, because of authors) typescript can definitely deduce type of result only if depth of object is 1, in all other cases it's any and must be checked in runtime

On other hand with elvis operator typescript may be able to infer type of result in objects with any depth, this is why Im looking forward to elvis operator

srghma commented Sep 13, 2017

@tolgaek , _.get has bad typings, even with this better typings (not yet merged, because of authors) typescript can definitely deduce type of result only if depth of object is 1, in all other cases it's any and must be checked in runtime

On other hand with elvis operator typescript may be able to infer type of result in objects with any depth, this is why Im looking forward to elvis operator

@tolgaek

This comment has been minimized.

Show comment
Hide comment
@tolgaek

tolgaek Sep 13, 2017

oh I see, I didn't know there was typings issue. Thanks @BjornMelgaard

tolgaek commented Sep 13, 2017

oh I see, I didn't know there was typings issue. Thanks @BjornMelgaard

@orta orta referenced this issue in artsy/emission Sep 14, 2017

Merged

Submit draft consignments to metaphysics #752

@chaowlert

This comment has been minimized.

Show comment
Hide comment
@chaowlert

chaowlert Sep 16, 2017

@mhegazy can't this feature be implemented first and mark as experimental feature? I think people should not have problem if there is spec changed in experimental feature.

@mhegazy can't this feature be implemented first and mark as experimental feature? I think people should not have problem if there is spec changed in experimental feature.

@ghost

This comment has been minimized.

Show comment
Hide comment
@ghost

ghost Sep 16, 2017

Elvis is not amused waiting this long.

ghost commented Sep 16, 2017

Elvis is not amused waiting this long.

@gs-akhan

This comment has been minimized.

Show comment
Hide comment
@gs-akhan

gs-akhan Sep 17, 2017

It has landed in Babel7. Typescript, we are running slow.. Someone please make this happen.
:)

It has landed in Babel7. Typescript, we are running slow.. Someone please make this happen.
:)

@alangpierce

This comment has been minimized.

Show comment
Hide comment
@alangpierce

alangpierce Sep 17, 2017

@gs-akhan the Babel plugin implements an old version of the proposal from a few months ago. There have been changes to the proposal since then (including a significant change to how the operator is parsed), and there likely will be more changes before the feature reaches stage 2 (let alone stage 3), so any code written with the current babel plugin could break when the actual feature is released. Babel intentionally implements proposed features before they are stable so that the spec authors and other interested parties can try out the feature as proposed. Just because Babel has implemented a feature doesn't mean it can be implemented in a way that won't require breaking changes in the future.

@gs-akhan the Babel plugin implements an old version of the proposal from a few months ago. There have been changes to the proposal since then (including a significant change to how the operator is parsed), and there likely will be more changes before the feature reaches stage 2 (let alone stage 3), so any code written with the current babel plugin could break when the actual feature is released. Babel intentionally implements proposed features before they are stable so that the spec authors and other interested parties can try out the feature as proposed. Just because Babel has implemented a feature doesn't mean it can be implemented in a way that won't require breaking changes in the future.

@gs-akhan

This comment has been minimized.

Show comment
Hide comment
@gs-akhan

gs-akhan Sep 17, 2017

@alangpierce That makes sense. Thanks

@alangpierce That makes sense. Thanks

@RyanCavanaugh

This comment has been minimized.

Show comment
Hide comment
@RyanCavanaugh

RyanCavanaugh Sep 18, 2017

Member

I get that this is a really, really nice operator, but having it be available before its rules have been ironed out is a footgun and we're not going to do that. The runtime behavior of the operator is still in flux; if you write code today it might stop working tomorrow in ways that aren't immediately apparent - maybe rare crashes, maybe data corruption, who knows? A little patience now will save you a lot of pain a few months down the road.

Member

RyanCavanaugh commented Sep 18, 2017

I get that this is a really, really nice operator, but having it be available before its rules have been ironed out is a footgun and we're not going to do that. The runtime behavior of the operator is still in flux; if you write code today it might stop working tomorrow in ways that aren't immediately apparent - maybe rare crashes, maybe data corruption, who knows? A little patience now will save you a lot of pain a few months down the road.

@kube kube referenced this issue in kube/monolite Sep 18, 2017

Open

Possibly null values in the input data? #20

@atrauzzi

This comment has been minimized.

Show comment
Hide comment
@atrauzzi

atrauzzi Sep 18, 2017

Starting to think this ticket should be locked (not closed) until the spec is ready.

atrauzzi commented Sep 18, 2017

Starting to think this ticket should be locked (not closed) until the spec is ready.

@oliverjanik

This comment has been minimized.

Show comment
Hide comment
@oliverjanik

oliverjanik Sep 19, 2017

When is the spec going to be ready?

When is the spec going to be ready?

@gisenberg

This comment has been minimized.

Show comment
Hide comment
@gisenberg

gisenberg Sep 19, 2017

Contributor

@oliverjanik The current draft specification can be found here. There's an agenda item to advance the proposal to stage 2 at the next TC39 meeting (9/26-9/28). I'll be presenting these slides at that time. For folks who'd like to review early and provide feedback, please file issues on the proposal repo.

Contributor

gisenberg commented Sep 19, 2017

@oliverjanik The current draft specification can be found here. There's an agenda item to advance the proposal to stage 2 at the next TC39 meeting (9/26-9/28). I'll be presenting these slides at that time. For folks who'd like to review early and provide feedback, please file issues on the proposal repo.

@markwhitfeld

This comment has been minimized.

Show comment
Hide comment
@markwhitfeld

markwhitfeld Sep 22, 2017

Thank you so much @gisenberg for championing this issue for us! I was thinking about putting together a summary slide deck to help clear up the options around the operator that could be used at the TC39 meeting as to avoid confusion, but you have done this already. Awesome work!
Maybe just one other thing that could be beneficial to the TC39 conversation (and slide deck) is to look at the semantics and syntax of the operator in other languages. Although other languages should not necessarily dictate how it should work in Javascript, it would be helpful to keep the operator similar to that of other languages as to avoid confusion.
Good luck for next week!!!

Thank you so much @gisenberg for championing this issue for us! I was thinking about putting together a summary slide deck to help clear up the options around the operator that could be used at the TC39 meeting as to avoid confusion, but you have done this already. Awesome work!
Maybe just one other thing that could be beneficial to the TC39 conversation (and slide deck) is to look at the semantics and syntax of the operator in other languages. Although other languages should not necessarily dictate how it should work in Javascript, it would be helpful to keep the operator similar to that of other languages as to avoid confusion.
Good luck for next week!!!

@noppa

This comment has been minimized.

Show comment
Hide comment
@noppa

noppa Sep 28, 2017

Sorry for going a bit off topic once again, but I figured some folks here could find it interesting that in Flow it's now possible to add somewhat working type definitions for safe getter functions like _.get.

Example: flowtype.org/try

Not the prettiest piece of code ever and it doesn't correctly distinguish between null and undefined, but other than that it seems to work quite nicely.

AFAIK the only thing that's missing from TS for it to do the same is something like that $NonMaybeType.
Not that it would remove the need for this operator, of course, I just thought that was cool.

noppa commented Sep 28, 2017

Sorry for going a bit off topic once again, but I figured some folks here could find it interesting that in Flow it's now possible to add somewhat working type definitions for safe getter functions like _.get.

Example: flowtype.org/try

Not the prettiest piece of code ever and it doesn't correctly distinguish between null and undefined, but other than that it seems to work quite nicely.

AFAIK the only thing that's missing from TS for it to do the same is something like that $NonMaybeType.
Not that it would remove the need for this operator, of course, I just thought that was cool.

@srghma srghma referenced this issue in ramda/ramda Sep 30, 2017

Closed

ts typings for path must return any #2307

@RyanCavanaugh

This comment has been minimized.

Show comment
Hide comment
@RyanCavanaugh

RyanCavanaugh Oct 2, 2017

Member

This did not reach Stage 2 at the latest TC39 meeting due to concerns about syntactic consistency regarding bracket vs dot vs call access and semantic behavior (predictability around side effects of expressions to the right of an undefined/null-evaluated operand) as well as open questions about what kind of expressions are allowed to be no-op'd (e.g. x.?b() when x.b is a number)

(vote 🎉 on this comment to throw rotten fruit)

Member

RyanCavanaugh commented Oct 2, 2017

This did not reach Stage 2 at the latest TC39 meeting due to concerns about syntactic consistency regarding bracket vs dot vs call access and semantic behavior (predictability around side effects of expressions to the right of an undefined/null-evaluated operand) as well as open questions about what kind of expressions are allowed to be no-op'd (e.g. x.?b() when x.b is a number)

(vote 🎉 on this comment to throw rotten fruit)

@oliverjanik

This comment has been minimized.

Show comment
Hide comment
@oliverjanik

oliverjanik Oct 5, 2017

Thanks for letting us know. What a bummer. Maybe the scope needs to be narrowed so it's simpler but still useful?

Thanks for letting us know. What a bummer. Maybe the scope needs to be narrowed so it's simpler but still useful?

@kitsonk

This comment has been minimized.

Show comment
Hide comment
@kitsonk

kitsonk Oct 5, 2017

Contributor

Maybe the scope needs to be narrowed so it's simpler but still useful?

This is the challenge that TC39 faces, which, while sucky, I have to admit I am glad people are going through this. It is really hard for them to introduce a fairly complex level syntax and in fact, in this case the weak typing of the language is actually causing a significant amount of edge cases that have to be addressed, or you get code that goes 💥 which isn't good for anyone. I don't think if you introduce an operator like this you can actually narrow its scope. It would be easier for the implementors to cover 90% of the cases, but I don't think ¯\_(ツ)_/¯ is valid code for the remaining 10%.

Contributor

kitsonk commented Oct 5, 2017

Maybe the scope needs to be narrowed so it's simpler but still useful?

This is the challenge that TC39 faces, which, while sucky, I have to admit I am glad people are going through this. It is really hard for them to introduce a fairly complex level syntax and in fact, in this case the weak typing of the language is actually causing a significant amount of edge cases that have to be addressed, or you get code that goes 💥 which isn't good for anyone. I don't think if you introduce an operator like this you can actually narrow its scope. It would be easier for the implementors to cover 90% of the cases, but I don't think ¯\_(ツ)_/¯ is valid code for the remaining 10%.

@ghost

This comment has been minimized.

Show comment
Hide comment
@ghost

ghost Oct 5, 2017

Guys, 3+ years later, I'd say it's time to say "fuck you" to the committee and do it our own way, whatever is idiomatic to TypeScript. This feature cannot work properly without static typing anyway.

Make a TypeScript implementation with a syntax that is explicitely incompatible with the TC39 proposition. Once ES gets a safe-nav operator, TypeScript will have two options: one with shitty semantics that's compatible with ES, and one that is tied to the type system. This means that you cannot use the TS safe-nav operator with the any "type", and that's just fine.

ghost commented Oct 5, 2017

Guys, 3+ years later, I'd say it's time to say "fuck you" to the committee and do it our own way, whatever is idiomatic to TypeScript. This feature cannot work properly without static typing anyway.

Make a TypeScript implementation with a syntax that is explicitely incompatible with the TC39 proposition. Once ES gets a safe-nav operator, TypeScript will have two options: one with shitty semantics that's compatible with ES, and one that is tied to the type system. This means that you cannot use the TS safe-nav operator with the any "type", and that's just fine.

@zlumer

This comment has been minimized.

Show comment
Hide comment
@zlumer

zlumer Oct 5, 2017

@notsnotso Typescript is not supposed to break JS, that's what Coffeescript is for.

zlumer commented Oct 5, 2017

@notsnotso Typescript is not supposed to break JS, that's what Coffeescript is for.

@goloveychuk

This comment has been minimized.

Show comment
Hide comment
@goloveychuk

goloveychuk Oct 5, 2017

Maybe good solution is to:

  • implement very simple ? operator for impatient devs, as experimental feature (like decorator), with warning - it WILL break your code in future
  • wait another 3 years when standart will be written, implement as not-experimental feature. Write tutorial, what could be broken. Even, with static typing, it's possible to write warnings when people will compile code with new operator implementation.
    "do it our own way, whatever is idiomatic to TypeScript" is not a case, because in 3 years people will face with problem, that ts elvis doesn't work same way as in js.

goloveychuk commented Oct 5, 2017

Maybe good solution is to:

  • implement very simple ? operator for impatient devs, as experimental feature (like decorator), with warning - it WILL break your code in future
  • wait another 3 years when standart will be written, implement as not-experimental feature. Write tutorial, what could be broken. Even, with static typing, it's possible to write warnings when people will compile code with new operator implementation.
    "do it our own way, whatever is idiomatic to TypeScript" is not a case, because in 3 years people will face with problem, that ts elvis doesn't work same way as in js.
@jehugaleahsa

This comment has been minimized.

Show comment
Hide comment
@jehugaleahsa

jehugaleahsa Oct 5, 2017

@ghost

This comment has been minimized.

Show comment
Hide comment
@ghost

ghost Oct 5, 2017

@zlumer nice joke. That said, a TS specific feature would not break Javascript.

ghost commented Oct 5, 2017

@zlumer nice joke. That said, a TS specific feature would not break Javascript.

@ghost

This comment has been minimized.

Show comment
Hide comment
@ghost

ghost Oct 5, 2017

@jehugaleahsa I like your proposal, and it's found in other languages. I think it would work just fine for TS.

ghost commented Oct 5, 2017

@jehugaleahsa I like your proposal, and it's found in other languages. I think it would work just fine for TS.

@MgSam

This comment has been minimized.

Show comment
Hide comment
@MgSam

MgSam Oct 5, 2017

I will say I don't really understand the strong need to wait until ECMAScript accepts the operator. TypeScript added classes, modules, lambdas, all long before they found their way into ECMAScript. In fact one of the stated goals of TypeScript was/is piloting experimental JS features. TypeScript providing its own implementation would no doubt help inform these discussions at the committee.

You guys have been willing to take breaking changes before when the committee eventually goes a different direction, I don't really see why this feature is so different.

MgSam commented Oct 5, 2017

I will say I don't really understand the strong need to wait until ECMAScript accepts the operator. TypeScript added classes, modules, lambdas, all long before they found their way into ECMAScript. In fact one of the stated goals of TypeScript was/is piloting experimental JS features. TypeScript providing its own implementation would no doubt help inform these discussions at the committee.

You guys have been willing to take breaking changes before when the committee eventually goes a different direction, I don't really see why this feature is so different.

@kgtkr

This comment has been minimized.

Show comment
Hide comment
@kgtkr

kgtkr Oct 5, 2017

async/await was also introduced to TypeScript at an early stage.

kgtkr commented Oct 5, 2017

async/await was also introduced to TypeScript at an early stage.

@trotyl

This comment has been minimized.

Show comment
Hide comment
@trotyl

trotyl Oct 5, 2017

That said, a TS specific feature would not break Javascript.

There won't be something like "TS specific feature", please check TypeScript Design Goals for more information.

TypeScript arrives before ES going to a bright path, there could be some existing ones,
they're just kept for compatibility.

trotyl commented Oct 5, 2017

That said, a TS specific feature would not break Javascript.

There won't be something like "TS specific feature", please check TypeScript Design Goals for more information.

TypeScript arrives before ES going to a bright path, there could be some existing ones,
they're just kept for compatibility.

@kitsonk

This comment has been minimized.

Show comment
Hide comment
@kitsonk

kitsonk Oct 5, 2017

Contributor

TypeScript added classes, modules, lambdas, all long before they found their way into ECMAScript.

And modules in particular are a huge debacle that still cause no end to confusion. That is an example that the TypeScript team have used repeatedly as an example of a mistake and jumping the gun too early. We also have private fields looming now, which again, I have been thankful for private for the past 5 years but it is going to cause no end to confusion.

Again, decorators have been available under the flag, but now that decorators are actually getting to Stage 3, it will require a re-implementation and code breakage for TypeScript. That is why these things cannot be taken likely.

async/await was also introduced to TypeScript at an early stage.

Once the ECMAScript proposal reached Stage 3.

Contributor

kitsonk commented Oct 5, 2017

TypeScript added classes, modules, lambdas, all long before they found their way into ECMAScript.

And modules in particular are a huge debacle that still cause no end to confusion. That is an example that the TypeScript team have used repeatedly as an example of a mistake and jumping the gun too early. We also have private fields looming now, which again, I have been thankful for private for the past 5 years but it is going to cause no end to confusion.

Again, decorators have been available under the flag, but now that decorators are actually getting to Stage 3, it will require a re-implementation and code breakage for TypeScript. That is why these things cannot be taken likely.

async/await was also introduced to TypeScript at an early stage.

Once the ECMAScript proposal reached Stage 3.

@Microsoft Microsoft locked and limited conversation to collaborators Oct 5, 2017

@RyanCavanaugh

This comment has been minimized.

Show comment
Hide comment
@RyanCavanaugh

RyanCavanaugh Oct 5, 2017

Member

No one's misbehaving but if this conversation can't go anywhere but in circles (what if there were a flag? Yes we are aware of flags) or off-topic (what if TS stops being JS? No five years later we are not changing our mind on that), we don't need to be having it. We've said for about 3 years now that we will implement this exactly when the ES committee locks down its semantics.

Once again, the proposal repo is https://github.com/tc39/proposal-optional-chaining and you can follow its progress there. We'll be working offline to try to improve the proposal's chances at the next TC39 meeting because we really want this to go through too.

Member

RyanCavanaugh commented Oct 5, 2017

No one's misbehaving but if this conversation can't go anywhere but in circles (what if there were a flag? Yes we are aware of flags) or off-topic (what if TS stops being JS? No five years later we are not changing our mind on that), we don't need to be having it. We've said for about 3 years now that we will implement this exactly when the ES committee locks down its semantics.

Once again, the proposal repo is https://github.com/tc39/proposal-optional-chaining and you can follow its progress there. We'll be working offline to try to improve the proposal's chances at the next TC39 meeting because we really want this to go through too.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.