Best-practice naming conventions for TypeScript via ESLint.
Documentation • Install • Usage • Rule overview
Naming conventions in software development offer numerous benefits:
- Readability: Consistent naming makes code easier to read and understand, reducing cognitive load when scanning files.
- Maintainability: Enforces best practices, improving code quality and consistency across the codebase.
- Code Review and Collaboration: Eliminates debates on naming styles, allowing reviewers to focus on logic and speeding up the process.
- Code Uniformity: Uniform naming promotes collective code ownership, as code looks the same regardless of who wrote it.
- Aesthetics: Provides functional benefits and aesthetic appeal, with a visually pleasing and harmonious structure.
This config makes naming predictable and review-friendly by enforcing a coherent set of rules that scale well across:
- Apps and Libraries
- Frontend and Backend
- Monorepos and Multi-team codebases
It is intentionally BYO parser setup — it ships rules only and does not assume your repo layout or parserOptions.project.
Comprehensive documentation with detailed rule explanations and examples is available at drsmile444.github.io/eslint-config-naming.
Key resources:
- Getting Started Guide — Installation and configuration
- Rule Matrix — Complete rule matrix with examples
- Changelog — Version history and updates
This configuration enforces consistent naming patterns across TypeScript constructs:
| Category | Enforced Style | Example |
|---|---|---|
| Types | PascalCase, no I/T prefixes |
ApiResponse, UserData |
| Classes | PascalCase | HttpClient, DataService |
| Interfaces | PascalCase | Config, Options |
| Enums | PascalCase (name), UPPER_CASE (members) | enum Status { ACTIVE, INACTIVE } |
| Variables | camelCase or UPPER_CASE | userName, MAX_RETRIES |
| Booleans | Prefixed with is|should|has|can|did|will |
isValid, hasAccess |
| Functions | camelCase | calculateTotal(), fetchData() |
| Class Members (public) | camelCase or snake_case | userCount, api_key |
| Class Members (protected) | camelCase (no underscore) | internalState |
| Generic Types | Single letter or T-prefixed | T, TData, TKey |
Abbreviations and single-letter variables are restricted — only x, y, z are allowed for coordinates. Descriptive names like errorMessage and index are enforced over vague shortcuts like err, str, or i.
| ❌ Incorrect | ✅ Correct |
|---|---|
// Type prefixes
interface IApiResponse {}
type TUserData = {};
// Wrong casing
enum status {
active,
inactive,
}
class userService {}
// Variables
const Myvariable = 'value';
const my_constant = 'constant';
const ready = true;
// Abbreviations
const str = 'message';
// Generics
type Cache<Key, val> = Map<Key, val>; |
// Clean type names
interface ApiResponse {}
type UserData = {};
// Proper casing
enum Status {
ACTIVE,
INACTIVE,
}
class UserService {}
// Variables
const myVariable = 'value';
const MY_CONSTANT = 'constant';
const isReady = true;
// Descriptive names
const message = 'message';
// Proper generics
type Cache<TKey, TValue> = Map<TKey, TValue>; |
Note: This is a high-level overview and most rules have additional conditions and exceptions. See the Rule Matrix for complete details and examples.
npm i -D eslint typescript typescript-eslintnpm i -D eslint-config-namingFor other installation options, see Flat Config Usage.
// eslint.config.js
import tseslint from 'typescript-eslint';
import namingConfig from 'eslint-config-naming';
export default [
// Your TS wiring (BYO parserOptions)
{
files: ['**/*.ts', '**/*.tsx'],
languageOptions: {
parser: tseslint.parser,
parserOptions: {
project: ['./tsconfig.json'],
tsconfigRootDir: import.meta.dirname,
},
},
plugins: {
'@typescript-eslint': tseslint.plugin,
},
},
// Naming rules
...namingConfig,
];Use the legacy shareable entry:
eslint-config-naming/legacy
// .eslintrc.cjs
module.exports = {
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
extends: ['eslint-config-naming/legacy'],
parserOptions: {
project: './tsconfig.json',
},
};Internally, the config is built as a set of small rule fragments grouped by selector:
memberLikerules (public/private/protected/static/readonly)variablesrules (default, const/global, destructured, boolean prefixes, *Component)typesrules (class, interface, enum, typeLike, enumMember, generics)parametersrules (base, destructured)functionsrules (including final camelCase enforcement)- quoted-key escape hatch (
requiresQuotes→ ignored)
They are combined in a single @typescript-eslint/naming-convention rule entry list, preserving precedence.
This is a quick “what it enforces” list. For full details (and good/bad examples), check the Rule Matrix..
class: PascalCaseinterface: PascalCase and forbidsI*/T*prefixestypeAlias: PascalCase and forbidsI*/T*prefixestypeParameter: Single uppercase letters (T,U,V,K) or prefixed descriptive names (TData,KKey,VValue) or numeric subscripts (T1,T2,K1, etc.)enum: PascalCase, forbids plural-ish names (likeStatuses) andI*/T*enumMember: UPPER_CASE
public static: camelCase, PascalCase or UPPER_CASEprivate static: camelCase, PascalCase or UPPER_CASE, no leading_readonly: UPPER_CASE, camelCase, or snake_casepublic: camelCase or snake_caseprivate: camelCase, no leading_private readonly: camelCase or UPPER_CASE, no leading_protected: camelCase, no leading_
- default: camelCase or UPPER_CASE, abbreviation restrictions apply
const+global: allows PascalCase (in addition to camelCase/UPPER_CASE), abbreviation restrictions apply- destructured: allows PascalCase, camelCase, snake_case
- booleans: camelCase with required prefix
is|are|should|has|can|did|will - destructured booleans: no prefix requirement (interop friendly)
*Component: allows PascalCase if variable name ends withComponent
- default: camelCase, abbreviation restrictions apply
- destructured: allows PascalCase, camelCase, snake_case
- exported/global: camelCase or PascalCase, abbreviation restrictions apply
- default (catch-all): camelCase, abbreviation restrictions apply
objectLiteralProperty: camelCase, snake_case, PascalCase, or UPPER_CASE (flexible to support various use cases including constant objects)
If a name requires quotes (e.g. HTTP headers, data contracts), it's ignored.
Automatically bans common abbreviations and anti-patterns:
- Single-letter names:
i,j,k,e→ useindex,itemIndex,error - Vague abbreviations:
str,num,arr,obj→ usestring,number,array,object - Intent-hiding names:
data,info,tmp→ usepayload,metadata,temporary - Ambiguous abbreviations:
res,req,dir,cfg→ useresponse,request,directory,config
Well-known technical terms like id, url, api, json, html, uuid are allowed.
See Abbreviation Restrictions docs for the complete list and customization options.
This project treats rule and packaging changes as user-facing. Before submitting a change that affects rules, exports, or packaging, please follow the project's update policy (full text in Update Policy Docs). In short, every rule-related change must include:
- Source changes (
src/) implementing the rule or selector. - Tests (
tests/) covering both accepted and rejected examples. - Snippets (
tests/snippets/...) for positive/negative cases where applicable. - Documentation updates in
docs/explaining the rule and examples. - README updates if usage or exports change.
- Package metadata changes (
package.json) such as version bump and export adjustments.
See docs/policies/update-policy.md for the complete checklist, versioning rules, and CI expectations.
MIT © 2025-Present Dmytro Vakulenko 🇺🇦