-
Notifications
You must be signed in to change notification settings - Fork 12.3k
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
Suggestion: "safe navigation operator", i.e. x?.y #16
Comments
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. |
You also really can't do For example:
gives So you need to do some explicit comparisons to 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. |
there has been few discussions in esdiscuss about that : |
👍 |
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 |
👍 I'd like to see TypeScript gets this in first without having to wait for ESxx. |
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). |
I totally agree with brain428 |
@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. |
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). |
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. |
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. |
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. |
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 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. |
The codeplex issue had quite a number of votes (61) I really badly need this to ease the pain of using It is very idiomatic in coffescript code (although I would like it not to be as popular as determinism is better than a fudgy Again I don't like that I need this, but its the state of JavaScript, and I'd rather 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. |
Okay now I have read the thread and see why we are waiting. I understand sigh. |
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 |
+1 |
In news today, Dart is getting official support for it : https://github.com/gbracha/nullAwareOperators/blob/master/proposal.md |
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:
Could compile to
A
Could compile to
This would also keep the generated JS reasonably idiomatic, even for long chains. Needs refinement obviously ... but maybe there is something here? |
Except that (_prop = prop) === null || _prop === void 0 ? void 0 : _prop./* do stuff */; |
I'm aware of that. Babel doesn't have a type system, TypeScript does. Perhaps it's not as simple as I made it sound, but I imagine there's already code for certain situations that is capable of tracking usage. |
Actually, you don't need a type system to track So you should just be able to do an |
Yeah but... why'd you do |
For sure - I was just pointing out you don't need a type system to track Personally I'm tempted to say just break it and see who complains, but it's in the spec, so easiest to just stick with that. |
@jhpratt But that currently goes against the TypeScript Design Non‑Goals. Also, you’d still have to do |
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 To be fair, TS has rejected a potential minifier that uses type information, and this is (sort of) related — the primary reason to emit 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 |
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! |
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 |
@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) |
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) |
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. document.querySelector('html')?.setAttribute('lang', 'en'); VS Currently in TypeScript 3.5. const htmlElement = document.querySelector('html');
if (htmlElement) {
htmlElement.setAttribute('lang', 'en');
} Will this work without any errors? Or is this still a class Test {
it() {
console.log('One');
document.querySelector('html')?.setAttribute('lang', 'en');
console.log('Two');
}
}
new Test().it(); I expect following: |
@domske FYI, this isn't strictly a TS feature; it's a JS feature. According to the TC39 proposal the syntax will be: document.querySelector('html')?.setAttribute?.('lang', 'en'); |
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! |
Current Status
Open questions
document.all
get?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.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.
The text was updated successfully, but these errors were encountered: