TypeScript & JavaScript · T300

Why did one question mark become more code?

Older TypeScript targets expand optional chaining and nullish coalescing into checks and temporary values. The saved value prevents a repeated call to getUser.

The important bit
Both targets return Guest for a missing user and call getUser once. An empty string stays empty with ??. Choose a target your runtimes support; raw emitted text is not a bundle or speed benchmark.

Understand it. Then fix it.

Older output. Same behavior.

Your target chooses which JavaScript syntax the compiler keeps. With an older target, TypeScript replaces newer operators with older code that does the same job. Target is an output setting, not a speed setting.

// target: ES2022
getUser()?.name ?? "Guest";

// target: ES2017
// Temporary values + null checks

Missing user? Stop the property read.

Question mark dot is optional chaining. If getUser returns null or undefined, it skips reading name and produces undefined. Double question mark then supplies Guest. Value trace, not executable code.

getUser() → null
null?.name → undefined
undefined ?? "Guest" → "Guest"

Save the value. Check it once received.

No. The compiler saves the result. This readable equivalent calls getUser once, checks that result, then checks the name. Null or undefined gets Guest. Readable equivalent · exact emitted code in the example.

const user = getUser();
const name = user === null ||
  user === undefined
    ? undefined : user.name;
const result = name === null ||
  name === undefined
    ? "Guest" : name;

An empty name is still a value.

An empty string stays empty. Double question mark only replaces null or undefined. Double vertical bar would also replace empty strings, zero and false. Do not replace ?? with || blindly.

"" ?? "Guest"  // ""
"" || "Guest"  // "Guest"

Choose the target your users can run.

Use a newer target when your supported browsers can run it. More generated text alone does not prove a larger final bundle or slower code.

Code blocks are teaching excerpts. Keep the surrounding error handling and application requirements.

Save the code excerpts ↓
Read the full transcript

Why did this tiny expression turn into several checks in my JavaScript? Your target chooses which JavaScript syntax the compiler keeps. With an older target, TypeScript replaces newer operators with older code that does the same job. Question mark dot is optional chaining. If getUser returns null or undefined, it skips reading name and produces undefined. Double question mark then supplies Guest. Do all those checks call getUser more than once? No. The compiler saves the result. This readable equivalent calls getUser once, checks that result, then checks the name. Null or undefined gets Guest. An empty string stays empty. Double question mark only replaces null or undefined. Double vertical bar would also replace empty strings, zero and false. Use a newer target when your supported browsers can run it. More generated text alone does not prove a larger final bundle or slower code. The question mark has more paperwork than I do.

Go to the source