Skip to content

Commit

Permalink
feat(eslint-plugin): [ban-types] add NonNullable suggestion and allow…
Browse files Browse the repository at this point in the history
… custom suggestions (#6876)

* Update ban-types error message

* Add suggest option
  • Loading branch information
NotWoods committed Apr 16, 2023
1 parent 40f4eee commit ff65235
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 1 deletion.
31 changes: 30 additions & 1 deletion packages/eslint-plugin/src/rules/ban-types.ts
Expand Up @@ -11,6 +11,7 @@ type Types = Record<
| {
message: string;
fixWith?: string;
suggest?: readonly string[];
}
>;

Expand All @@ -20,7 +21,7 @@ export type Options = [
extendDefaults?: boolean;
},
];
export type MessageIds = 'bannedTypeMessage';
export type MessageIds = 'bannedTypeMessage' | 'bannedTypeReplacement';

function removeSpaces(str: string): string {
return str.replace(/\s/g, '');
Expand Down Expand Up @@ -88,15 +89,24 @@ const defaultTypes: Types = {
'The `Object` type actually means "any non-nullish value", so it is marginally better than `unknown`.',
'- If you want a type meaning "any object", you probably want `object` instead.',
'- If you want a type meaning "any value", you probably want `unknown` instead.',
'- If you really want a type meaning "any non-nullish value", you probably want `NonNullable<unknown>` instead.',
].join('\n'),
suggest: ['object', 'unknown', 'NonNullable<unknown>'],
},
'{}': {
message: [
'`{}` actually means "any non-nullish value".',
'- If you want a type meaning "any object", you probably want `object` instead.',
'- If you want a type meaning "any value", you probably want `unknown` instead.',
'- If you want a type meaning "empty object", you probably want `Record<string, never>` instead.',
'- If you really want a type meaning "any non-nullish value", you probably want `NonNullable<unknown>` instead.',
].join('\n'),
suggest: [
'object',
'unknown',
'Record<string, never>',
'NonNullable<unknown>',
],
},
};

Expand All @@ -123,8 +133,10 @@ export default util.createRule<Options, MessageIds>({
recommended: 'error',
},
fixable: 'code',
hasSuggestions: true,
messages: {
bannedTypeMessage: "Don't use `{{name}}` as a type.{{customMessage}}",
bannedTypeReplacement: 'Replace `{{name}}` with `{{replacement}}`.',
},
schema: [
{
Expand All @@ -142,6 +154,10 @@ export default util.createRule<Options, MessageIds>({
properties: {
message: { type: 'string' },
fixWith: { type: 'string' },
suggest: {
type: 'array',
items: { type: 'string' },
},
},
additionalProperties: false,
},
Expand Down Expand Up @@ -182,6 +198,10 @@ export default util.createRule<Options, MessageIds>({
const customMessage = getCustomMessage(bannedType);
const fixWith =
bannedType && typeof bannedType === 'object' && bannedType.fixWith;
const suggest =
bannedType && typeof bannedType === 'object'
? bannedType.suggest
: undefined;

context.report({
node: typeNode,
Expand All @@ -193,6 +213,15 @@ export default util.createRule<Options, MessageIds>({
fix: fixWith
? (fixer): TSESLint.RuleFix => fixer.replaceText(typeNode, fixWith)
: null,
suggest: suggest?.map(replacement => ({
messageId: 'bannedTypeReplacement',
data: {
name,
replacement,
},
fix: (fixer): TSESLint.RuleFix =>
fixer.replaceText(typeNode, replacement),
})),
});
}

Expand Down
37 changes: 37 additions & 0 deletions packages/eslint-plugin/tests/rules/ban-types.test.ts
Expand Up @@ -136,6 +136,43 @@ ruleTester.run('ban-types', rule, {
],
options,
},
{
code: 'let a: Object;',
errors: [
{
messageId: 'bannedTypeMessage',
data: {
name: 'Object',
customMessage: [
' The `Object` type actually means "any non-nullish value", so it is marginally better than `unknown`.',
'- If you want a type meaning "any object", you probably want `object` instead.',
'- If you want a type meaning "any value", you probably want `unknown` instead.',
'- If you really want a type meaning "any non-nullish value", you probably want `NonNullable<unknown>` instead.',
].join('\n'),
},
line: 1,
column: 8,
suggestions: [
{
messageId: 'bannedTypeReplacement',
data: { name: 'Object', replacement: 'object' },
output: 'let a: object;',
},
{
messageId: 'bannedTypeReplacement',
data: { name: 'Object', replacement: 'unknown' },
output: 'let a: unknown;',
},
{
messageId: 'bannedTypeReplacement',
data: { name: 'Object', replacement: 'NonNullable<unknown>' },
output: 'let a: NonNullable<unknown>;',
},
],
},
],
options: [{}],
},
{
code: 'let aa: Foo;',
errors: [
Expand Down

0 comments on commit ff65235

Please sign in to comment.