Enforcing naming conventions helps keep the codebase consistent, and reduces overhead when thinking about how to name a variable.
Additionally, a well designed style guide can help communicate intent, such as by enforcing all private properties begin with an _
, and all global-level constants are written in UPPER_CASE
.
There are many different rules that have existed over time, but they have had the problem of not having enough granularity, meaning it was hard to have a well defined style guide, and most of the time you needed 3 or more rules at once to enforce different conventions, hoping they didn't conflict.
This rule allows you to enforce conventions for any identifier, using granular selectors to create a fine-grained style guide.
This rule accepts an array of objects, with each object describing a different naming convention. Each property will be described in detail below. Also see the examples section below for illustrated examples.
type Options = {
// format options
format:
| (
| 'camelCase'
| 'strictCamelCase'
| 'PascalCase'
| 'StrictPascalCase'
| 'snake_case'
| 'UPPER_CASE'
)[]
| null;
custom?: {
regex: string;
match: boolean;
};
leadingUnderscore?: 'forbid' | 'allow' | 'require';
trailingUnderscore?: 'forbid' | 'allow' | 'require';
prefix?: string[];
suffix?: string[];
// selector options
selector: Selector;
filter?: string;
// the allowed values for these are dependent on the selector - see below
modifiers?: Modifiers<Selector>[];
types?: Types<Selector>[];
}[];
// the default config essentially does the same thing as ESLint's camelcase rule
const defaultOptions: Options = [
{
selector: 'default',
format: ['camelCase'],
leadingUnderscore: 'allow',
trailingUnderscore: 'allow',
},
{
selector: 'variable',
format: ['camelCase', 'UPPER_CASE'],
leadingUnderscore: 'allow',
trailingUnderscore: 'allow',
},
{
selector: 'typeLike',
format: ['PascalCase'],
},
];
Every single selector can have the same set of format options. When the format of an identifier is checked, it is checked in the following order:
- validate leading underscore
- validate trailing underscore
- validate prefix
- validate suffix
- validate custom
- validate format
For steps 1-4, if the identifier matches the option, the matching part will be removed.
For example, if you provide the following formatting option: { leadingUnderscore: 'allow', prefix: ['I'], format: ['StrictPascalCase'] }
, for the identifier _IMyInterface
, then the following checks will occur:
name = _IMyInterface
- validate leading underscore - pass
- Trim leading underscore -
name = IMyInterface
- Trim leading underscore -
- validate trailing underscore - no check
- validate prefix - pass
- Trim prefix -
name = MyInterface
- Trim prefix -
- validate suffix - no check
- validate custom - no check
- validate format - pass
One final note is that if the name were to become empty via this trimming process, it is considered to match all format
s. An example of where this might be useful is for generic type parameters, where you want all names to be prefixed with T
, but also want to allow for the single character T
name.
The format
option defines the allowed formats for the identifier. This option accepts an array of the following values, and the identifier can match any of them:
camelCase
- standard camelCase format - no underscores are allowed between characters, and consecutive capitals are allowed (i.e. bothmyID
andmyId
are valid).strictCamelCase
- same ascamelCase
, but consecutive capitals are not allowed (i.e.myId
is valid, butmyID
is not).PascalCase
- same ascamelCase
, except the first character must be upper-case.StrictPascalCase
- same asstrictCamelCase
, except the first character must be upper-case.snake_case
- standard snake_case format - all characters must be lower-case, and underscores are allowed.UPPER_CASE
- same assnake_case
, except all characters must be upper-case.
Instead of an array, you may also pass null
. This signifies "this selector shall not have its format checked".
This can be useful if you want to enforce no particular format for a specific selector, after applying a group selector.
The custom
option defines a custom regex that the identifier must (or must not) match. This option allows you to have a bit more finer-grained control over identifiers, letting you ban (or force) certain patterns and substrings.
Accepts an object with the following properties:
regex
- accepts a regular expression (anything accepted intonew RegExp(regex)
).match
- true if the identifier must match theregex
, false if the identifier must not match theregex
.
The leadingUnderscore
/ trailingUnderscore
options control whether leading/trailing underscores are considered valid. Accepts one of the following values:
forbid
- a leading/trailing underscore is not allowed at all.allow
- existence of a leading/trailing underscore is not explicitly enforced.require
- a leading/trailing underscore must be included.
The prefix
/ suffix
options control which prefix/suffix strings must exist for the identifier. Accepts an array of strings.
If these are provided, the identifier must start with one of the provided values. For example, if you provide { prefix: ['IFace', 'Class', 'Type'] }
, then the following names are valid: IFaceFoo
, ClassBar
, TypeBaz
, but the name Bang
is not valid, as it contains none of the prefixes.
selector
(see "Allowed Selectors, Modifiers and Types" below).filter
accepts a regular expression (anything accepted intonew RegExp(filter)
). It allows you to limit the scope of this configuration to names that match this regex.modifiers
allows you to specify which modifiers to granularly apply to, such as the accessibility (private
/public
/protected
), or if the thing isstatic
, etc.- The name must match all of the modifiers.
- For example, if you provide
{ modifiers: ['private', 'static', 'readonly'] }
, then it will only match something that isprivate static readonly
, and something that is justprivate
will not match.
types
allows you to specify which types to match. This option supports simple, primitive types only (boolean
,string
,number
,array
,function
).- The name must match one of the types.
- NOTE - Using this option will require that you lint with type information.
- For example, this lets you do things like enforce that
boolean
variables are prefixed with a verb. boolean
matches any type assignable toboolean | null | undefined
string
matches any type assignable tostring | null | undefined
number
matches any type assignable tonumber | null | undefined
array
matches any type assignable toArray<unknown> | null | undefined
function
matches any type assignable toFunction | null | undefined
The ordering of selectors does not matter. The implementation will automatically sort the selectors to ensure they match from most-specific to least specific. It will keep checking selectors in that order until it finds one that matches the name.
For example, if you provide the following config:
[
/* 1 */ { selector: 'default', format: ['camelCase'] },
/* 2 */ { selector: 'variable', format: ['snake_case'] },
/* 3 */ { selector: 'variable', type: ['boolean'], format: ['UPPER_CASE'] },
/* 4 */ { selector: 'variableLike', format: ['PascalCase'] },
];
Then for the code const x = 1
, the rule will validate the selectors in the following order: 3
, 2
, 4
, 1
.
There are two types of selectors, individual selectors, and grouped selectors.
Individual Selectors match specific, well-defined sets. There is no overlap between each of the individual selectors.
variable
- matches anyvar
/let
/const
variable name.- Allowed
modifiers
: none. - Allowed
types
:boolean
,string
,number
,function
,array
.
- Allowed
function
- matches any named function declaration or named function expression.- Allowed
modifiers
: none. - Allowed
types
: none.
- Allowed
parameter
- matches any function parameter. Does not match parameter properties.- Allowed
modifiers
: none. - Allowed
types
:boolean
,string
,number
,function
,array
.
- Allowed
property
- matches any object, class, or object type property. Does not match properties that have direct function expression or arrow function expression values.- Allowed
modifiers
:private
,protected
,public
,static
,readonly
,abstract
. - Allowed
types
:boolean
,string
,number
,function
,array
.
- Allowed
parameterProperty
- matches any parameter property.- Allowed
modifiers
:private
,protected
,public
,readonly
. - Allowed
types
:boolean
,string
,number
,function
,array
.
- Allowed
method
- matches any object, class, or object type method. Also matches properties that have direct function expression or arrow function expression values. Does not match accessors.- Allowed
modifiers
:private
,protected
,public
,static
,readonly
,abstract
. - Allowed
types
: none.
- Allowed
accessor
- matches any accessor.- Allowed
modifiers
:private
,protected
,public
,static
,readonly
,abstract
. - Allowed
types
:boolean
,string
,number
,function
,array
.
- Allowed
enumMember
- matches any enum member.- Allowed
modifiers
: none. - Allowed
types
: none.
- Allowed
class
- matches any class declaration.- Allowed
modifiers
:abstract
. - Allowed
types
: none.
- Allowed
interface
- matches any interface declaration.- Allowed
modifiers
: none. - Allowed
types
: none.
- Allowed
typeAlias
- matches any type alias declaration.- Allowed
modifiers
: none. - Allowed
types
: none.
- Allowed
enum
- matches any enum declaration.- Allowed
modifiers
: none. - Allowed
types
: none.
- Allowed
typeParameter
- matches any generic type parameter declaration.- Allowed
modifiers
: none. - Allowed
types
: none.
- Allowed
Group Selectors are provided for convenience, and essentially bundle up sets of individual selectors.
default
- matches everything.- Allowed
modifiers
:private
,protected
,public
,static
,readonly
,abstract
. - Allowed
types
: none.
- Allowed
variableLike
- matches the same asvariable
,function
andparameter
.- Allowed
modifiers
: none. - Allowed
types
: none.
- Allowed
memberLike
- matches the same asproperty
,parameterProperty
,method
,accessor
,enumMember
.- Allowed
modifiers
:private
,protected
,public
,static
,readonly
,abstract
. - Allowed
types
: none.
- Allowed
typeLike
- matches the same asclass
,interface
,typeAlias
,enum
,typeParameter
.- Allowed
modifiers
:abstract
. - Allowed
types
: none.
- Allowed
{
"@typescript-eslint/naming-convention": [
"error",
{ "selector": "variableLike", "format": ["camelCase"] }
]
}
{
"@typescript-eslint/naming-convention": [
"error",
{
"selector": "memberLike",
"modifiers": ["private"],
"format": ["camelCase"],
"leadingUnderscore": "require"
}
]
}
{
"@typescript-eslint/naming-convention": [
"error",
{
"selector": "variable",
"types": ["boolean"],
"format": ["PascalCase"],
"prefix": ["is", "should", "has", "can", "did", "will"]
}
]
}
{
"@typescript-eslint/naming-convention": [
"error",
{
"selector": "variable",
"format": ["camelCase", "UPPER_CASE"]
}
]
}
{
"@typescript-eslint/naming-convention": [
"error",
{
"selector": "typeParameter",
"format": ["PascalCase"],
"prefix": ["T"]
}
]
}
{
"@typescript-eslint/naming-convention": [
"error",
{
"selector": "default",
"format": ["camelCase"]
},
{
"selector": "variable",
"format": ["camelCase", "UPPER_CASE"]
},
{
"selector": "parameter",
"format": ["camelCase"],
"leadingUnderscore": "allow"
},
{
"selector": "memberLike",
"modifiers": ["private"],
"format": ["camelCase"],
"leadingUnderscore": "require"
},
{
"selector": "typeLike",
"format": ["PascalCase"]
}
]
}
If you do not want to enforce naming conventions for anything.