top | item 21458662

(no title)

achou | 6 years ago

I just did some refactoring on a medium size code base and here are a few things to watch out for when adopting optional chaining and the new null coalescing operator:

  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.

discuss

order

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

Going between rust and ts, it's the judicious use of enums that js really feels like it's missing. The whole hoopla with undefined/null/NaN/etc could be avoided with a simple enum type. Not to mention the entire concept of Exceptions.

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

Sorry, JS? While JS might get this stuff one day, these language features are for TypeScript which is its own language. It's strongly typed and just happens to interop with and in some scenarios transpile down to JavaScript. It's whole existence is to deal with that billion dollar mistake you mentioned.

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

I don't see it as two kinds of null, there is a null value, and then there is the fact that no value has ever been defined, which is undefined.

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 have to watch out for first and last one in JavaScript but not on TypeScript as it isn't possible to make that mistake because you have to type it as a promise or in the last one as void.

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

The first example can happen in TypeScript; foo has type

  (() => Promise<void>) | undefined
admittedly it may not be all that common to have a function-valued variable that may be undefined, but it happened in the code base I was working with.

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.