(no title)
achou | 6 years ago
foo && await foo();
is not the same as await foo?.();
this will work in most cases but subtly, the await wraps the undefined case into a Promise, while the original code would skip the await altogether.String regular expression matching returns null, not undefined, so rewriting code such as:
const match = str.match(/reg(ex)/);
return match && match[1];
is not the same thing as: return match?.[1];
because the latter returns undefined, not null, in case of match failure. This can cause problems if subsequent code expects null for match failure. An equivalent rewrite would be: return match?.[1] ?? null;
which is longer than the original and arguably less clear.A common idiom to catch and ignore exceptions can interact poorly with optional chaining:
const v = await foo().catch(_ => {});
return v?.field; // property 'field' does not exist on type 'void'
This can be easily remedied by changing the first line to: const v = await foo().catch(_ => undefined);
Of course, these new operators are very welcome and will greatly simplify and help increase the safety of much existing code. But as in all things syntax, being judicious about usage of these operators is important to maximize clarity.
paulddraper|6 years ago
It's a shame JS at the beginning doubled down on the "billon dollar mistake" [1] with two(!) kinds of NULL instead of just using Maybe/Option.
Ah well, if it were good it wouldn't be popular :/
[1] https://www.lucidchart.com/techblog/2015/08/31/the-worst-mis...
jkoudys|6 years ago
Nullish alone had me back to the == instead of === once I went ts. No reason to care about identity when ts makes sure I don't compare a string to a number, but treating undefined == null as true is what I want 99.9% of the time, and that 0.1% I should be explicit that I do care about treating them differently.
vikingcaffiene|6 years ago
Speaking of which, optional chaining and null coalescence are core language features of some very good languages. Kotlin and C# for instance. Kotlin, much like TypeScript, interops with a "broken" language (Java and the JVM in its case) and attempts to address some core deficiencies in that ecosystem. Let us have nice things!! :)
I am really excited about these new features and hope they do land in JS sooner rather than later. I hope they do the pipe operator next! `pipe |> operator|> plz`
davnicwil|6 years ago
It can be useful to have the latter case distinguished in a dynamic language because it can enable certain powerful patterns. At the end of the day, compressing both these cases to a single concept of null would be lossy. This may have certain advantageous implications for simplicity, but you're trading that off for language power. Which you favour more of course depends on the usecase.
mattigames|6 years ago
You can even avoid the problem in the second one by using NonNullable TypeScript types, but I admit that's not common so its still likely to arise.
achou|6 years ago
In the last example, you're right that TypeScript will catch this at compile time. My point was to show how this compile time error can happen from refactoring to use optional chaining, and one easy solution in this case.