The Wiert Corner – irregular stream of stuff

Jeroen W. Pluimers on .NET, C#, Delphi, databases, and personal interests

  • My badges

  • Twitter Updates

  • My Flickr Stream

  • Pages

  • All categories

  • Enter your email address to subscribe to this blog and receive notifications of new posts by email.

    Join 1,839 other subscribers

Know your TypeScript/JavaScript operators… or why having little ceremony sometimes makes programmers life harder

Posted by jpluimers on 2026/05/12

const a = undefined; const result1 = a ?? 0 + 10; const b = 100; const result2 = b ?? 0 + 10;Take this cool example I extended from [Wayback/Archive] Thomas 🅰️🇨🇵 on Twitter: “#Typescript quizz What will be the value of result1 and result2 ?” which lacked alt-badge, so I [Wayback/Archive] responded with the image on the right that has alt-text.

Based on that, I added a bit of logging:

const a = undefined;
const result1 = a ?? 0 + 10;

const b = 100;
const result2 = b ?? 0 + 10;

console.log(result1);
console.log(result2);

Two questions:

  1. What is the output of both log lines?

  1. What was your reasoning for both outputs?

While you are thinking about your answers

The construct used here consists of two JavaScript operators, so it is not even TypeScript specific, as many programming languages nowadays have these operators:

So far so good: two expressions with the same two operators.

You can run the example at [Wayback/Archive] TypeScript: TS Playground – An online editor for exploring TypeScript and JavaScript to check if your output estimates are correct.

Then ask yourself two more questions:

  1. Did you get them correctly?
  2. If so: why do you think someone would not get it correctly?

Intermezzo: ceremony versus essence in code

A while ago, [Wayback/Archive] Stefan Glienke taught the term ceremony (and related essence) to me with respect to Delphi code. Later I found out that many people used it. Some examples going back to the origin:

In the video, [Wayback/Archive] Venkat Subramaniam 🇺🇦 ✌️ (@venkat_s) argues that ceremony in programming languages is a bad thing. It often is, as for simple code, especially simple example programs, the ceremony adds a lot of overhead. It’s like seeing the trees through the forest: the actual meaningful code is hard to see and understand.

“Ceremony is what you have to do before you get to do what you really want to do.”

Venkat Subramaniam

