description |
---|
Enforce using concise optional chain expressions instead of chained logical ands, negated logical ors, or empty objects. |
🛑 This file is source code, not the primary documentation location! 🛑
See https://typescript-eslint.io/rules/prefer-optional-chain for documentation.
?.
optional chain expressions provide undefined
if an object is null
or undefined
.
Because the optional chain operator only chains when the property value is null
or undefined
, it is much safer than relying upon logical AND operator chaining &&
; which chains on any truthy value.
It is also often less code to use ?.
optional chaining than &&
truthiness checks.
This rule reports on code where an &&
operator can be safely replaced with ?.
optional chaining.
foo && foo.a && foo.a.b && foo.a.b.c;
foo && foo['a'] && foo['a'].b && foo['a'].b.c;
foo && foo.a && foo.a.b && foo.a.b.method && foo.a.b.method();
// With empty objects
(((foo || {}).a || {}).b || {}).c;
(((foo || {})['a'] || {}).b || {}).c;
// With negated `or`s
!foo || !foo.bar;
!foo || !foo[bar];
!foo || !foo.bar || !foo.bar.baz || !foo.bar.baz();
// this rule also supports converting chained strict nullish checks:
foo &&
foo.a != null &&
foo.a.b !== null &&
foo.a.b.c != undefined &&
foo.a.b.c.d !== undefined &&
foo.a.b.c.d.e;
foo?.a?.b?.c;
foo?.['a']?.b?.c;
foo?.a?.b?.method?.();
foo?.a?.b?.c?.d?.e;
!foo?.bar;
!foo?.[bar];
!foo?.bar?.baz?.();
In the context of the descriptions below a "loose boolean" operand is any operand that implicitly coerces the value to a boolean.
Specifically the argument of the not operator (!loose
) or a bare value in a logical expression (loose && looser
).
When this option is true
, the rule will not provide an auto-fixer for cases where the return type of the expression would change. For example for the expression !foo || foo.bar
the return type of the expression is true | T
, however for the equivalent optional chain foo?.bar
the return type of the expression is undefined | T
. Thus changing the code from a logical expression to an optional chain expression has altered the type of the expression.
In some cases this distinction may matter - which is why these fixers are considered unsafe - they may break the build! For example in the following code:
declare const foo: { bar: boolean } | null | undefined;
declare function acceptsBoolean(arg: boolean): void;
// ✅ typechecks succesfully as the expression only returns `boolean`
acceptsBoolean(foo != null && foo.bar);
// ❌ typechecks UNSUCCESSFULLY as the expression returns `boolean | undefined`
acceptsBoolean(foo?.bar);
This style of code isn't super common - which means having this option set to true
should be safe in most codebases. However we default it to false
due to its unsafe nature. We have provided this option for convenience because it increases the autofix cases covered by the rule. If you set option to true
the onus is entirely on you and your team to ensure that each fix is correct and safe and that it does not break the build.
When this option is false
unsafe cases will have suggestion fixers provided instead of auto-fixers - meaning you can manually apply the fix using your IDE tooling.
When this option is true
the rule will check operands that are typed as any
when inspecting "loose boolean" operands.
declare const thing: any;
thing && thing.toString();
declare const thing: any;
thing && thing.toString();
When this option is true
the rule will check operands that are typed as unknown
when inspecting "loose boolean" operands.
declare const thing: unknown;
thing && thing.toString();
declare const thing: unknown;
thing && thing.toString();
When this option is true
the rule will check operands that are typed as string
when inspecting "loose boolean" operands.
declare const thing: string;
thing && thing.toString();
declare const thing: string;
thing && thing.toString();
When this option is true
the rule will check operands that are typed as number
when inspecting "loose boolean" operands.
declare const thing: number;
thing && thing.toString();
declare const thing: number;
thing && thing.toString();
When this option is true
the rule will check operands that are typed as boolean
when inspecting "loose boolean" operands.
declare const thing: boolean;
thing && thing.toString();
declare const thing: boolean;
thing && thing.toString();
When this option is true
the rule will check operands that are typed as bigint
when inspecting "loose boolean" operands.
declare const thing: bigint;
thing && thing.toString();
declare const thing: bigint;
thing && thing.toString();
When this option is true
the rule will skip operands that are not typed with null
and/or undefined
when inspecting "loose boolean" operands.
declare const thing1: string | null;
thing1 && thing1.toString();
declare const thing1: string | null;
thing1?.toString();
declare const thing2: string;
thing2 && thing2.toString();
If you don't mind using more explicit &&
s/||
s, you don't need this rule.