agentleFS
yu-iskwyu-iskw/skill-inspector/.cursor/rules/typescript-coding-convention.mdc

skill-inspector / rules

Cursor rule2 starsChanged 10 months agoApache-2.0View on GitHub

This guide outlines the default-standard coding conventions for TypeScript, based on the Google TypeScript Style Guide, supplemented with best practices from other widely-used TypeScript style guides. Identifiers must use only ASCII letters, digits, underscores (for constants and structured test method names), and the '$' sign. When aliasing an existing symbol, use the format of the existing identifier. For variables, use const, and for class fields, use readonly. Names should not be decorated with information already included in the type. For…

The file

Show its rules and text

We split this typescript-coding-convention.mdc into the rules it gives an agent

65 rules. About 0 are common, 17 are distinctive, and 48 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈17

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 48

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Abbreviations: Treat abbreviations like acronyms as whole words (e.g., loadHttpUrl, not loadHTTPURL).

    line 21
  2. Very specific:

    Dollar sign: Avoid using $ in identifiers, except when aligning with third-party framework conventions (e.g., jquery).

    line 22
  3. Very specific:

    Test names: Test method names may be structured with _ separators (e.g., testX_whenY_doesZ()).

    line 24
  4. Very specific:

    _ prefix/suffix: Do not use _ as a prefix or suffix, or as an identifier by itself.

    line 25
  5. Very specific:

    When aliasing an existing symbol, use the format of the existing identifier.

    line 29
  6. Distinctive:

    Do not use trailing or leading underscores for private properties or methods.

    line 35
  7. Very specific:

    Do not use the opt_ prefix for optional parameters.

    line 36
  8. Very specific:

    Do not prefix interfaces with I (e.g., IUser should be User).

    line 37
  9. Very specific:

    Follow consistent formatting, typically enforced by automated formatters (e.g., Prettier, trunk fmt).

    line 54
  10. Distinctive:

    Indentation: Use 2 spaces for indentation.

    line 56
  11. Distinctive:

    Whitespace: Use spaces around operators, after colons and semicolons, and before open braces for clarity.

    line 58
  12. Distinctive:

    Brace Style: Use 1TBS (the one true brace style) where the opening brace is on the same line.

    line 59
  13. Very specific:

    Quotes: Prefer single quotes (') unless escaping.

    line 60
  14. Very specific:

    JSDoc vs Comments: Use / JSDoc */ for documentation (for users of the code) and // line comments or /* block comments */ for implementation …

    line 64
  15. Distinctive:

    Documentation Scope: Document all top-level exports of modules.

    line 66
  16. Very specific:

    Parameter Property Comments: Use @param JSDoc annotation to document parameter properties in constructors.

    line 68
  17. Very specific:

    Call Site Comments: If needed, document parameters at call sites inline using block comments (e.g., new Foo(/* arg1= */ value)).

    line 69
  18. Very specific:

    Placement: Place documentation prior to decorators (JSDoc block should come before the @Decorator).

    line 70
  19. Very specific:

    Avoid @override: Do not use @override in TypeScript source code.

    line 71
  20. Very specific:

    private vs #private: Prefer private for class members intended to be internal.

    line 78
  21. Very specific:

    public Modifier: Never use the public modifier explicitly, except when declaring non-readonly public parameter properties in …

    line 79
  22. Distinctive:

    Conciseness: Keep constructors concise.

    line 86
  23. Very specific:

    Parameter properties: Use parameter properties (e.g., constructor(private readonly myService: MyService)) for concise declarations instead …

    line 91
  24. Distinctive:

    Field initializers: If a class member is not a parameter, initialize it where it's declared, which can sometimes remove the need for a …

    line 92
  25. Very specific:

    Always use the lowercase primitive types: string, boolean, number, symbol, bigint.

    line 97
  26. Very specific:

    Never invoke wrapper types as constructors (e.g., new Number(5)).

    line 98
  27. Distinctive:

    Avoid implicit type coercion.

    line 106
  28. Very specific:

    Use String() and Boolean() functions, string template literals, or !!

    line 107
  29. Very specific:

    Use Number() to parse numeric values and explicitly check for NaN unless parsing is guaranteed to succeed.

    line 108
  30. Very specific:

    Do not use unary plus (+) to coerce strings to numbers.

    line 109
  31. Very specific:

    Do not use parseInt or parseFloat to parse numbers, except for non-base-10 strings, and always validate input before using them.

    line 110
  32. Very specific:

    Always use const or let to declare variables.

    line 114
  33. Very specific:

    Use try-catch blocks for error handling.

    line 119
  34. Very specific:

    Use for...of for iterating over arrays and other iterable objects.

    line 123
  35. Very specific:

    Use for...in for iterating over object keys, but be cautious of inherited properties.

    line 124
  36. Very specific:

    Prefer array methods (map, filter, reduce) over manual loops.

    line 125
  37. Very specific:

    Use the spread operator (...) for array and object cloning/merging.

    line 129
  38. Very specific:

    Always use curly braces {} for if, for, while, do-while statements, even for single-line bodies.

    line 133
  39. Very specific:

    Always include a default case.

    line 137
  40. Very specific:

    Use break for non-fall-through cases.

    line 138
  41. Very specific:

    Use strict equality operators (=== and !==) instead of loose ones (== and !=).

    line 142
  42. Very specific:

    Arrow functions: Prefer arrow functions for anonymous functions and when this context binding is important.

    line 147
  43. Distinctive:

    Expression bodies: Use expression bodies for arrow functions when appropriate for conciseness.

    line 148
  44. Very specific:

    Always use semicolons ; explicitly.

    line 152
  45. Very specific:

    When a TypeScript error cannot be mitigated, use @ts-expect-error as a last resort to suppress it.

    line 157
  46. Distinctive:

    Prefer type guards over assertions for safer type narrowing.

    line 162
  47. Very specific:

    Place documentation comments (JSDoc) *before* decorators.

    line 166
  48. Very specific:

    Use ES modules (import/export) consistently.

    line 172
  49. Distinctive:

    Import Paths: Use relative paths for files within the same project.

    line 173
  50. Very specific:

    Avoid TypeScript namespace keyword.

    line 174
  51. Distinctive:

    Prefer named exports over default exports for better traceability and refactoring.

    line 178
  52. Distinctive:

    Module vs. Destructuring: Use destructuring imports for specific members.

    line 183
  53. Very specific:

    Avoid wildcard (*) imports as they can increase bundle size and prevent effective tree-shaking.

    line 186
  54. Very specific:

    When a file exports a component for a framework (e.g., React) that requires PascalCase for components, use PascalCase for the filename to …

    line 195
  55. Very specific:

    Test files: Use .test.ts or .spec.ts suffix for unit test files (e.g., utils.test.ts, userService.spec.ts).

    line 196
  56. Very specific:

    Use the satisfies operator to combine strict type-checking and immutability for constants, ensuring they conform to a specific type while …

    line 218
  57. Very specific:

    Prefer undefined in general for explicit unavailability or missing values.

    line 230
  58. Very specific:

    Use truthy checks for objects being null or undefined (e.g., if (error)).

    line 231
  59. Very specific:

    Use == null or != null (not === or !==) to check for both null and undefined on primitives.

    line 232
  60. Very specific:

    Prefer interface for declaring object shapes due to their extendability.

    line 236
  61. Very specific:

    When performing declaration merging (e.g., extending third-party library types), use interface.

    line 237
  62. Distinctive:

    Avoid creating APIs that have return type only generics.

    line 245
  63. Distinctive:

    Prefer to accept at least one argument and return data.

    line 259
  64. Distinctive:

    Not have side effects (pure), or if they do, clearly separate business logic from infrastructure concerns.

    line 260
  65. Distinctive:

    When applicable, use discriminated union types for function arguments to eliminate optional properties, reducing complexity and ensuring …

    line 272

≈ “Common” is an estimate: at least 1 in 5 of the 6,668 Cursor rule files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
typescript-coding-convention.mdc
---
globs: **/*.ts
alwaysApply: false
---
# TypeScript Style Guide

This guide outlines the default-standard coding conventions for TypeScript, based on the Google TypeScript Style Guide, supplemented with best practices from other widely-used TypeScript style guides.

## 1. Syntax

### 1.1. Identifiers

Identifiers must use only ASCII letters, digits, underscores (for constants and structured test method names), and the '$' sign.

| Style          | Category                                                           |
| -------------- | ------------------------------------------------------------------ |
| `UpperCamelCase` | class / interface / type / enum / decorator / type / parameters    |
| `lowerCamelCase` | variable / parameter / function / method / property / module alias |
| `CONSTANT_CASE` | global constant values, including enum values                      |

* **Abbreviations**: Treat abbreviations like acronyms as whole words (e.g., `loadHttpUrl`, not `loadHTTPURL`).
* **Dollar sign**: Avoid using `$` in identifiers, except when aligning with third-party framework conventions (e.g., `jquery`).
* **Type parameters**: May use a single upper case character (`T`) or `UpperCamelCase`.
* **Test names**: Test method names may be structured with `_` separators (e.g., `testX_whenY_doesZ()`).
* **`_` prefix/suffix**: Do not use `_` as a prefix or suffix, or as an identifier by itself.

### 1.2. Aliases

When aliasing an existing symbol, use the format of the existing identifier. For variables, use `const`, and for class fields, use `readonly`.

### 1.3. Naming Style

Names should not be decorated with information already included in the type.

* Do not use trailing or leading underscores for private properties or methods.
* Do not use the `opt_` prefix for optional parameters.
* Do not prefix interfaces with `I` (e.g., `IUser` should be `User`).
* Prefix booleans with `is` or `has` for clarity (e.g., `isLoggedIn`, `hasPermission`).

### 1.4. File Encoding

For non-ASCII characters, use the actual Unicode character (e.g., `∞`). For non-printable characters, the equivalent hex or Unicode escapes (e.g., `\u221e`) can be used along with an explanatory comment.

```ts
// Perfectly clear, even without a comment.
const units = "μs";

// Use escapes for non-printable characters.
const output = "\ufeff" + content; // byte order mark
```

### 1.5. Source Code Formatting

Follow consistent formatting, typically enforced by automated formatters (e.g., Prettier, `trunk fmt`).

* **Indentation**: Use 2 spaces for indentation.
* **Line Length**: Aim for 100 characters, but allow flexibility for readability.
* **Whitespace**: Use spaces around operators, after colons and semicolons, and before open braces for clarity.
* **Brace Style**: Use 1TBS (the one true brace style) where the opening brace is on the same line.
* **Quotes**: Prefer single quotes (`'`) unless escaping.

### 1.6. Comments & Documentation

* **JSDoc vs Comments**: Use `/** JSDoc */` for documentation (for users of the code) and `// line comments` or `/* block comments */` for implementation details (for developers working on the code itself).
* **JSDoc Rules**: Generally follow JavaScript style guide rules for JSDoc. Omit comments that are redundant with TypeScript types.
* **Documentation Scope**: Document all top-level exports of modules. Document properties and methods (exported/public or not) whose purpose is not immediately obvious.
* **Adding Information**: Make comments that add actual information, avoiding mere restatement of names or types.
* **Parameter Property Comments**: Use `@param` JSDoc annotation to document parameter properties in constructors.
* **Call Site Comments**: If needed, document parameters at call sites inline using block comments (e.g., `new Foo(/* arg1= */ value)`).
* **Placement**: Place documentation prior to decorators (JSDoc block should come before the `@Decorator`).
* **Avoid `@override`**: Do not use `@override` in TypeScript source code.

## 2. Language Rules

### 2.1. Visibility

* **Limiting Visibility**: Limit symbol visibility as much as possible.
* **`private` vs `#private`**: Prefer `private` for class members intended to be internal. Avoid `#private` fields due to emit size and performance regressions.
* **`public` Modifier**: Never use the `public` modifier explicitly, except when declaring non-readonly public parameter properties in constructors, as TypeScript symbols are public by default.
* **Properties outside lexical scope**: For properties used outside the lexical scope of their containing class (e.g., in templates), prefer `public` visibility. `protected` may also be used as needed.

### 2.2. Constructors

* **Parentheses**: Constructor calls must use parentheses, even when no arguments are passed (e.g., `new Foo()`).
* **Unnecessary Constructors**: It is unnecessary to provide an empty constructor or one that simply delegates to its parent class. Constructors with parameter properties, modifiers, or parameter decorators should not be omitted.
* **Conciseness**: Keep constructors concise; delegate complex logic to methods.

### 2.3. Class Members

* **`readonly`**: Mark properties that are never reassigned outside of the constructor with the `readonly` modifier.
* **Parameter properties**: Use parameter properties (e.g., `constructor(private readonly myService: MyService)`) for concise declarations instead of manually assigning parameters to class fields.
* **Field initializers**: If a class member is not a parameter, initialize it where it's declared, which can sometimes remove the need for a constructor.
* **Getters and Setters**: May be used. The getter method must be a pure function. Accessors can restrict visibility of internal details. Avoid "pass-through" accessors; make the property public instead.

### 2.4. Primitive Types & Wrapper Classes

* Always use the lowercase primitive types: `string`, `boolean`, `number`, `symbol`, `bigint`. Never use `String`, `Boolean`, `Number`, `Symbol`, `BigInt`, or `Object` (use `{}` or `object` instead).
* Never invoke wrapper types as constructors (e.g., `new Number(5)`).

### 2.5. Array Constructor

TypeScript code must not use the `Array()` constructor, with or without `new`. Always use bracket notation to initialize arrays (e.g., `[1, 2, 3]`), or `Array.from()` to initialize an array with a specific size.

### 2.6. Type Coercion

* Avoid implicit type coercion. Use explicit conversions when necessary.
* Use `String()` and `Boolean()` functions, string template literals, or `!!` to coerce types.
* Use `Number()` to parse numeric values and explicitly check for `NaN` unless parsing is guaranteed to succeed.
* Do not use unary plus (`+`) to coerce strings to numbers.
* Do not use `parseInt` or `parseFloat` to parse numbers, except for non-base-10 strings, and always validate input before using them.

### 2.7. Variables

* Always use `const` or `let` to declare variables. Use `const` by default, unless a variable needs to be reassigned. Never use `var`.
* `const` and `let` are block-scoped, which helps avoid common JavaScript bugs associated with `var`'s function scoping.

### 2.8. Exceptions

* Use `try-catch` blocks for error handling. Throw `Error` instances or custom error classes.

### 2.9. Iterating Objects/Containers

* Use `for...of` for iterating over arrays and other iterable objects.
* Use `for...in` for iterating over object keys, but be cautious of inherited properties.
* Prefer array methods (`map`, `filter`, `reduce`) over manual loops.

### 2.10. Spread Operator

Use the spread operator (`...`) for array and object cloning/merging.

### 2.11. Control Flow Statements & Blocks

Always use curly braces `{}` for `if`, `for`, `while`, `do-while` statements, even for single-line bodies.

### 2.12. Switch Statements

* Always include a `default` case.
* Use `break` for non-fall-through cases.

### 2.13. Equality Checks

* Use strict equality operators (`===` and `!==`) instead of loose ones (`==` and `!=`).
* Exception: `obj == null` is allowed to check for `null || undefined`.

### 2.14. Function Declarations & Expressions

* **Arrow functions**: Prefer arrow functions for anonymous functions and when `this` context binding is important.
* **Expression bodies**: Use expression bodies for arrow functions when appropriate for conciseness.

### 2.15. Automatic Semicolon Insertion

Always use semicolons `;` explicitly.

### 2.16. `@ts-ignore` and `@ts-expect-error`

* Avoid `@ts-ignore`.
* When a TypeScript error cannot be mitigated, use `@ts-expect-error` as a last resort to suppress it. Always use `@ts-expect-error` with a clear description explaining why it is necessary.

### 2.17. Type and Non-nullability Assertions

* Type assertions (`as Type`) and non-nullability assertions (`!`) are unsafe and should be used sparingly, only when absolutely certain of the type, or as an exception (e.g., third-party library type mismatches, dereferencing `unknown`) with strong rationale.
* Prefer type guards over assertions for safer type narrowing.

### 2.18. Decorators

Place documentation comments (`JSDoc`) *before* decorators.

## 3. Source Organization

### 3.1. Modules

* Use ES modules (`import`/`export`) consistently.
* **Import Paths**: Use relative paths for files within the same project. Avoid deep nesting.
* Avoid TypeScript `namespace` keyword.

### 3.2. Exports

* Prefer named exports over default exports for better traceability and refactoring.
* Avoid mutable exports.

### 3.3. Imports

* **Module vs. Destructuring**: Use destructuring imports for specific members.
* **Renaming**: Rename imports to avoid naming conflicts if necessary.
* `import type`: Use `import type` for importing only types to ensure they are erased at compile time. This improves tree-shaking and minimizes dependencies.
* Avoid wildcard (`*`) imports as they can increase bundle size and prevent effective tree-shaking.

### 3.4. Organize By Feature

Structure your project by feature or domain, encapsulating related components, models, services, and routes within their own directories.

### 3.5. File Naming

* Name files with `camelCase` (e.g., `utils.ts`, `map.ts`).
* When a file exports a component for a framework (e.g., React) that requires `PascalCase` for components, use `PascalCase` for the filename to match (e.g., `Accordion.tsx`, `MyControl.tsx`).
* **Test files**: Use `.test.ts` or `.spec.ts` suffix for unit test files (e.g., `utils.test.ts`, `userService.spec.ts`).

## 4. Type System

### 4.1. Type Inference

Allow TypeScript to infer types when clear. Explicitly type when inference is ambiguous or for API boundaries, or when narrowing a type (e.g., `useState<UserRole>('admin')`).

### 4.2. Data Immutability

Immutability should be a key principle. Wherever possible, data should remain immutable by leveraging types like `Readonly` and `ReadonlyArray`. When performing data processing, always return new arrays, objects, or other reference-based data structures.

### 4.3. Required & Optional Object Properties

Strive to have the majority of object properties required and use optional properties sparingly. If many optional properties are unavoidable, utilize discriminated union types.

### 4.4. Discriminated Unions

Embrace discriminated unions to model complex data structures and improve type safety. They remove optional object properties, enable exhaustiveness checks (e.g., in `switch` statements), avoid flag variables, and improve code clarity.

### 4.5. Type-Safe Constants With Satisfies

Use the `satisfies` operator to combine strict type-checking and immutability for constants, ensuring they conform to a specific type while preserving narrowed inferred types.

### 4.6. Template Literal Types

Embrace template literal types to create precise and type-safe string constructs (e.g., for versioning, API endpoints, internationalization keys, CSS utilities, database queries), providing better type safety and autocompletion.

### 4.7. `any` Type and `unknown` Type

`any` type must not be used as it bypasses type checking and can mask errors. When dealing with ambiguous data types, use `unknown`, which is the type-safe counterpart of `any`. Narrow the type of `unknown` values using type guards or type assertions before using them.

### 4.8. Null vs. Undefined

* Prefer `undefined` in general for explicit unavailability or missing values. Use `null` where it's part of an external API or conventional (e.g., Node.js callbacks).
* Use truthy checks for objects being `null` or `undefined` (e.g., `if (error)`).
* Use `== null` or `!= null` (not `===` or `!==`) to check for both `null` and `undefined` on primitives.

### 4.9. Interfaces vs. Type Aliases

* Prefer `interface` for declaring object shapes due to their extendability. Use `type` for unions, intersections, mapped types, and primitive aliases.
* When performing declaration merging (e.g., extending third-party library types), use `interface`.

### 4.10. Array Types

Define array types using generic syntax (e.g., `Array<string>` or `ReadonlyArray<string>`) instead of `string[]`.

### 4.11. Return Type Only Generics

Avoid creating APIs that have return type only generics. Always explicitly specify generics when working with existing APIs that have them.

### 4.12. Services & Types Generation

For external services (e.g., REST, GraphQL, MQ), generate types from their contracts (Swagger, schemas). Avoid manually declaring and maintaining types, as they can easily fall out of sync.

## 5. Functions

### 5.1. General Principles

Functions should:

* Have a single responsibility.
* Be stateless, where the same input arguments return the same value every time (pure functions).
* Prefer to accept at least one argument and return data.
* Not have side effects (pure), or if they do, clearly separate business logic from infrastructure concerns.

### 5.2. Single Object Argument

To keep functions readable and easily extensible, strive to use a single object as the function argument instead of multiple positional arguments. Exceptions include simple functions with a single primitive argument or when implementing currying.

### 5.3. Required & Optional Arguments

Strive to have the majority of arguments required and use optional arguments sparingly. If a function becomes too complex with many optional arguments, it should likely be broken into smaller, more focused functions.

### 5.4. Arguments as Discriminated Type

When applicable, use discriminated union types for function arguments to eliminate optional properties, reducing complexity and ensuring that only required properties are passed depending on the use case.

### 5.5. Return Types

Requiring explicit return types improves safety, catches errors early, and helps with long-term maintainability. As a rule of thumb, be explicit on the outside (API boundaries) and implicit on the inside (within private implementation details).

## 6. Consistency

For any style question not definitively covered by this guide, be consistent with existing code in the same file or directory. If that doesn't resolve the question, consider emulating other files in the same directory.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.