@typescript-eslint/prefer-nullish-coalescing
Require ?? instead of || or a ternary to give a default for null or undefined
Also called typescript-eslint/prefer-nullish-coalescing, typescript/prefer-nullish-coalescing
port || 3000 falls back to 3000 for every falsy value, so a port of 0 is replaced, and so are "" and false. port ?? 3000 only falls back when port is null or undefined.
|| and ||= are reported when the left side's type includes null or undefined. A ternary or an if that does the job of ?? or ??= by hand is reported too.
--fix doesn't change these. Look at each one before you change it, because ?? keeps 0, "", and false where || replaced them.
This rule needs strictNullChecks (or strict) in your tsconfig.json.
Configure#
Off by default. This rule needs types, so turning it on makes bun lint type check your code first.
[lint.rules]
"@typescript-eslint/prefer-nullish-coalescing" = "error" # or "warn"Every option, set to its default:
[lint.rules]
"@typescript-eslint/prefer-nullish-coalescing" = ["error", {
ignoreConditionalTests = true,
ignoreTernaryTests = false,
ignoreIfStatements = false,
ignoreMixedLogicalExpressions = false,
ignoreBooleanCoercion = false,
ignorePrimitives = {
bigint = false,
boolean = false,
number = false,
string = false,
},
allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing = false,
}]Examples#
function connect(options: { port?: number; host: string | null; name?: string; tags?: string[] }) {
const port = options.port ||Prefer using nullish coalescing operator (??) instead of a logical or (||), as it is a safer operator. 3000;
const host = options.host !== null ? options.host : "localhost"Prefer using nullish coalescing operator (??) instead of a ternary expression, as it is simpler to read.;
options.name ||=Prefer using nullish coalescing operator (??=) instead of a logical assignment (||=), as it is a safer operator. "default";
if (!options.tags) {Prefer using nullish coalescing operator (??=) instead of an assignment expression, as it is simpler to read.
options.tags = [];
}
}function connect(options: { port?: number; host: string | null; name?: string; tags?: string[] }) {
const port = options.port ?? 3000;
const host = options.host ?? "localhost";
options.name ??= "default";
options.tags ??= [];
}
function label(text: string) {
return text || "untitled"; // never null or undefined
}Options#
| Option | Default | What it does |
|---|---|---|
ignoreConditionalTests | true | Allow || in the test of an if, a loop, or a ternary, where only true or false matters. |
ignoreTernaryTests | false | Don't report ternaries. |
ignoreIfStatements | false | Don't report if statements. |
ignoreMixedLogicalExpressions | false | Allow || in an expression that also has &&. |
ignoreBooleanCoercion | false | Allow || inside a Boolean(...) call. |
ignorePrimitives | {} | Allow || when the left side can be one of these: { string, number, boolean, bigint }, each true or false. true means all four. |
allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing | false | Without strictNullChecks, the rule can't tell what can be null, and it reports that once for each file. Set this to true to turn that report off. |
The test of an if is allowed by default:
function show(title: string | undefined, subtitle: string) {
if (title || subtitle) {
console.log(title, subtitle);
}
}With ignoreConditionalTests: false it's reported:
{ "ignoreConditionalTests": false }1 problemfunction show(title: string | undefined, subtitle: string) {
if (title ||Prefer using nullish coalescing operator (??) instead of a logical or (||), as it is a safer operator. subtitle) {
console.log(title, subtitle);
}
}Use ignorePrimitives: { string: true } if you want an empty string to get the default too:
{ "ignorePrimitives": { "string": true } }No problemsfunction greet(name: string | undefined) {
return "Hello, " + (name || "stranger");
}A number is still reported with that setting:
{ "ignorePrimitives": { "string": true } }1 problemfunction retries(count: number | undefined) {
return count ||Prefer using nullish coalescing operator (??) instead of a logical or (||), as it is a safer operator. 3;
}ignoreMixedLogicalExpressions allows the first line, and ignoreBooleanCoercion allows the second:
{ "ignoreMixedLogicalExpressions": true, "ignoreBooleanCoercion": true }No problemsfunction ready(user: { name: string } | null, guest: { name: string } | null, open: boolean) {
const allowed = user || (guest && open);
return Boolean(user || guest) && allowed;
}