diff --git a/rules/no-useless-undefined.js b/rules/no-useless-undefined.js index 2c2497758e..6ee252a17b 100644 --- a/rules/no-useless-undefined.js +++ b/rules/no-useless-undefined.js @@ -1,7 +1,7 @@ 'use strict'; const {isCommaToken} = require('@eslint-community/eslint-utils'); const {replaceNodeOrTokenAndSpacesBefore} = require('./fix/index.js'); -const {isUndefined} = require('./ast/index.js'); +const {isUndefined, isFunction} = require('./ast/index.js'); const messageId = 'no-useless-undefined'; const messages = { @@ -80,6 +80,9 @@ const isFunctionBindCall = node => && node.callee.property.type === 'Identifier' && node.callee.property.name === 'bind'; +const isTypeScriptFile = context => + /\.(?:ts|mts|cts|tsx)$/i.test(context.getPhysicalFilename()); + /** @param {import('eslint').Rule.RuleContext} context */ const create = context => { const {sourceCode} = context; @@ -179,7 +182,24 @@ const create = context => { ) { return getProblem( node, - fixer => fixer.removeRange([node.parent.left.range[1], node.range[1]]), + function * (fixer) { + const assignmentPattern = node.parent; + const {left} = assignmentPattern; + + yield fixer.removeRange([left.range[1], node.range[1]]); + if ( + (left.typeAnnotation || isTypeScriptFile(context)) + && !left.optional + && isFunction(assignmentPattern.parent) + && assignmentPattern.parent.params.includes(assignmentPattern) + ) { + yield ( + left.typeAnnotation + ? fixer.insertTextBefore(left.typeAnnotation, '?') + : fixer.insertTextAfter(left, '?') + ); + } + }, /* CheckFunctionReturnType */ true, ); } diff --git a/test/no-useless-undefined.mjs b/test/no-useless-undefined.mjs index 0901e83230..9964a7a14c 100644 --- a/test/no-useless-undefined.mjs +++ b/test/no-useless-undefined.mjs @@ -471,3 +471,34 @@ test.snapshot({ `, ], }); + +test.snapshot({ + testerOptions: { + parser: parsers.typescript, + }, + valid: [], + invalid: [ + 'function f(foo: Type = undefined) {}', + 'function f(foo?: Type = undefined) {}', + 'const f = function(foo: Type = undefined) {}', + 'const f = (foo: Type = undefined) => {}', + 'const f = {method(foo: Type = undefined){}}', + 'const f = class {method(foo: Type = undefined){}}', + 'function f(foo = undefined) {}', + ...[ + undefined, + 'foo.js', + 'foo.ts', + 'foo.MTs', + 'foo.cts', + 'foo.tsx', + ].map(filename => ({ + code: 'function f(foo = undefined) {}', + filename, + })), + { + code: 'function a({foo} = undefined) {}', + filename: 'foo.ts', + }, + ], +}); diff --git a/test/snapshots/no-useless-undefined.mjs.md b/test/snapshots/no-useless-undefined.mjs.md index 158c538dc4..8d4be87aa1 100644 --- a/test/snapshots/no-useless-undefined.mjs.md +++ b/test/snapshots/no-useless-undefined.mjs.md @@ -324,3 +324,263 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^ Do not use useless \`undefined\`.␊ 4 | ␊ ` + +## Invalid #1 + 1 | function f(foo: Type = undefined) {} + +> Output + + `␊ + 1 | function f(foo?: Type) {}␊ + ` + +> Error 1/1 + + `␊ + > 1 | function f(foo: Type = undefined) {}␊ + | ^^^^^^^^^ Do not use useless \`undefined\`.␊ + ` + +## Invalid #2 + 1 | function f(foo?: Type = undefined) {} + +> Output + + `␊ + 1 | function f(foo?: Type) {}␊ + ` + +> Error 1/1 + + `␊ + > 1 | function f(foo?: Type = undefined) {}␊ + | ^^^^^^^^^ Do not use useless \`undefined\`.␊ + ` + +## Invalid #3 + 1 | const f = function(foo: Type = undefined) {} + +> Output + + `␊ + 1 | const f = function(foo?: Type) {}␊ + ` + +> Error 1/1 + + `␊ + > 1 | const f = function(foo: Type = undefined) {}␊ + | ^^^^^^^^^ Do not use useless \`undefined\`.␊ + ` + +## Invalid #4 + 1 | const f = (foo: Type = undefined) => {} + +> Output + + `␊ + 1 | const f = (foo?: Type) => {}␊ + ` + +> Error 1/1 + + `␊ + > 1 | const f = (foo: Type = undefined) => {}␊ + | ^^^^^^^^^ Do not use useless \`undefined\`.␊ + ` + +## Invalid #5 + 1 | const f = {method(foo: Type = undefined){}} + +> Output + + `␊ + 1 | const f = {method(foo?: Type){}}␊ + ` + +> Error 1/1 + + `␊ + > 1 | const f = {method(foo: Type = undefined){}}␊ + | ^^^^^^^^^ Do not use useless \`undefined\`.␊ + ` + +## Invalid #6 + 1 | const f = class {method(foo: Type = undefined){}} + +> Output + + `␊ + 1 | const f = class {method(foo?: Type){}}␊ + ` + +> Error 1/1 + + `␊ + > 1 | const f = class {method(foo: Type = undefined){}}␊ + | ^^^^^^^^^ Do not use useless \`undefined\`.␊ + ` + +## Invalid #7 + 1 | function f(foo = undefined) {} + +> Output + + `␊ + 1 | function f(foo) {}␊ + ` + +> Error 1/1 + + `␊ + > 1 | function f(foo = undefined) {}␊ + | ^^^^^^^^^ Do not use useless \`undefined\`.␊ + ` + +## Invalid #8 + 1 | function f(foo = undefined) {} + +> Output + + `␊ + 1 | function f(foo) {}␊ + ` + +> Error 1/1 + + `␊ + > 1 | function f(foo = undefined) {}␊ + | ^^^^^^^^^ Do not use useless \`undefined\`.␊ + ` + +## Invalid #9 + 1 | function f(foo = undefined) {} + +> Filename + + `␊ + foo.js␊ + ` + +> Output + + `␊ + 1 | function f(foo) {}␊ + ` + +> Error 1/1 + + `␊ + > 1 | function f(foo = undefined) {}␊ + | ^^^^^^^^^ Do not use useless \`undefined\`.␊ + ` + +## Invalid #10 + 1 | function f(foo = undefined) {} + +> Filename + + `␊ + foo.ts␊ + ` + +> Output + + `␊ + 1 | function f(foo?) {}␊ + ` + +> Error 1/1 + + `␊ + > 1 | function f(foo = undefined) {}␊ + | ^^^^^^^^^ Do not use useless \`undefined\`.␊ + ` + +## Invalid #11 + 1 | function f(foo = undefined) {} + +> Filename + + `␊ + foo.MTs␊ + ` + +> Output + + `␊ + 1 | function f(foo?) {}␊ + ` + +> Error 1/1 + + `␊ + > 1 | function f(foo = undefined) {}␊ + | ^^^^^^^^^ Do not use useless \`undefined\`.␊ + ` + +## Invalid #12 + 1 | function f(foo = undefined) {} + +> Filename + + `␊ + foo.cts␊ + ` + +> Output + + `␊ + 1 | function f(foo?) {}␊ + ` + +> Error 1/1 + + `␊ + > 1 | function f(foo = undefined) {}␊ + | ^^^^^^^^^ Do not use useless \`undefined\`.␊ + ` + +## Invalid #13 + 1 | function f(foo = undefined) {} + +> Filename + + `␊ + foo.tsx␊ + ` + +> Output + + `␊ + 1 | function f(foo?) {}␊ + ` + +> Error 1/1 + + `␊ + > 1 | function f(foo = undefined) {}␊ + | ^^^^^^^^^ Do not use useless \`undefined\`.␊ + ` + +## Invalid #14 + 1 | function a({foo} = undefined) {} + +> Filename + + `␊ + foo.ts␊ + ` + +> Output + + `␊ + 1 | function a({foo}?) {}␊ + ` + +> Error 1/1 + + `␊ + > 1 | function a({foo} = undefined) {}␊ + | ^^^^^^^^^ Do not use useless \`undefined\`.␊ + ` diff --git a/test/snapshots/no-useless-undefined.mjs.snap b/test/snapshots/no-useless-undefined.mjs.snap index df70b8a758..c393334e76 100644 Binary files a/test/snapshots/no-useless-undefined.mjs.snap and b/test/snapshots/no-useless-undefined.mjs.snap differ