Skip to content

Commit

Permalink
fix(eslint-plugin): [no-unnecessary-type-assertion] handle exactOptio…
Browse files Browse the repository at this point in the history
…nalPropertyTypes compiler option (#8770)

* fix(eslint-plugin): [no-unnecessary-type-assertion] handle exactOptionalPropertyTypes compiler option

* rename function

* edit tc

* apply review
  • Loading branch information
yeonjuan committed Apr 1, 2024
1 parent 7f9fcca commit f0b1c4f
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,38 @@ export default createRule<Options, MessageIds>({
);
}

function isTypeUnchanged(uncast: ts.Type, cast: ts.Type): boolean {
if (uncast === cast) {
return true;
}

if (
isTypeFlagSet(uncast, ts.TypeFlags.Undefined) &&
isTypeFlagSet(cast, ts.TypeFlags.Undefined) &&
tsutils.isCompilerOptionEnabled(
compilerOptions,
'exactOptionalPropertyTypes',
)
) {
const uncastParts = tsutils
.unionTypeParts(uncast)
.filter(part => !isTypeFlagSet(part, ts.TypeFlags.Undefined));

const castParts = tsutils
.unionTypeParts(cast)
.filter(part => !isTypeFlagSet(part, ts.TypeFlags.Undefined));

if (uncastParts.length !== castParts.length) {
return false;
}

const uncastPartsSet = new Set(uncastParts);
return castParts.every(part => uncastPartsSet.has(part));
}

return false;
}

return {
TSNonNullExpression(node): void {
if (
Expand Down Expand Up @@ -232,7 +264,7 @@ export default createRule<Options, MessageIds>({

const castType = services.getTypeAtLocation(node);
const uncastType = services.getTypeAtLocation(node.expression);
const typeIsUnchanged = uncastType === castType;
const typeIsUnchanged = isTypeUnchanged(uncastType, castType);

const wouldSameTypeBeInferred = castType.isLiteral()
? isLiteralVariableDeclarationChangingTypeWithConst(node)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ const optionsWithOnUncheckedIndexedAccess = {
project: './tsconfig.noUncheckedIndexedAccess.json',
};

const optionsWithExactOptionalPropertyTypes = {
tsconfigRootDir: rootDir,
project: './tsconfig.exactOptionalPropertyTypes.json',
};

ruleTester.run('no-unnecessary-type-assertion', rule, {
valid: [
`
Expand Down Expand Up @@ -287,6 +292,51 @@ const templateLiteral = \`\${myString}-somethingElse\` as const;
const myString = 'foo';
const templateLiteral = <const>\`\${myString}-somethingElse\`;
`,
{
code: `
declare const foo: {
a?: string;
};
const bar = foo.a as string;
`,
parserOptions: optionsWithExactOptionalPropertyTypes,
},
{
code: `
declare const foo: {
a?: string | undefined;
};
const bar = foo.a as string;
`,
parserOptions: optionsWithExactOptionalPropertyTypes,
},
{
code: `
declare const foo: {
a: string;
};
const bar = foo.a as string | undefined;
`,
parserOptions: optionsWithExactOptionalPropertyTypes,
},
{
code: `
declare const foo: {
a?: string | null | number;
};
const bar = foo.a as string | undefined;
`,
parserOptions: optionsWithExactOptionalPropertyTypes,
},
{
code: `
declare const foo: {
a?: string | number;
};
const bar = foo.a as string | undefined | bigint;
`,
parserOptions: optionsWithExactOptionalPropertyTypes,
},
],

invalid: [
Expand Down Expand Up @@ -934,5 +984,50 @@ function bar(items: string[]) {
},
],
},
// exactOptionalPropertyTypes = true
{
code: `
declare const foo: {
a?: string;
};
const bar = foo.a as string | undefined;
`,
output: `
declare const foo: {
a?: string;
};
const bar = foo.a;
`,
errors: [
{
messageId: 'unnecessaryAssertion',
line: 5,
column: 13,
},
],
parserOptions: optionsWithExactOptionalPropertyTypes,
},
{
code: `
declare const foo: {
a?: string | undefined;
};
const bar = foo.a as string | undefined;
`,
output: `
declare const foo: {
a?: string | undefined;
};
const bar = foo.a;
`,
errors: [
{
messageId: 'unnecessaryAssertion',
line: 5,
column: 13,
},
],
parserOptions: optionsWithExactOptionalPropertyTypes,
},
],
});

0 comments on commit f0b1c4f

Please sign in to comment.