Repository navigation
Suggestion: "safe navigation operator", i.e. x?.y #16
Copy link
Copy link
Closed
Labels
CommittedThe team has roadmapped this issueThe team has roadmapped this issueES NextNew featurers for ECMAScript (a.k.a. ESNext)New featurers for ECMAScript (a.k.a. ESNext)SuggestionAn idea for TypeScriptAn idea for TypeScriptUpdate Docs on Next ReleaseIndicates that this PR affects docsIndicates that this PR affects docs
Milestone
Description
Current Status
- The TC39 proposal is now at stage 3 (🎉🎉🎉🎉🎉)
- Implementation is in progress
- You can expect this feature in TypeScript 3.7
- We'll update here when it's available in a nightly build
- Holding off on Optional Call until its semantics are clarified at committee
Open questions
- What special-casing, if any, should
document.allget?
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 Daniel Rosenwasser (@DanielRosenwasser) February 27, 2018: This proposal is also called the "null propagation" operator.
Reacted by Anders D. Johnson, Pulkit Goyal, Sergey Svekolnikov, eaigner, Michael Rätzel, mavericken, Dylan Robinson, Joseph Musser, Hunter Gatewood, Juan Sepulveda and 738 more
Reacted by mind0n, Terrance Smith, kgtkr, Patrick Shaw, iygnay, Alan Johnson, Charl, Paul Dilley, Francois Wouts, Derek Dowling and 27 more
Reacted by Saeed Tabrizi, Eros Stein, iygnay, Daniel Prado, ELI JOSEPH BRADLEY, Gudjon, Alex Lockwood, Michael Sanford, Chet Corcos, Jonathan and 63 more
Reacted by eaigner, Dylan Robinson, Mathias Raacke, Hunter Gatewood, hulucc, Frederick Fogerty, Tony Trinh, Vo Thanh Dat, Santiago Aguilar Hernández, Matt Turnbull and 202 more
Reacted by ExE Boss, Derek Dowling, Joshua O'Brien, Mike George, isbasex, Ilia Sidorenko, Igor Pavlichenko, Joe Calzaretta, marko164, Jeff Rooks and 18 more
Reacted by Mike George, isbasex, Ilia Sidorenko, Igor Pavlichenko, Joe Calzaretta, marko164, Jeff Rooks, stephen, Neil Bryson, Henry Zelenka and 8 more
Metadata
Metadata
Assignees
Labels
CommittedThe team has roadmapped this issueThe team has roadmapped this issueES NextNew featurers for ECMAScript (a.k.a. ESNext)New featurers for ECMAScript (a.k.a. ESNext)SuggestionAn idea for TypeScriptAn idea for TypeScriptUpdate Docs on Next ReleaseIndicates that this PR affects docsIndicates that this PR affects docs
Activity
JsonFreeman commentedon 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 commentedon Jul 15, 2014
You also really can't do
&&in many cases because truthiness becomes a bit of a problem for primitives.For example:
gives
"".So you need to do some explicit comparisons to
nullusing==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.
fdecampredon commentedon Jul 22, 2014
there has been few discussions in esdiscuss about that :
philipbulley commentedon Sep 1, 2014
👍
RyanCavanaugh commentedon 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 commentedon Sep 11, 2014
👍 I'd like to see TypeScript gets this in first without having to wait for ESxx.
brian428 commentedon 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 commentedon Oct 1, 2014
I totally agree with brain428
fdecampredon commentedon Oct 2, 2014
Brian Kotek (@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 commentedon Oct 2, 2014
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:
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 commentedon Oct 2, 2014
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 commentedon 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.
k4b7 commentedon 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.
200 remaining items
jhpratt commentedon Jul 27, 2019
Oskari Noppa (@noppa) It can be performed at compile time. If
foo instanceof HTMLAllCollectionistrue, emitfoo === null || foo === void 0, otherwiise we can safely emitfoo == null.Gareth Jones (@G-Rath) Like it or not, deprecated doesn't mean it shouldn't work. TypeScript should remain compatible with JavaScript.
ExE-Boss commentedon Jul 27, 2019
Jacob Pratt (@jhpratt) But that currently goes against the TypeScript Design Non‑Goals.
Also, you’d still have to do
foo === null || foo === void 0for anything to whichHTMLAllCollectioncould be assigned, eg.anyorobject, so I don’t think it’s really worth it.jhpratt commentedon Jul 27, 2019
I presume you're referring to non-goal (5)
Though I agree this would certainly emit different code based on the type, it's only to reduce code size. Though as you've pointed out, it's not quite as simple as checking for
HTMLAllCollection.To be fair, TS has rejected a potential minifier that uses type information, and this is (sort of) related — the primary reason to emit
== nullis to reduce code size based on type information.If this isn't implemented, it would be great if the lang team adds an option to tsconfig similar to Babel's "loose" option.
After quickly checking, terser automatically converts
foo === null || foo === undefinedtofoo == null, which isn't safe due to this edge case.fbartho commentedon Jul 28, 2019
Relatedly, many of us use TypeScript for build tools, and for mobile applications, none of which have to worry about browser constraints!
In fact, we use both TS & Babel together, so maybe one of these options should be to passthrough the operator to Babel/underlying runtime!
Zarel commentedon Aug 12, 2019
Frederic Barthelemy (@fbartho)
I don't understand this comment. You don't need any extra options to passthrough the operator to Babel; if you have TypeScript set up for Babel, you already have
noEmit: truewhich already passes everything through to Babel.fbartho commentedon Aug 12, 2019
Guangcong Luo (@Zarel) Babel’s TypeScript implementation is missing several features that our codebase was already relying on, including namespaces and const enums. We’re using TSC with emit enabled, and applying Babel as a second transformation. (We’re working on getting rid of the namespaces, but it’s unclear if we’ll ever be able to get rid of all of the mismatched features)
RyanCavanaugh commentedon Aug 19, 2019
People coming to this thread should start at the earlier stage 3 announcement and read the comments starting there (blame GitHub for hiding tons of user content with no straightforward way to load everything)
domske commentedon Aug 24, 2019
Great feature - "optional chaining" / "safe navigation". Especially in TypeScript strict-mode. Awesome to hear that this will be implemented soon. ❤️
This brought me here and I hope it will be supported. Just an use case:
Expected in TypeScript 3.7.
VS
Currently in TypeScript 3.5.
Will this work without any errors? Or is this still a
TypeError: Cannot read property 'setAttribute' of null.? The?op. should be cancel further chains after null / undefined.I expect following:
If html element does not exist (null). The console should logged
OneandTwo, and thesetAttributemethod is not tried to invoked. (No errors).Did I understand that correctly?
joehillen commentedon Aug 24, 2019
domske FYI, this isn't strictly a TS feature; it's a JS feature.
According to the TC39 proposal the syntax will be:
RyanCavanaugh commentedon Aug 24, 2019
The discussion has started to go circular again so we're back to locked state.
I truly beg anyone tempted to leave a comment in a 100+-comment long GitHub thread to really commit to reading all the prior comments first. Probably your question, and the answer to it, will be found there!