Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type { MakeRequired } from '../util';
import {
createRule,
getParserServices,
getWrappingFixer,
nullThrows,
NullThrowsReasons,
} from '../util';
Expand Down Expand Up @@ -108,7 +109,28 @@ export default createRule({
for (const reference of smTypeParameterVariable.references) {
if (reference.isTypeReference) {
const referenceNode = reference.identifier;
yield fixer.replaceText(referenceNode, constraintText);
const isComplexType =
constraint?.type === AST_NODE_TYPES.TSUnionType ||
constraint?.type === AST_NODE_TYPES.TSIntersectionType ||
constraint?.type === AST_NODE_TYPES.TSConditionalType;
const hasMatchingAncestorType = [
AST_NODE_TYPES.TSArrayType,
AST_NODE_TYPES.TSIndexedAccessType,
AST_NODE_TYPES.TSIntersectionType,
AST_NODE_TYPES.TSUnionType,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
].some(type => referenceNode.parent.parent!.type === type);
if (isComplexType && hasMatchingAncestorType) {
const fixResult = getWrappingFixer({
node: referenceNode,
innerNode: constraint,
sourceCode: context.sourceCode,
wrap: constraintNode => constraintNode,
})(fixer);
yield fixResult;
} else {
yield fixer.replaceText(referenceNode, constraintText);
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/eslint-plugin/src/util/getWrappingFixer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ interface WrappingFixerParams {
*/
export function getWrappingFixer(
params: WrappingFixerParams,
): TSESLint.ReportFixFunction {
): (fixer: TSESLint.RuleFixer) => TSESLint.RuleFix {
const { node, innerNode = node, sourceCode, wrap } = params;
const innerNodes = Array.isArray(innerNode) ? innerNode : [innerNode];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1744,5 +1744,172 @@ class Joiner {
},
],
},
{
code: `
function join<T extends string | number>(els: T[]) {
return els.map(el => '' + el).join(',');
}
`,
errors: [
{
data: { descriptor: 'function', name: 'T', uses: 'used only once' },
messageId: 'sole',
suggestions: [
{
messageId: 'replaceUsagesWithConstraint',
output: `
function join(els: (string | number)[]) {
return els.map(el => '' + el).join(',');
}
`,
},
],
},
],
},
{
code: `
function join<T extends string & number>(els: T[]) {
return els.map(el => '' + el).join(',');
}
`,
errors: [
{
data: { descriptor: 'function', name: 'T', uses: 'used only once' },
messageId: 'sole',
suggestions: [
{
messageId: 'replaceUsagesWithConstraint',
output: `
function join(els: (string & number)[]) {
return els.map(el => '' + el).join(',');
}
`,
},
],
},
],
},
{
code: `
function join<T extends (string & number) | boolean>(els: T[]) {
return els.map(el => '' + el).join(',');
}
`,
errors: [
{
data: { descriptor: 'function', name: 'T', uses: 'used only once' },
messageId: 'sole',
suggestions: [
{
messageId: 'replaceUsagesWithConstraint',
output: `
function join(els: ((string & number) | boolean)[]) {
return els.map(el => '' + el).join(',');
}
`,
},
],
},
],
},
{
code: noFormat`
function join<T extends (string | number)>(els: T[]) {
return els.map(el => '' + el).join(',');
}
`,
errors: [
{
data: { descriptor: 'function', name: 'T', uses: 'used only once' },
messageId: 'sole',
suggestions: [
{
messageId: 'replaceUsagesWithConstraint',
output: `
function join(els: (string | number)[]) {
return els.map(el => '' + el).join(',');
}
`,
},
],
},
],
},
{
code: `
function join<T extends { hoge: string } | { hoge: number }>(els: T['hoge'][]) {
return els.map(el => '' + el).join(',');
}
`,
errors: [
{
data: { descriptor: 'function', name: 'T', uses: 'used only once' },
messageId: 'sole',
suggestions: [
{
messageId: 'replaceUsagesWithConstraint',
output: `
function join(els: ({ hoge: string } | { hoge: number })['hoge'][]) {
return els.map(el => '' + el).join(',');
}
`,
},
],
},
],
},
{
code: `
type A = string;
type B = string;
type C = string;
declare function f<T extends A | B>(): T & C;
`,
errors: [
{
data: { descriptor: 'function', name: 'T', uses: 'used only once' },
messageId: 'sole',
suggestions: [
{
messageId: 'replaceUsagesWithConstraint',
output: `
type A = string;
type B = string;
type C = string;
declare function f(): (A | B) & C;
`,
},
],
},
],
},
{
code: `
type A = string;
type B = string;
type C = string;
type D = string;
declare function f<T extends A extends B ? C : D>(): T | null;
`,
errors: [
{
data: { descriptor: 'function', name: 'T', uses: 'used only once' },
messageId: 'sole',
suggestions: [
{
messageId: 'replaceUsagesWithConstraint',
output: `
type A = string;
type B = string;
type C = string;
type D = string;
declare function f(): (A extends B ? C : D) | null;
`,
},
],
},
],
},
],
});