question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Nullish coalescing should always include the type of the right operand

See original GitHub issue
$ ./node_modules/.bin/tsc --version
Version 3.7.2

Search Terms:

Code

A toy example would be something like this.

let foo: string = "";
foo = "bar" ?? 123;

This one is obviously fine since "bar" is always truthy.

However, this becomes a little bit problematic when you consider the idiom of having Record objects and checking their truthiness before using them.

const elts = ["foo", "bar", "spam", "spam", "foo", "eggs"];
const counts: Record<string, number>;
for (const elt of elts) {
  // This really **should** raise an error.
  counts[elt] = counts[elt] ?? "zero";
  counts[elt] += 1;
}

Expected behavior: An error should be raised.

Actual behavior: Curiously, an error is not raised in strict mode but is raised in un-strict mode.

$ ./node_modules/.bin/tsc ./foo.ts 
foo.ts:5:3 - error TS2322: Type 'number | "zero"' is not assignable to type 'number'.
  Type '"zero"' is not assignable to type 'number'.

5   counts[elt] = counts[elt] ?? "zero";
    ~~~~~~~~~~~


Found 1 error.
$ ./node_modules/.bin/tsc --strict ./foo.ts
# No error, exits 0 and emits JS.

Playground Link: Playground Link

Toggling the strictNullChecks config option will show the issue.

Related Issues:

Issue Analytics

  • State:open
  • Created 4 years ago
  • Reactions:3
  • Comments:5 (2 by maintainers)

github_iconTop GitHub Comments

2reactions
jcalzcommented, Jan 24, 2020

This is probably related to or a duplicate of #13778.

With --strictNullChecks on, the compiler assumes that counts[elt] must be of type number, which is never null or undefined. If you want the compiler to notice that counts[elt] may be undefined, then you (currently) need to include undefined in the property type manually:

const counts: Record<string, number | undefined> = {};
for (const elt of elts) {
  counts[elt] = (counts[elt] ?? "zero"); // error now
  counts[elt] = (counts[elt] ?? 0) + 1; // okay
}

Playground

With --strictNullChecks off, the compiler never assumes nullish coalescing will short-circuit because it can never eliminate null and undefined. See this comment from the PR

0reactions
travigdcommented, Sep 30, 2020

I respectfully disagree (obviously - I created the issue 😉). Using too-loose Record types is a common idiom in TypeScript, and I think changing this behavior makes that idiom easier to work with.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Nullish coalescing operator '??' - The Modern JavaScript Tutorial
The nullish coalescing operator ?? provides a short way to choose the first “defined” value from a list. It's used to assign default...
Read more >
Nullish coalescing operator (??) - JavaScript - MDN Web Docs
The nullish coalescing (??) operator is a logical operator that returns its right-hand side operand when its left-hand side operand is null ...
Read more >
Nullish Coalescing Operator (??) In JavaScript - What Is It And ...
According to MDN, the nullish coalescing is "a logical operator that returns its right-hand side operand when its left-hand side operand is ...
Read more >
The Ultimate Guide to the ES2020 Nullish Coalescing Operator
If the left-hand operand of the && operator evaluates to false , the JavaScript interpreter is smart enough to know that the result...
Read more >
Optional chaining and nullish coalescing in TypeScript
TypeScript's type system defines two very special types, null and undefined , to represent the values null and undefined , respectively. However ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found