Skip to content

Commit 83c3bbf

Browse files
authored
fix(eslint-plugin): [no-unnecessary-condition] handle noUncheckedIndexedAccess true (typescript-eslint#10514)
* fix(eslint-plugin): [no-unnecessary-condition] handle index signature * fix * refactor
1 parent c6a387f commit 83c3bbf

File tree

2 files changed

+66
-20
lines changed

2 files changed

+66
-20
lines changed

packages/eslint-plugin/src/rules/no-unnecessary-condition.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,10 @@ export default createRule<Options, MessageId>({
273273
compilerOptions,
274274
'strictNullChecks',
275275
);
276+
const isNoUncheckedIndexedAccess = tsutils.isCompilerOptionEnabled(
277+
compilerOptions,
278+
'noUncheckedIndexedAccess',
279+
);
276280

277281
if (
278282
!isStrictNullChecks &&
@@ -787,11 +791,15 @@ export default createRule<Options, MessageId>({
787791
}
788792
const indexInfo = checker.getIndexInfosOfType(type);
789793

790-
return indexInfo.some(
791-
info =>
792-
getTypeName(checker, info.keyType) === 'string' &&
793-
isNullableType(info.type),
794-
);
794+
return indexInfo.some(info => {
795+
const isStringTypeName =
796+
getTypeName(checker, info.keyType) === 'string';
797+
798+
return (
799+
isStringTypeName &&
800+
(isNoUncheckedIndexedAccess || isNullableType(info.type))
801+
);
802+
});
795803
});
796804
return !isOwnNullable && isNullableType(prevType);
797805
}

packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts

Lines changed: 53 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ const optionsWithExactOptionalPropertyTypes = {
2727
tsconfigRootDir: rootPath,
2828
};
2929

30+
const optionsWithNoUncheckedIndexedAccess = {
31+
project: './tsconfig.noUncheckedIndexedAccess.json',
32+
projectService: false,
33+
tsconfigRootDir: getFixturesRootDir(),
34+
};
35+
3036
const necessaryConditionTest = (condition: string): string => `
3137
declare const b1: ${condition};
3238
declare const b2: boolean;
@@ -607,11 +613,7 @@ const key = '1' as BrandedKey;
607613
foo?.[key]?.trim();
608614
`,
609615
languageOptions: {
610-
parserOptions: {
611-
project: './tsconfig.noUncheckedIndexedAccess.json',
612-
projectService: false,
613-
tsconfigRootDir: getFixturesRootDir(),
614-
},
616+
parserOptions: optionsWithNoUncheckedIndexedAccess,
615617
},
616618
},
617619
{
@@ -665,11 +667,7 @@ function Foo(outer: Outer, key: Foo): number | undefined {
665667
}
666668
`,
667669
languageOptions: {
668-
parserOptions: {
669-
project: './tsconfig.noUncheckedIndexedAccess.json',
670-
projectService: false,
671-
tsconfigRootDir: getFixturesRootDir(),
672-
},
670+
parserOptions: optionsWithNoUncheckedIndexedAccess,
673671
},
674672
},
675673
{
@@ -682,11 +680,51 @@ declare const key: Key;
682680
foo?.[key]?.trim();
683681
`,
684682
languageOptions: {
685-
parserOptions: {
686-
project: './tsconfig.noUncheckedIndexedAccess.json',
687-
projectService: false,
688-
tsconfigRootDir: getFixturesRootDir(),
689-
},
683+
parserOptions: optionsWithNoUncheckedIndexedAccess,
684+
},
685+
},
686+
{
687+
code: `
688+
type Foo = {
689+
key?: Record<string, { key: string }>;
690+
};
691+
declare const foo: Foo;
692+
foo.key?.someKey?.key;
693+
`,
694+
languageOptions: {
695+
parserOptions: optionsWithNoUncheckedIndexedAccess,
696+
},
697+
},
698+
{
699+
code: `
700+
type Foo = {
701+
key?: {
702+
[key: string]: () => void;
703+
};
704+
};
705+
declare const foo: Foo;
706+
foo.key?.value?.();
707+
`,
708+
languageOptions: {
709+
parserOptions: optionsWithNoUncheckedIndexedAccess,
710+
},
711+
},
712+
{
713+
code: `
714+
type A = {
715+
[name in Lowercase<string>]?: {
716+
[name in Lowercase<string>]: {
717+
a: 1;
718+
};
719+
};
720+
};
721+
722+
declare const a: A;
723+
724+
a.a?.a?.a;
725+
`,
726+
languageOptions: {
727+
parserOptions: optionsWithNoUncheckedIndexedAccess,
690728
},
691729
},
692730
`

0 commit comments

Comments
 (0)