IDEs and frameworks often try to generate ceremony for you, so it does not get in the way as much as starting typing all by hand. Examples like this are Delphi, .NET (not just using Visual Studio like mentioned in Essence and ceremony, Ruby and C# – dave^2 = -1, but also [Wayback/Archive] dotnet new <TEMPLATE> – .NET CLI | Microsoft Learn), Java (through Maven: [Wayback/Archive] Maven – Introduction to Archetypes mvn archetype:generate), JavaScript (for instance through [Wayback/Archive] Create JavaScript Project · Set up a JavaScript project by running one command. create-js-project [project-name]) and others.

Programming languages try to get rid of ceremony in several ways, one of which is introducing language constructs that make ceremony go away in full or in part. The result is ever denser code which requires ever more knowledge to understand.

In that sense, ceremony can be a good thing too as it makes it easier for more novice programmers to maintain an existing codebase.

The ?? null coalescing operator is one of such constructs, as it basically reduces an if statement where the then and else clause both assign the same variable. However in the above case the assignment isn’t to a variable, but to a constant:

const result1 = a ?? 0 + 10;

This you cannot do with an if/then/else construct so knowing how to use the the ?? null coalescing operator is the only way to assign the constant at all.

Which brings us to the topic of understanding the ?? null coalescing operator in relation to other operators.

Operator precedence

The hardest thing with dense code containing less used operators is to remember the order of operations or operator precedence.

A different case with typeof is referenced further below.

In this case, many people wrongly think the above code will have the precedence enforced by these parentheses:

const a = undefined;
const result1 = (a ?? 0) + 10;

const b = 100;
const result2 = (b ?? 0) + 10;

console.log(result1);
console.log(result2);

In fact, the order of operations is this:

const a = undefined;
const result1 = a ?? (0 + 10);

const b = 100;
const result2 = b ?? (0 + 10);

console.log(result1);
console.log(result2);

A similar precedence holds for almost any other language: math operators are somewhere in the middle of the operator precedence list.

So for any language you use, be aware of documentation like in [Wayback/Archive] Operator precedence: table – JavaScript | MDN

Precedence Operator type Associativity Individual operators
18 Grouping n/a ( … )
17 Member Access left-to-right … . …
Optional chaining … ?. …
Computed Member Access n/a … [ … ]
new (with argument list) new … ( … )
Function Call … ( … )
16 new (without argument list) n/a new …
15 Postfix Increment n/a … ++
Postfix Decrement … --
14 Logical NOT (!) n/a ! …
Bitwise NOT (~) ~ …
Unary plus (+) + …
Unary negation (-) - …
Prefix Increment ++ …
Prefix Decrement -- …
typeof typeof …
void void …
delete delete …
await await …
13 Exponentiation (**) right-to-left … ** …
12 Multiplication (*) left-to-right … * …
Division (/) … / …
Remainder (%) … % …
11 Addition (+) left-to-right … + …
Subtraction (-) … - …
10 Bitwise Left Shift (<<) left-to-right … << …
Bitwise Right Shift (>>) … >> …
Bitwise Unsigned Right Shift (>>>) … >>> …
9 Less Than (<) left-to-right … < …
Less Than Or Equal (<=) … <= …
Greater Than (>) … > …
Greater Than Or Equal (>=) … >= …
in … in …
instanceof … instanceof …
8 Equality (==) left-to-right … == …
Inequality (!=) … != …
Strict Equality (===) … === …
Strict Inequality (!==) … !== …
7 Bitwise AND (&) left-to-right … & …
6 Bitwise XOR (^) left-to-right … ^ …
5 Bitwise OR (|) left-to-right … | …
4 Logical AND (&&) left-to-right … && …
3 Logical OR (||) left-to-right … || …
Nullish coalescing operator (??) … ?? …
2 Assignment right-to-left … = …
… += …
… -= …
… **= …
… *= …
… /= …
… %= …
… <<= …
… >>= …
… >>>= …
… &= …
… ^= …
… |= …
… &&= …
… ||= …
… ??= …
Conditional (ternary) operator right-to-left
(Groups on expressions after ?)
… ? … : …
Arrow (=>) right-to-left … => …
yield n/a yield …
yield* yield* …
Spread (…) ... …
1 Comma / Sequence left-to-right … , …

Several notes about the table:

  1. Not all syntax included here are “operators” in the strict sense. For example, spread ... and arrow => are typically not regarded as operators. However, we still included them to show how tightly they bind compared to other operators/expressions.
  2. The left operand of an exponentiation ** (precedence 13) cannot be one of the unary operators with precedence 14 without grouping, or there will be a SyntaxError. That means, although -1 ** 2 is technically unambiguous, the language requires you to use (-1) ** 2 instead.
  3. The operands of nullish coalescing ?? (precedence 3) cannot be a logical OR || (precedence 3) or logical AND && (precedence 4). That means you have to write (a ?? b) || c or a ?? (b || c), instead of a ?? b || c.
  4. Some operators have certain operands that require expressions narrower than those produced by higher-precedence operators. For example, the right-hand side of member access . (precedence 17) must be an identifier instead of a grouped expression. The left-hand side of arrow => (precedence 2) must be an arguments list or a single identifier instead of some random expression.
  5. Some operators have certain operands that accept expressions wider than those produced by higher-precedence operators. For example, the bracket-enclosed expression of bracket notation [ … ] (precedence 17) can be any expression, even comma (precedence 1) joined ones. These operators act as if that operand is “automatically grouped”. In this case we will omit the associativity.

The typeof operator has high precedence

Quoting in full, because important, from [Wayback/Archive] What’s the precedence of TypeScript operators? – Stack Overflow (thanks [Wayback/Archive] Guerric P, [Wayback/Archive] jcalz and [Wayback/Archive] kaya3):

Q

When writing complex types in TypeScript, the different operators have a certain precedence.
For example, this code:
typeof map[T]
Intuitively, we could think that it is equivalent of:
typeof (map[T])
But it’s actually equivalent to:
(typeof map)[T]
Which means that the typeof operator has higher precedence than the square brackets, but I can’t find any documentation about it.
Is there a precedence order table with all the keywords : extendsinferkeyoftypeof, etc?

C

There’s the outdated spec which shows the grammar for types, which wouldn’t have infer and doesn’t explicitly spell out the precedence in human-friendly order; there’s an open feature request at ms/TS#7094 to put this in the docs, but it’s in the backlog so I doubt it will happen anytime soon. And people might even change it ms/TS#19707.

I could put this into an answer, which is the closest to an “authoritative” answer I can think of. Otherwise it’s just someone experimenting with expressions to empirically determine the precedence, or looking through the type checker code. Pragmatically, ambiguity should be resolved through parentheses and the actual order is interesting but not of vital importance (in my opinion). Maybe someone else will come along and go through the exercise of determining the actual rules

C

typeof is specifically only legal on “identifiers (i.e. variable names) or their properties” (docs). So in any situation where typeof a[b] is syntactically ambiguous, b must be a literal value, in which case it is also a literal type and the result is the same regardless of operator precedence. If b is an identifier instead, then according to the docs, it cannot be parsed as typeof (a[b]) because typeof is not legal on that kind of expression.

Other than that, I cannot think of any situation where multiple Typescript type operators could be syntactically ambiguous – even if there is no precedence order table, can you give any example involving (say) extends and infer where you would need one?

Queries and other useful references

Queries

References

My tweets:

  1. [Wayback/Archive] @laforge_toma Via …

    [Wayback/Archive] Alt Text Utilities #SaveA11yBots on Twitter: “@jpluimers Extracted text in image descriptions”

    1 2 3 const a = undefined; const result1 = a ?? 0 + 10; 4 const b = 100; 5 const result2 = b ?? 0 + 10; Thomas Laforge

  2. [Wayback/Archive] @laforge_toma It taught me about JavaScript operator precedence. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence
  3. [Wayback/Archive] @laforge_toma TypeScript playground to show that operator + has higher precedence than ?? typescriptlang.org/play?ts=3.9.0-dev.20200219#code/MYewdgzgLgBAhjAvDArmAJgUwGYEsyboDcAUKJLAE6YQoA2UAjEvDAPxswAMMA1DIy6ky4aDABGLQUJEUY1WgwBMLSR258BM2RBB1MAOjogA5gAoF9JgEpS5XfqOmLNK0ttA
  4. [Wayback/Archive] @laforge_toma I gave this some more thought. It reminded me of the “Essence & Ceremony” series of videos by @neal4d. What you see here is the ever evolving of language features to try helping focus on essence by providing alternatives for ceremony 1/
  5. [Wayback/Archive] @laforge_toma @neal4d while at the same time allowing constructs where you cannot go back to the ceremony. Essence and ceremony form a delicate balance where you really need to think about who is going to maintain the code base: many language features with little ceremony requires more knowledge. 2/
  6. [Wayback/Archive] @laforge_toma @neal4d This is a serious issue to think about when developing software with many older programmers retiring or quitting and a decreasing number of people entering the field of software development. 3/3

–jeroen

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.