Know your TypeScript/JavaScript operators… or why having little ceremony sometimes makes programmers life harder
Posted by jpluimers on 2026/05/12
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:
- What is the output of both log lines?
- 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:
- the
??null coalescing operator which returnspossiblyNullValue ?? valueIfNull - the
+addition operator which returnsleftToBeAdded + rightToBeAdded
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:
- Did you get them correctly?
- 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:
- The 2020 post [Wayback/Archive] Ceremony vs. Essence Revisited – DEV Community about HTML and JavaScript based web frameworks AppRun and Svelte pointing back to the 2010 discussion on [Wayback/Archive] Essence and ceremony, Ruby and C# – dave^2 = -1.
- The 2017 [Wayback/Archive] Venkat Subramaniam explains ceremony – YouTube 3 minute abstract (which I found via [Wayback/Archive] Zone of Ceremony by [Wayback/Archive] Mark Seemann (@ploeh) / Twitter.)
- The originator of the term [Wayback/Archive] Neal Ford (@neal4d) / Twitter on a 2009 video series based on his 2008 material:
- [Wayback/Archive] 4Developers Neal Ford – Essence & Ceremony Part 1 – YouTube: Introduction, History, Philosophy
- [Wayback/Archive] 4Developers Neal Ford – Essence & Ceremony Part 2 – YouTube: Net Producing Programmer, Antipatterns, Complexity, Ceremony Essence
- [Wayback/Archive] 4Developers Neal Ford – Essence & Ceremony Part 3 – YouTube: Intro, Enterprise Architecture 101, Demand for Software, The Problem
- [Wayback/Archive] 4Developers Neal Ford – Essence & Ceremony Part 4 – YouTube: Intro, Spring Hibernate, Software ubiquity, Service accidental complexity
- [Wayback/Archive] 4Developers Neal Ford – Essence & Ceremony Part 5 – YouTube: not all ceremony is bad
- [Wayback/Archive] 4Developers Neal Ford – Essence & Ceremony Part 6 – YouTube: efficacy over dogma, courage over status quo, essence over ceremony
(found via [Wayback/Archive] Essence vs. Ceremony | by Qooxdoo News | Qooxdoo News and [Wayback/Archive] Journal of a Programmer: Ceremony vs Essence which makes some good points and has pointers to related materials)
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 -- …typeoftypeof …voidvoid …deletedelete …awaitawait …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 … => …yieldn/a yield …yield*yield* …Spread (…) ... …1 Comma / Sequence left-to-right … , …Several notes about the table:
- 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.- The left operand of an exponentiation
**(precedence 13) cannot be one of the unary operators with precedence 14 without grouping, or there will be aSyntaxError. That means, although-1 ** 2is technically unambiguous, the language requires you to use(-1) ** 2instead.- 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) || cora ?? (b || c), instead ofa ?? b || c.- 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.- 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 thetypeofoperator 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 :extends,infer,keyof,typeof, etc?C
There’s the outdated spec which shows the grammar for types, which wouldn’t have
inferand 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
typeofis specifically only legal on “identifiers (i.e. variable names) or their properties” (docs). So in any situation wheretypeof a[b]is syntactically ambiguous,bmust be a literal value, in which case it is also a literal type and the result is the same regardless of operator precedence. Ifbis an identifier instead, then according to the docs, it cannot be parsed astypeof (a[b])becausetypeofis 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)
extendsandinferwhere you would need one?
Queries and other useful references
Queries
- [Wayback/Archive] “typescript” operator precedence – Google Search
- [Wayback/Archive] operator precedence site:https://github.com/microsoft/TypeScript – Google Search
- [Wayback/Archive] the ceremony part of programming languages – Google Search
- [Wayback/Archive] dotnet create solution – Google Search
- [Wayback/Archive] dotnet create project – Google Search
- [Wayback/Archive] java equivalent to “dotnet new” – Google Search
- [Wayback/Archive] java create project from command line – Google Search
- [Wayback/Archive] maven create project from command line – Google Search
- [Wayback/Archive] javascript create project from command line template – Google Search
References
- [Wayback/Archive]
dotnet slncommand – .NET CLI | Microsoft Learn - [Wayback/Archive] Maven – Maven in 5 Minutes
- [Wayback/Archive] Null coalesce has unintuitive precedence · Issue #36897 · microsoft/TypeScript (spoiler: it actually is in line with the JavaScript specifications)
- [Wayback/Archive] Precedence in parsing types · Issue #7094 · microsoft/TypeScript
- [Wayback/Archive] typeof Foo[‘bar’] has strange precedence · Issue #19707 · microsoft/TypeScript
- [Wayback/Archive] TypeScript: Documentation – Typeof Type Operator: limitations
My tweets:
- [Wayback/Archive] @laforge_toma Via …
[Wayback/Archive] Alt Text Utilities #SaveA11yBots on Twitter: “@jpluimers Extracted text in image descriptions”
- [Wayback/Archive] @laforge_toma It taught me about JavaScript operator precedence.
developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence - [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 - [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/
- [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/
- [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