TypeScript
Provide best-practice coding conventions and generate standards-compliant TypeScript code.
Provide best-practice coding conventions and generate standards-compliant TypeScript code.
Real data. Real impact.
Emerging
Developers
Per week
Open source
Skills give you superpowers. Install in 30 seconds.
Activation: This skill activates whenever the user says or implies TypeScript. It responds with standards-compliant TypeScript code and can explain any rule on demand.
"strict": true in tsconfig.json. This turns on noImplicitAny,
strictNullChecks, strictFunctionTypes, and more.any: Treat every use of any as tech debt. Prefer unknown when the type is
truly not known, or use generics.const over let; prefer readonly properties and
ReadonlyArray<T> / Readonly<T> utility types.| Construct | Convention | Example |
|---|---|---|
| Variable / Function | | , |
| Boolean variable | with prefix | , , |
| Constant (module) | | , |
| Constant (local) | | |
| Class | | , |
| Interface | | , |
| Type alias | | , |
| Enum | | , |
| Enum member | | , |
| Generic parameter | Single uppercase letter or descriptive | , , |
| File name | | , |
| Test file name | or | |
| React component file | | |
| Private field | (no prefix) | |
I (e.g., IUserUser).Type or Interface.i, j) or generic type parameters (T, K, V).IO, ID); 3+ characters use PascalCase
(Http, Xml, Api).interface for Object Shapes// ✅ Good — use interface for object shapes interface User { readonly id: string; name: string; email: string; }// ✅ Good — use type for unions, intersections, mapped types type Status = 'active' | 'inactive' | 'suspended'; type Result<T> = Success<T> | Failure;
type vs interfaceUse when … | Use when … |
|---|---|
| Defining the shape of an object or class | Creating union or intersection types |
| You want declaration merging | Using mapped / conditional types |
| Extending other interfaces | Aliasing primitive or tuple types |
type over interface for function signatures:
type Comparator<T> = (a: T, b: T) => number;
Record<K, V> instead of { [key: string]: V }.Partial<T>, Pick<T, K>, Omit<T, K>, Required<T>) to derive types
rather than duplicating.// ✅ Good — string enum for readability and debuggability enum Direction { Up = 'UP', Down = 'DOWN', Left = 'LEFT', Right = 'RIGHT', }
const Enum or Union Types// ✅ Good — const enum for zero-runtime-cost enums (no reverse mapping needed) const enum HttpMethod { Get = 'GET', Post = 'POST', Put = 'PUT', Delete = 'DELETE', }// ✅ Also good — string literal union (simpler, tree-shakeable) type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
// ✅ Good const maxRetries = 3; const baseUrl = 'https://api.example.com'; let currentAttempt = 0;// ❌ Bad — using var var legacyValue = 'old';
// ❌ Bad — using let when value never changes let neverReassigned = 42;
const unless the variable needs reassignment; then use let.var.// ✅ Good const { name, age } = user;// ❌ Bad const name = user.name; const age = user.age;
as const for immutable literal objects / arrays:
const ROUTES = { home: '/', about: '/about', contact: '/contact', } as const;
// ✅ Good — explicit return type for public functions function calculateTotal(items: CartItem[]): number { return items.reduce((sum, item) => sum + item.price * item.quantity, 0); }// ✅ Good — arrow function for callbacks / short lambdas const double = (n: number): number => n * 2;
// ✅ Good — use default parameters instead of optional + fallback function createUser(name: string, role: Role = Role.Viewer): User { // ... }
// ✅ Good interface CreateUserOptions { name: string; email: string; role?: Role; department?: string; } function createUser(options: CreateUserOptions): User { ... }// ❌ Bad — too many positional parameters function createUser(name: string, email: string, role: Role, dept: string): User { ... }
arguments; use rest parameters (...args) instead.Function type. Use specific function signatures.// ✅ Good function getDiscount(user: User): number { if (!user.isPremium) return 0; if (user.yearsActive < 2) return 5; return 10; }
// ✅ Good class UserService { private readonly repository: UserRepository;constructor(repository: UserRepository) { this.repository = repository; }
async findById(id: string): Promise<User | undefined> { return this.repository.get(id); } }
readonly for properties that should not change after construction.public, protected, private) on every member._. TypeScript's private keyword is sufficient.# (ES private fields) when true runtime privacy is required.class Logger { constructor(private readonly prefix: string) {} }
class UserRepositoryImpl implements UserRepository { ... }
// ✅ Good — named exports export function parseConfig(raw: string): Config { ... } export interface Config { ... }// ✅ Good — re-export barrel file (index.ts) export { parseConfig } from './parse-config'; export type { Config } from './parse-config';
import type / export type for type-only imports to help bundlers tree-shake:
import type { User } from './user';
node:fs, node:path)react, lodash)@/utils, @/components)../)./)import * as) unless re-exporting a module namespace.index.ts) thin — do not add logic.// ✅ Good — descriptive when intent is ambiguous function merge<TTarget, TSource>(target: TTarget, source: TSource): TTarget & TSource { return { ...target, ...source }; }// ✅ Good — single-letter when intent is obvious function identity<T>(value: T): T { return value; }
// ✅ Good — constrained generics function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; }
T, U, K, V) for simple generics.T (TKey, TValue, TResult) when there are
multiple type parameters or the purpose is not obvious.<T extends SomeType>).Array<T>, Promise<T>, Map<K, V>) over their shorthand where
readability benefits.// ✅ Good — custom error class class AppError extends Error { constructor( message: string, public readonly code: string, public readonly statusCode: number = 500, ) { super(message); this.name = 'AppError'; } }// ✅ Good — typed error handling function parseJson<T>(raw: string): T { try { return JSON.parse(raw) as T; } catch (error) { throw new AppError(, 'PARSE_ERROR', 400, ); } }Failed to parse JSON: ${error instanceof Error ? error.message : String(error)}
catch blocks).Error for domain-specific errors.catch variable as unknown (default in TS 4.4+) and narrow before use.type Result<T, E = Error> = { ok: true; value: T } | { ok: false; error: E };
finally blocks or use disposable patterns.// ✅ Good — async/await async function fetchUser(id: string): Promise<User> { const response = await httpClient.get<User>(`/users/${id}`); return response.data; }// ✅ Good — parallel execution async function fetchDashboard(userId: string): Promise<Dashboard> { const [user, posts, notifications] = await Promise.all([ fetchUser(userId), fetchPosts(userId), fetchNotifications(userId), ]); return { user, posts, notifications }; }
async/await over .then() chains for readability.Promise<T>.Promise.all() for independent concurrent operations.Promise.allSettled() when failures should not abort sibling operations.new Promise() when an async function suffices (avoid the explicit-construction
antipattern).async void functions — they cannot be await-ed and swallow errors.
Exception: event handlers where the framework requires void.for...of with await for sequential async iteration, not forEach./** * Calculate the compound interest for a principal amount. * * @param principal - The initial amount of money. * @param rate - The annual interest rate (decimal, e.g. 0.05 for 5%). * @param times - Number of times interest is compounded per year. * @param years - Number of years the money is invested. * @returns The total amount after compound interest. * * @example * ```typescript * const total = compoundInterest(1000, 0.05, 12, 10); * // total ≈ 1647.01 * ``` */ function compoundInterest( principal: number, rate: number, times: number, years: number, ): number { return principal * Math.pow(1 + rate / times, times * years); }
/** */) for all public APIs, exported functions, classes, interfaces, and types.@param, @returns, @throws, and @example tags where applicable.// TODO: with a ticket number for planned improvements.// FIXME: for known issues that need resolution.// HACK: for workarounds that should be revisited.') for strings; backticks (`) for template literals.// ✅ Good — braces always, even for single-line if if (isValid) { process(); }// ❌ Bad if (isValid) process();
// ✅ Good — trailing comma const config = { host: 'localhost', port: 3000, debug: true, };
// ✅ Good — consistent object shorthand const name = 'Alice'; const user = { name, age: 30 };
{ "semi": true, "singleQuote": true, "trailingComma": "all", "printWidth": 100, "tabWidth": 2, "arrowParens": "always" }
@typescript-eslint/eslint-plugin and
@typescript-eslint/parser for linting.// ✅ Good — optional chaining const city = user?.address?.city;// ✅ Good — nullish coalescing const displayName = user.nickname ?? user.name ?? 'Anonymous';
// ✅ Good — explicit null checks for critical paths function getUser(id: string): User { const user = repository.findById(id); if (user === undefined) { throw new AppError(, 'USER_NOT_FOUND', 404); } return user; }User not found: ${id}
strictNullChecks (included in strict mode).undefined over null as the "absence of value" indicator, unless interacting with
external APIs that use null.?.) and nullish coalescing (??) instead of manual checks.!) unless you can prove the value is always defined (add a
comment explaining why).satisfies operator (TS 5.0+) to validate types without widening.// ✅ Good — type guard function function isUser(value: unknown): value is User { return ( typeof value === 'object' && value !== null && 'id' in value && 'name' in value ); }// ✅ Good — discriminated union interface Success<T> { kind: 'success'; data: T; } interface Failure { kind: 'failure'; error: Error; } type Result<T> = Success<T> | Failure;
function handleResult<T>(result: Result<T>): T { switch (result.kind) { case 'success': return result.data; case 'failure': throw result.error; } }
is keyword) over type assertions (as).as const for literal narrowing, not as SpecificType.kind / type field for state machines and result types.as unknown as T double assertions — they are almost always a design smell.satisfies for type validation without assertion:
const palette = { red: [255, 0, 0], green: '#00ff00', } satisfies Record<string, string | number[]>;
// ✅ Good — functional component with explicit props type interface UserCardProps { readonly user: User; readonly onSelect?: (userId: string) => void; }export function UserCard({ user, onSelect }: UserCardProps): React.ReactElement { const handleClick = useCallback(() => { onSelect?.(user.id); }, [onSelect, user.id]);
return ( <div className="user-card" onClick={handleClick} role="button" tabIndex={0}> <h3>{user.name}</h3> <p>{user.email}</p> </div> ); }
interface (e.g., UserCardProps), not inline.readonly.React.FC — it is discouraged since React 18.React.ReactElement or React.ReactNode as return type annotation.useMemo, stable callbacks with useCallback.// ✅ Good — descriptive test structure describe('UserService', () => { describe('findById', () => { it('should return the user when the id exists', async () => { const user = await service.findById('user-1'); expect(user).toEqual(expect.objectContaining({ id: 'user-1' })); });it('should return undefined when the id does not exist', async () => { const user = await service.findById('non-existent'); expect(user).toBeUndefined(); });}); });
__tests__ directory.*.test.ts or *.spec.ts.expect calls are okay if they test one logical
assertion).jest.fn() or equivalent for spies/mocks; type them properly:
const mockFetch = jest.fn<Promise<User>, [string]>();
tsconfig.json (Baseline){ "compilerOptions": { "target": "ES2022", "module": "NodeNext", "moduleResolution": "NodeNext", "lib": ["ES2022"], "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "resolveJsonModule": true, "isolatedModules": true, "declaration": true, "declarationMap": true, "sourceMap": true, "noUnusedLocals": true, "noUnusedParameters": true, "noFallthroughCasesInSwitch": true, "noUncheckedIndexedAccess": true, "exactOptionalPropertyTypes": true } }
| Rule | Setting |
|---|---|
| |
| (for exported functions) |
| |
| |
| |
| |
| |
| |
| (configured per construct) |
Simply mention TypeScript in your conversation — the skill activates automatically.
Example prompts:
| Prompt | Skill Response |
|---|---|
| "Write a TypeScript function to merge two objects deeply" | Generates a fully typed, standards-compliant function following all rules above. |
| "Review my TypeScript code for style issues" | Analyses the provided code against this guide and suggests improvements. |
| "Convert this JavaScript to TypeScript" | Converts code, adds strict types, interfaces, and follows all naming / formatting conventions. |
| "What's the TypeScript best practice for error handling?" | Explains the Result pattern, custom error classes, and typed catch blocks per §10. |
| "Create a TypeScript React component for a data table" | Generates a well-typed functional component following §16 React rules with proper props interface. |
| "Set up a new TypeScript project" | Provides , ESLint config, Prettier config, and project structure following §18. |
You can ask about any specific section:
No automatic installation available. Please visit the source repository for installation instructions.
View Installation Instructions1,500+ AI skills, agents & workflows. Install in 30 seconds. Part of the Torly.ai family.
© 2026 Torly.ai. All rights reserved.