diff --git a/eslint-rules/src/rules/no-optional-inputs.ts b/eslint-rules/src/rules/no-optional-inputs.ts new file mode 100644 index 0000000000..6c89d3bb43 --- /dev/null +++ b/eslint-rules/src/rules/no-optional-inputs.ts @@ -0,0 +1,40 @@ +import { Selectors } from '@angular-eslint/utils'; +import { AST_NODE_TYPES, TSESLint, TSESTree } from '@typescript-eslint/utils'; + +const messages = { + doNotUseOptionalOperatorOnInputs: 'Angular @Input() properties are optional by default.', +}; + +const noOptionalInputsRule: TSESLint.RuleModule = { + meta: { + messages, + type: 'problem', + fixable: 'code', + schema: [], + docs: { + description: 'Disallow optional inputs', + recommended: 'warn', + }, + }, + create: context => ({ + [Selectors.INPUT_DECORATOR](node: TSESTree.Decorator): void { + if ( + node.parent.type === AST_NODE_TYPES.PropertyDefinition && + node.parent.key.type === AST_NODE_TYPES.Identifier && + node.parent.key.parent.type === AST_NODE_TYPES.PropertyDefinition && + node.parent.key.parent.optional + ) { + const loc = node.parent.key.range[1]; + context.report({ + node: node.parent.key.parent, + messageId: 'doNotUseOptionalOperatorOnInputs', + fix(fixer) { + return fixer.removeRange([loc, loc + 1]); + }, + }); + } + }, + }), +}; + +export default noOptionalInputsRule; diff --git a/eslint-rules/tests/no-optional-inputs.spec.ts b/eslint-rules/tests/no-optional-inputs.spec.ts new file mode 100644 index 0000000000..b8c28b93ab --- /dev/null +++ b/eslint-rules/tests/no-optional-inputs.spec.ts @@ -0,0 +1,24 @@ +import noOptionalInputsRule from '../src/rules/no-optional-inputs'; + +import testRule from './rule-tester'; + +testRule(noOptionalInputsRule, { + valid: [ + { + name: 'should not report when inputs do not use the optional operator', + code: 'export class MyComponent { @Input() foo: string; }', + }, + ], + invalid: [ + { + name: 'should report when inputs use the optional operator', + code: 'export class MyComponent { @Input() foo?: string; }', + errors: [ + { + messageId: 'doNotUseOptionalOperatorOnInputs', + }, + ], + output: 'export class MyComponent { @Input() foo: string; }', + }, + ], +});