diff --git a/packages/eslint-plugin/docs/rules/interface-name-prefix.md b/packages/eslint-plugin/docs/rules/interface-name-prefix.md index f6439b490fd1..96ebecd63c72 100644 --- a/packages/eslint-plugin/docs/rules/interface-name-prefix.md +++ b/packages/eslint-plugin/docs/rules/interface-name-prefix.md @@ -11,18 +11,21 @@ This rule enforces whether or not the `I` prefix is required for interface names The `_` prefix is sometimes used to designate a private declaration, so the rule also supports a private interface that might be named `_IAnimal` instead of `IAnimal`. +Finally, there are a few rare names that look like an `I` prefix, such as Identity and Access Management, more commonly refereed to as "IAM". +For these rare names, the rule can be provided an array of names to allow, via the `allowedPrefixes` option. + ## Options This rule has an object option: -- `{ "prefixWithI": "never" }`: (default) disallows all interfaces being prefixed with `"I"` or `"_I"` +- `{ "prefixWithI": "never", "allowedPrefixes": [] }`: (default) disallows all interfaces being prefixed with `"I"` or `"_I"` - `{ "prefixWithI": "always" }`: requires all interfaces be prefixed with `"I"` (but does not allow `"_I"`) - `{ "prefixWithI": "always", "allowUnderscorePrefix": true }`: requires all interfaces be prefixed with either `"I"` or `"_I"` For backwards compatibility, this rule supports a string option instead: -- `"never"`: Equivalent to `{ "prefixWithI": "never" }` +- `"never"`: Equivalent to `{ "prefixWithI": "never", "allowedPrefixes": [] }` - `"always"`: Equivalent to `{ "prefixWithI": "always" }` ## Examples @@ -59,6 +62,34 @@ interface Iguana { } ``` +### never and allowing names + +**Configuration:** `{ "prefixWithI": "never", "allowedPrefixes": ["IAM"] }` + +The following patterns are considered warnings: + +```ts +interface IAnimal { + name: string; +} + +interface IPMUser { + name: string; +} +``` + +The following patterns are not warnings: + +```ts +interface Animal { + name: string; +} + +interface IAMUser { + name: string; +} +``` + ### always **Configuration:** `{ "prefixWithI": "always" }` diff --git a/packages/eslint-plugin/src/rules/interface-name-prefix.ts b/packages/eslint-plugin/src/rules/interface-name-prefix.ts index 6f2c57dc3159..1884878d8e8e 100644 --- a/packages/eslint-plugin/src/rules/interface-name-prefix.ts +++ b/packages/eslint-plugin/src/rules/interface-name-prefix.ts @@ -3,6 +3,7 @@ import * as util from '../util'; type ParsedOptions = | { prefixWithI: 'never'; + allowedPrefixes: string[]; } | { prefixWithI: 'always'; @@ -13,6 +14,7 @@ type Options = [ | 'always' | { prefixWithI?: 'never'; + allowedPrefixes?: string[]; } | { prefixWithI: 'always'; @@ -34,7 +36,11 @@ export function parseOptions([options]: Options): ParsedOptions { allowUnderscorePrefix: !!options.allowUnderscorePrefix, }; } - return { prefixWithI: 'never' }; + return { + prefixWithI: 'never', + allowedPrefixes: + (typeof options === 'object' && options.allowedPrefixes) || [], + }; } export default util.createRule({ @@ -58,7 +64,7 @@ export default util.createRule({ oneOf: [ { enum: [ - // Deprecated, equivalent to: { prefixWithI: 'never' } + // Deprecated, equivalent to: { prefixWithI: 'never', allowedPrefixes: [] } 'never', // Deprecated, equivalent to: { prefixWithI: 'always', allowUnderscorePrefix: false } 'always', @@ -71,6 +77,12 @@ export default util.createRule({ type: 'string', enum: ['never'], }, + allowedPrefixes: { + type: 'array', + items: { + type: 'string', + }, + }, }, additionalProperties: false, }, @@ -92,7 +104,7 @@ export default util.createRule({ }, ], }, - defaultOptions: [{ prefixWithI: 'never' }], + defaultOptions: [{ prefixWithI: 'never', allowedPrefixes: [] }], create(context, [options]) { const parsedOptions = parseOptions([options]); @@ -101,10 +113,6 @@ export default util.createRule({ * @param name The string to check */ function isPrefixedWithI(name: string): boolean { - if (typeof name !== 'string') { - return false; - } - return /^I[A-Z]/.test(name); } @@ -113,17 +121,18 @@ export default util.createRule({ * @param name The string to check */ function isPrefixedWithIOrUnderscoreI(name: string): boolean { - if (typeof name !== 'string') { - return false; - } - return /^_?I[A-Z]/.test(name); } return { TSInterfaceDeclaration(node): void { if (parsedOptions.prefixWithI === 'never') { - if (isPrefixedWithIOrUnderscoreI(node.id.name)) { + if ( + isPrefixedWithIOrUnderscoreI(node.id.name) && + !parsedOptions.allowedPrefixes.some(allowedPrefix => + node.id.name.startsWith(allowedPrefix), + ) + ) { context.report({ node: node.id, messageId: 'noPrefix', diff --git a/packages/eslint-plugin/tests/rules/interface-name-prefix.test.ts b/packages/eslint-plugin/tests/rules/interface-name-prefix.test.ts index 337a96a368fb..4c1776f158ba 100644 --- a/packages/eslint-plugin/tests/rules/interface-name-prefix.test.ts +++ b/packages/eslint-plugin/tests/rules/interface-name-prefix.test.ts @@ -1,26 +1,41 @@ -import assert from 'assert'; import rule, { parseOptions } from '../../src/rules/interface-name-prefix'; import { RuleTester } from '../RuleTester'; describe('interface-name-prefix', () => { it('parseOptions', () => { - assert.deepEqual(parseOptions(['never']), { prefixWithI: 'never' }); - assert.deepEqual(parseOptions(['always']), { + expect(parseOptions(['never'])).toStrictEqual({ + prefixWithI: 'never', + allowedPrefixes: [], + }); + expect(parseOptions(['always'])).toStrictEqual({ prefixWithI: 'always', allowUnderscorePrefix: false, }); - assert.deepEqual(parseOptions([{}]), { prefixWithI: 'never' }); - assert.deepEqual(parseOptions([{ prefixWithI: 'never' }]), { + expect(parseOptions([{}])).toStrictEqual({ + prefixWithI: 'never', + allowedPrefixes: [], + }); + expect(parseOptions([{ prefixWithI: 'never' }])).toStrictEqual({ + prefixWithI: 'never', + allowedPrefixes: [], + }); + expect(parseOptions([{ allowedPrefixes: ['IAM'] }])).toStrictEqual({ + prefixWithI: 'never', + allowedPrefixes: ['IAM'], + }); + expect( + parseOptions([{ prefixWithI: 'never', allowedPrefixes: [] }]), + ).toStrictEqual({ prefixWithI: 'never', + allowedPrefixes: [], }); - assert.deepEqual(parseOptions([{ prefixWithI: 'always' }]), { + expect(parseOptions([{ prefixWithI: 'always' }])).toStrictEqual({ prefixWithI: 'always', allowUnderscorePrefix: false, }); - assert.deepEqual( + expect( parseOptions([{ prefixWithI: 'always', allowUnderscorePrefix: true }]), - { prefixWithI: 'always', allowUnderscorePrefix: true }, - ); + ).toStrictEqual({ prefixWithI: 'always', allowUnderscorePrefix: true }); }); }); @@ -83,6 +98,14 @@ interface I18n { `, options: ['never'], }, + { + code: ` +interface IAMUser { + name: string; +} + `, + options: [{ allowedPrefixes: ['IAM'] }], + }, ], invalid: [ { @@ -101,6 +124,21 @@ interface IAnimal { }, { code: ` +interface IAMUser { + name: string; +} + `, + options: [{ allowedPrefixes: ['IPM'] }], + errors: [ + { + messageId: 'noPrefix', + line: 2, + column: 11, + }, + ], + }, + { + code: ` interface Animal { name: string; } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy