Skip to content

Commit

Permalink
fix(eslint-plugin): [no-redundant-type-constituents] incorrectly mark…
Browse files Browse the repository at this point in the history
…s & string as redundant (#8282)
  • Loading branch information
arka1002 committed Mar 16, 2024
1 parent df00134 commit 093225c
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,10 @@ export default createRule({
PrimitiveTypeFlag,
TSESTree.TypeNode[]
>();
const seenUnionTypes = new Map<
TSESTree.TypeNode,
TypeFlagsWithName[]
>();

function checkIntersectionBottomAndTopTypes(
{ typeFlags, typeName }: TypeFlagsWithName,
Expand Down Expand Up @@ -323,8 +327,58 @@ export default createRule({
}
}
}
// if any typeNode is TSTypeReference and typePartFlags have more than 1 element, than the referenced type is definitely a union.
if (typePartFlags.length >= 2) {
seenUnionTypes.set(typeNode, typePartFlags);
}
}
/**
* @example
* ```ts
* type F = "a"|2|"b";
* type I = F & string;
* ```
* This function checks if all the union members of `F` are assignable to the other member of `I`. If every member is assignable, then its reported else not.
*/
const checkIfUnionsAreAssignable = (): undefined => {
for (const [typeRef, typeValues] of seenUnionTypes) {
let primitive: number | undefined = undefined;
for (const { typeFlags } of typeValues) {
if (
seenPrimitiveTypes.has(
literalToPrimitiveTypeFlags[
typeFlags as keyof typeof literalToPrimitiveTypeFlags
],
)
) {
primitive =
literalToPrimitiveTypeFlags[
typeFlags as keyof typeof literalToPrimitiveTypeFlags
];
} else {
primitive = undefined;
break;
}
}
if (Number.isInteger(primitive)) {
context.report({
data: {
literal: typeValues.map(name => name.typeName).join(' | '),
primitive:
primitiveTypeFlagNames[
primitive as keyof typeof primitiveTypeFlagNames
],
},
messageId: 'primitiveOverridden',
node: typeRef,
});
}
}
};
if (seenUnionTypes.size > 0) {
checkIfUnionsAreAssignable();
return;
}

// For each primitive type of all the seen primitive types,
// if there was a literal type seen that overrides it,
// report each of the primitive type's type nodes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,10 @@ ruleTester.run('no-redundant-type-constituents', rule, {
type B = \`\${string}\`;
type T = B & null;
`,
`
type T = 'a' | 1 | 'b';
type U = T & string;
`,
],

invalid: [
Expand Down Expand Up @@ -785,5 +789,46 @@ ruleTester.run('no-redundant-type-constituents', rule, {
},
],
},
{
code: `
type T = 'a' | 'b';
type U = T & string;
`,
errors: [
{
column: 18,
data: {
literal: '"a" | "b"',
primitive: 'string',
},
messageId: 'primitiveOverridden',
},
],
},
{
code: `
type S = 1 | 2;
type T = 'a' | 'b';
type U = S & T & string & number;
`,
errors: [
{
column: 18,
data: {
literal: '1 | 2',
primitive: 'number',
},
messageId: 'primitiveOverridden',
},
{
column: 22,
data: {
literal: '"a" | "b"',
primitive: 'string',
},
messageId: 'primitiveOverridden',
},
],
},
],
});

0 comments on commit 093225c

Please sign in to comment.