Typescript coding
Skill nguyenthdat/opencode-manager/.opencode/skills/typescript-coding
Comprehensive idiomatic TypeScript/JavaScript guidance: 171 prioritized rules across 14 categories. Use when writing, reviewing, refactoring, optimizing, or debugging TypeScript or JavaScript (`.ts`, `.tsx`, `.js`, `.jsx`, `tsconfig.json`, `package.json`). Preserve the target project's declared TypeScript version, module system, and strictness settings; apply `satisfies`, `verbatimModuleSyntax`, `noUncheckedIndexedAccess`, and other modern-config guidance only when the project's toolchain supports them.From its SKILL.md
npx -y skills add nguyenthdat/opencode-manager --skill typescript-codingAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- 20 days oldThe repository was created 20 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
30.2 KB, ~7.5k tokens by cl100k_base, as published. Nobody here has run it
TypeScript Best Practices
Comprehensive guide for writing high-quality, idiomatic TypeScript and modern JavaScript-via-TypeScript code. Contains 171 rules across 14 categories, prioritized by impact. Project constraints override generic defaults: preserve the declared TypeScript version, tsconfig.json strictness settings, module system (ESM/CJS), and target runtime unless the user explicitly requests a migration.
When to Apply
Reference these guidelines when:
- Writing new TypeScript/JavaScript functions, classes, or modules
- Implementing error handling or async/await code
- Designing public APIs for libraries or shared packages
- Reviewing code for type-safety gaps (
any, unchecked assertions, missing narrowing) - Optimizing bundle size, allocations, or hot paths
- Structuring a project, monorepo, or module boundaries
- Writing or reviewing tests (Vitest/Jest)
- Migrating a codebase to stricter
tsconfig.jsonsettings or newer TypeScript releases
Modern TypeScript & tsconfig Notes
TypeScript's release cadence (5.4–5.9+ as of 2025) is additive, not edition-based like Rust — there is no single "flip a switch" migration. Preserve a project's existing tsconfig.json and only apply the notes below when the installed TypeScript version and runtime actually support them; verify with npx tsc -v and the project's package.json engines field before assuming a feature is available.
strict: true. The single highest-leverage setting. It bundlesstrictNullChecks,noImplicitAny,strictFunctionTypes,strictBindCallApply,strictPropertyInitialization,noImplicitThis, andalwaysStrict. New projects should start here; existing projects should ratchet toward it incrementally (strictNullChecksfirst).satisfiesoperator (5.0+). Validates that a literal conforms to a type without widening the literal's inferred type the way an: Typeannotation would — seetype-satisfies-operator.verbatimModuleSyntax(5.0+). Replaces the olderisolatedModules+importsNotUsedAsValuescombination; makes type-only imports/exports explicit (import type) so single-file transpilers (esbuild, swc, Babel) never accidentally elide or keep a runtime import incorrectly.moduleResolution: "bundler"(5.0+). Matches how modern bundlers (Vite, esbuild, webpack) actually resolve packages, includingexportsmap conditions, without requiring file extensions on relative imports the way"node16"/"nodenext"does.noUncheckedIndexedAccess. MakesT[K]on an index signature or array returnT | undefinedinstead ofT, closing a large hole instrictmode around out-of-bounds/missing-key access.- Const type parameters (
const T extends ..., 5.0+). Infers generic arguments as their literal (as const-like) form without callers having to writeas constthemselves at every call site. - Template literal types &
as const. Together they let you derive precise string-literal unions from data instead of hand-maintaining parallelenum/union declarations — seetype-template-literalandtype-const-assertion. - ESM vs. CJS interop. Node.js, bundlers, and TypeScript increasingly default to ESM. Use
"type": "module"inpackage.jsonfor new packages,import/exportsyntax, and theexportsfield to declare public entry points; reserve"type": "commonjs"and.cjsfor legacy consumers. Mixed dual-package builds should generate both outputs rather than relying on runtime shims. isolatedModules. Required by every non-tsctranspiler (esbuild, swc, Babel) because they compile files independently without full type information; keep it on for any project using such a toolchain.
Rule Categories by Priority
| Priority | Category | Impact | Prefix | Rules |
|---|---|---|---|---|
| 1 | Type Safety & Narrowing | CRITICAL | type- | 16 |
| 2 | Error Handling | CRITICAL | err- | 13 |
| 3 | Async/Promises/Concurrency | CRITICAL | async- | 16 |
| 4 | API/Module Design | HIGH | api- | 14 |
| 5 | Immutability & Data Patterns | HIGH | imm- | 10 |
| 6 | Functional Patterns | HIGH | fn- | 10 |
| 7 | Naming Conventions | MEDIUM | name- | 12 |
| 8 | Testing | MEDIUM | test- | 13 |
| 9 | Documentation | MEDIUM | doc- | 9 |
| 10 | Performance Patterns | MEDIUM | perf- | 12 |
| 11 | Node.js/Runtime | MEDIUM | node- | 9 |
| 12 | Project Structure & Tooling | LOW | proj- | 11 |
| 13 | Linting | LOW | lint- | 10 |
| 14 | Anti-patterns | REFERENCE | anti- | 16 |
Quick Reference
1. Type Safety & Narrowing (CRITICAL)
type-unknown-over-any- Useunknowninstead ofanyfor values of uncertain typetype-narrow-guards- Use user-defined type guards (is) to narrow union types safelytype-discriminated-union- Model variants with discriminated unions and a common tag fieldtype-exhaustive-switch- Enforce exhaustiveness checks with aneverassertiontype-satisfies-operator- Usesatisfiesto validate a value's shape without widening its typetype-const-assertion- Useas constto infer literal, readonly typestype-branded-nominal- Use branded/nominal types to distinguish primitives with the same runtime typetype-avoid-assertion- Avoidastype assertions; prefer narrowing or validationtype-strict-null-checks- EnablestrictNullChecksand model absence withundefined/nullexplicitlytype-template-literal- Use template literal types to constrain string patternstype-generic-constraints- Constrain generic type parameters withextendsinstead of leaving them unboundedtype-readonly-arrays- Acceptreadonly T[]for parameters that shouldn't be mutatedtype-utility-types- Prefer built-in utility types (Pick,Omit,Partial,Required) over hand-rolled equivalentstype-index-signature-safety- EnablenoUncheckedIndexedAccessand guard indexed access resultstype-zod-schema-inference- Derive static types from a runtime schema instead of maintaining both by handtype-function-overloads- Use overload signatures to model functions with varying call shapes
2. Error Handling (CRITICAL)
err-custom-error-class- ExtendErrorwith custom subclasses that carry structured contexterr-cause-chaining- Chain root causes with the standardcauseoptionerr-never-swallow- Never silently swallow errors in empty catch blockserr-result-pattern- Use aResult-like return type for expected, recoverable failureserr-async-propagation- Let async/await propagate rejections naturally instead of mixing.then/.catcherr-promise-allsettled- UsePromise.allSettledwhen independent operations may fail without aborting otherserr-boundary-validation- Validate untrusted input at system boundaries with a schema libraryerr-specific-catch- Catch and handle specific error types instead of a blanket catch-allerr-rethrow-context- Add context when rethrowing instead of losing the original errorerr-no-throw-strings- Always throwErrorinstances, never strings or plain objectserr-unhandled-rejection- Register process-level handlers for unhandled promise rejectionserr-finally-cleanup- Usefinallyfor cleanup that must run regardless of outcomeerr-typed-catch-unknown- Type the catch binding asunknownand narrow before use
3. Async/Promises/Concurrency (CRITICAL)
async-await-over-then- Preferasync/awaitover chained.then()callsasync-no-floating-promises- Never leave a promise floating; await, return, or explicitly void itasync-promise-all-parallel- UsePromise.allto run independent async work concurrentlyasync-avoid-sequential-await- Avoid awaiting independent operations sequentially inside loopsasync-abort-controller- UseAbortControllerto make async operations cancellableasync-timeout-race- Implement timeouts by racing a promise against a timerasync-concurrency-limit- Bound concurrency with a limiter when processing large batchesasync-no-async-constructor- Avoidasyncconstructors; use a static async factory method insteadasync-for-await-iteration- Usefor await...ofto consume async iterablesasync-top-level-await- Use top-levelawaitonly at module entry pointsasync-avoid-async-foreach- AvoidArray.prototype.forEachwith an async callbackasync-immediately-invoked- Use an async IIFE to run async code in non-async contextsasync-microtask-ordering- Understand microtask vs. macrotask ordering to avoid subtle scheduling bugsasync-retry-backoff- Retry transient failures with exponential backoff and jitterasync-void-operator- Use thevoidoperator to mark an intentionally ignored promiseasync-generator-streams- Use async generators to model lazy, pull-based async sequences
4. API/Module Design (HIGH)
api-minimal-surface- Keep the public API surface as small as the consumer actually needsapi-named-over-default-export- Prefer named exports over default exportsapi-barrel-file-tradeoffs- Use barrel (index.ts) files judiciously; they can defeat tree-shakingapi-builder-pattern- Use a builder/fluent API for objects with many optional construction parametersapi-readonly-public-types- Mark public interface propertiesreadonlyunless mutation is part of the contractapi-explicit-return-types- Annotate explicit return types on exported functionsapi-avoid-optional-overuse- Avoid excessive optional properties; model valid states as required unions insteadapi-generic-defaults- Give generic type parameters sensible defaults where one existsapi-function-overload-order- Order overload signatures from most specific to most generalapi-interface-vs-type- Useinterfacefor extendable object shapes,typefor unions/aliases/mapped typesapi-accept-narrow-return-wide- Accept the most general input types callers already have; return the most specific typesapi-avoid-enum-const-object- Prefer literal unions oras constobjects overenumapi-module-boundary-types- Define explicit DTOs at module/service boundaries, separate from internal domain modelsapi-versioned-public-api- Version public package APIs deliberately and follow semver for breaking changes
5. Immutability & Data Patterns (HIGH)
imm-prefer-const- Default toconst; useletonly when a binding is reassignedimm-as-const-literal- Freeze literal object/array structures withas constimm-object-freeze-runtime- UseObject.freezewhen you need a runtime immutability guarantee, not just a compile-time oneimm-spread-not-mutate- Create updated copies with spread/rest instead of mutating in placeimm-avoid-array-mutation- Avoid mutating array methods (push,splice,sort) on shared/shared-reference arraysimm-structural-sharing- Use structural sharing so immutable updates don't copy untouched subtreesimm-readonly-class-fields- Mark class fieldsreadonlywhen they are set once in the constructorimm-deep-immutability-types- Use a deep-readonly utility type for nested immutable state treesimm-avoid-param-mutation- Never mutate a function's input parametersimm-immutable-collections- Consider a persistent/immutable collection library for hot mutation-heavy state paths
6. Functional Patterns (HIGH)
fn-pure-functions- Prefer pure functions with no hidden side effectsfn-array-methods-over-loops- Usemap/filter/reducefor transformations instead of manualforloopsfn-composition-over-inheritance- Compose small functions instead of building class inheritance hierarchiesfn-curry-partial-application- Use currying/partial application to produce reusable configured functionsfn-avoid-reduce-abuse- Avoidreducewhen a more specific method already expresses the intentfn-optional-chaining- Use optional chaining (?.) instead of manual nested null checksfn-nullish-coalescing- Use??instead of||when onlynull/undefinedshould trigger the defaultfn-pipeline-composition- Compose sequential data transformations as an explicit pipelinefn-early-return- Use early returns/guard clauses to reduce nestingfn-avoid-side-effects-in-map- Never use.map()purely for side effects; use.forEach()or aforloop
7. Naming Conventions (MEDIUM)
name-camelCase-vars- UsecamelCasefor variables and functionsname-PascalCase-types- UsePascalCasefor types, interfaces, classes, and enumsname-SCREAMING-const- UseSCREAMING_SNAKE_CASEfor true module-level constantsname-boolean-prefix- Prefix booleans withis/has/can/shouldname-no-hungarian- Avoid Hungarian notation and redundant type suffixes in identifier namesname-verb-noun-functions- Name functions with a leading verb describing the action they performname-avoid-abbreviations- Avoid unclear abbreviations in identifiersname-private-underscore-avoid- Useprivate/#for privacy instead of a leading-underscore conventionname-generic-type-params- Use conventional short generic names (T,K,V,E) or a descriptive name for complex genericsname-file-naming-convention- Apply one consistent file naming convention (kebab-case or PascalCase) per projectname-interface-no-I-prefix- Don't prefix interfaces withIname-async-suffix-when-ambiguous- Suffix an async function's name when a sync counterpart exists with the same base name
8. Testing (MEDIUM)
test-arrange-act-assert- Structure tests as arrange/act/asserttest-descriptive-names- Name tests descriptively: "should X when Y"test-vitest-jest-setup- Follow standard Vitest/Jest project conventions for config and structuretest-mock-boundaries- Mock external boundaries (network, filesystem, clock), not internal implementation detailstest-avoid-snapshot-abuse- Use snapshot tests sparingly, and review generated snapshots deliberatelytest-async-test-patterns- Always await async assertions; never leave a test's promise unhandledtest-test-doubles- Choose the right test double: stub, spy, mock, or faketest-isolate-tests- Keep tests isolated and order-independent, with no shared mutable statetest-coverage-meaningful- Target meaningful coverage of behavior, not a 100% coverage vanity metrictest-integration-vs-unit- Balance the test pyramid between unit and integration teststest-fixture-factories- Use factory functions to build test fixtures instead of duplicating literalstest-parameterized-tests- Use parameterized/table-driven tests (it.each) for input/output variantstest-fake-timers- Use fake timers to test time-dependent code deterministically
9. Documentation (MEDIUM)
doc-tsdoc-public-api- Document all public API with TSDoc commentsdoc-example-tags- Include an@exampleblock in non-trivial doc commentsdoc-param-returns-tags- Document@param/@returnsfor signatures that aren't self-evidentdoc-deprecated-tag- Mark deprecated APIs with@deprecatedand a migration pathdoc-readme-package- Maintain a README with install/usage examples for every published packagedoc-changelog-semver- Maintain a CHANGELOG that follows semantic versioningdoc-inline-why-not-what- Write comments that explain why, not what the code already saysdoc-type-as-documentation- Let precise types replace comments that only describe a shapedoc-throws-tag- Document the errors a function can throw with@throws
10. Performance Patterns (MEDIUM)
perf-avoid-premature-optimize- Profile before optimizingperf-tree-shaking-friendly- Write side-effect-free modules so bundlers can tree-shake unused exportsperf-lazy-load-dynamic-import- Use dynamicimport()for code splitting and lazy loadingperf-avoid-unnecessary-allocation- Avoid allocating objects/arrays inside hot loopsperf-memoize-expensive- Memoize expensive pure computationsperf-debounce-throttle- Debounce or throttle high-frequency event handlersperf-avoid-json-parse-large- Avoid blocking synchronous JSON parsing of large payloads on hot pathsperf-string-concat-builder- Build large strings with arrays/template literals, not repeated+=concatenationperf-avoid-deep-clone- Avoid deep cloning when structural sharing or shallow copies sufficeperf-bundle-size-audit- Audit bundle size and dependency weight regularlyperf-worker-offload- Offload CPU-heavy work to a worker thread instead of blocking the main threadperf-avoid-blocking-event-loop- Avoid long synchronous operations that block the event loop
11. Node.js/Runtime (MEDIUM)
node-esm-first- Prefer ES modules over CommonJS for new Node.js projectsnode-package-exports-map- Define package entry points with theexportsfieldnode-env-var-validation- Validate environment variables against a schema at startupnode-graceful-shutdown- HandleSIGTERM/SIGINTfor graceful shutdownnode-streams-backpressure- Use streams with backpressure for large I/O instead of buffering in memorynode-avoid-sync-fs- Avoid synchronousfscalls on a server's request pathnode-process-exit-avoid- Avoidprocess.exit()in library code; let the caller control the processnode-worker-threads-cpu- Useworker_threadsfor CPU-bound work in a Node.js servernode-structured-logging- Use structured, leveled logging instead ofconsole.log
12. Project Structure & Tooling (LOW)
proj-path-aliases- Usetsconfigpath aliases instead of long relative import chainsproj-monorepo-workspaces- Use workspaces (pnpm/npm/yarn) to manage a monorepo's packagesproj-feature-based-structure- Organize source by feature/domain, not by technical file typeproj-single-tsconfig-base- Share a basetsconfig.jsonand extend it per packageproj-module-boundaries- Enforce module boundaries; don't import another module's internal filesproj-colocate-tests- Colocate tests with source, or mirror source structure consistently — pick oneproj-env-specific-config- Keep environment-specific configuration separate from codeproj-verbatim-module-syntax- EnableverbatimModuleSyntaxfor unambiguous type-only imports/exportsproj-isolated-modules- EnableisolatedModulesfor compatibility with single-file transpilersproj-declaration-files- Emit.d.tsdeclaration files for any published packageproj-lockfile-commit- Commit the lockfile for reproducible installs
13. Linting (LOW)
lint-typescript-eslint-recommended- Adopttypescript-eslint's recommended (or recommended-type-checked) configlint-no-explicit-any- Enable@typescript-eslint/no-explicit-anylint-no-floating-promises-rule- Enable@typescript-eslint/no-floating-promiseslint-no-non-null-assertion- Enable@typescript-eslint/no-non-null-assertionlint-prettier-integration- Use Prettier for formatting and let ESLint own only code-quality ruleslint-strict-tsconfig- Enablestrict: trueand other strictness flags intsconfig.jsonlint-no-unused-vars- Enable the TypeScript-awareno-unused-varsrulelint-consistent-type-imports- Enforceconsistent-type-importsso type-only imports are marked explicitlylint-ci-lint-gate- Run typecheck and lint as a required CI gatelint-no-unchecked-indexed-access- EnablenoUncheckedIndexedAccessintsconfig.json
14. Anti-patterns (REFERENCE)
anti-any-abuse- Don't useanyto silence type errorsanti-non-null-assertion-abuse- Don't overuse the!non-null assertion operatoranti-loose-equality- Don't use==/!=; use===/!==anti-callback-hell- Don't nest callbacks; use async/await insteadanti-mutate-props-state- Don't mutate props or shared state objects directlyanti-for-in-arrays- Don't usefor...into iterate arraysanti-stringly-typed-data- Don't represent structured data as ad hoc stringsanti-god-object- Don't build "God" objects/functions with too many responsibilitiesanti-magic-numbers- Don't scatter unexplained magic numbers/strings through codeanti-var-usage- Don't usevar; useconst/letanti-empty-catch-block- Don't leave catch blocks emptyanti-any-cast-double- Don't force incorrect types withas unknown as Tdouble castsanti-deeply-nested-ternary- Don't nest ternary expressions deeplyanti-global-mutable-state- Don't rely on global mutable stateanti-promise-constructor-antipattern- Don't wrap an already-promise-returning call innew Promiseanti-type-any-return- Don't returnanyfrom a function; it erases type safety for every caller
Recommended tsconfig.json / package.json Settings
// tsconfig.json
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler", // or "nodenext" for a Node.js-only library
"lib": ["ES2022"],
"strict": true, // strictNullChecks, noImplicitAny, etc.
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"verbatimModuleSyntax": true,
"isolatedModules": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"outDir": "dist",
"rootDir": "src"
},
"include": ["src"],
"exclude": ["dist", "node_modules"]
}
// package.json (relevant fields for an ESM-first published package)
{
"type": "module",
"engines": { "node": ">=20" },
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"files": ["dist"],
"sideEffects": false,
"scripts": {
"build": "tsc -p tsconfig.build.json",
"typecheck": "tsc --noEmit",
"lint": "eslint . --max-warnings 0",
"test": "vitest run"
}
}
How to Use
This skill provides rule identifiers for quick reference. When generating or reviewing TypeScript/JavaScript code:
- Check relevant category based on task type
- Apply rules with matching prefix
- Prioritize CRITICAL > HIGH > MEDIUM > LOW
- Read rule files in
rules/for detailed examples
Rule Application by Task
| Task | Primary Categories |
|---|---|
| New function/module | type-, err-, name- |
| New public API/package | api-, type-, doc- |
| Async/network code | async-, err- |
| Error handling | err-, type- |
| State management | imm-, fn- |
| Performance tuning | perf-, async-, node- |
| Writing tests | test- |
| Project/monorepo setup | proj-, lint-, node- |
| Code review | anti-, lint- |
Related Skills
- design-patterns - choosing and implementing GoF and idiomatic patterns; apply alongside this skill's API and naming rules for pattern-heavy TypeScript design.
- security-review - security-focused audit checklists; apply alongside this skill's error-handling and async rules when reviewing TypeScript code for vulnerabilities.
Sources
This skill synthesizes best practices from:
- TypeScript Handbook and the official TSConfig Reference
- typescript-eslint rule documentation
- Google TypeScript Style Guide
- Airbnb JavaScript Style Guide
- Effective TypeScript (Dan Vanderkam) and Total TypeScript (Matt Pocock)
- Production codebases:
type-fest,zod,trpc,vue,react,vite - Node.js and TC39/ECMAScript proposal documentation
- Community conventions (2024-2025)
What ships with it: 171 files
464.4 KB alongside SKILL.md
rules/
- anti-any-abuse.md2.4 KB
- anti-any-cast-double.md2.9 KB
- anti-callback-hell.md2.6 KB
- anti-deeply-nested-ternary.md2.7 KB
- anti-empty-catch-block.md2.5 KB
- anti-for-in-arrays.md2.4 KB
- anti-global-mutable-state.md2.7 KB
- anti-god-object.md3.3 KB
- anti-loose-equality.md2.4 KB
- anti-magic-numbers.md2.8 KB
- anti-mutate-props-state.md2.8 KB
- anti-non-null-assertion-abuse.md2.4 KB
- anti-promise-constructor-antipattern.md2.6 KB
- anti-stringly-typed-data.md2.8 KB
- anti-type-any-return.md3.0 KB
- anti-var-usage.md2.3 KB
- api-accept-narrow-return-wide.md3.0 KB
- api-avoid-enum-const-object.md2.9 KB
- api-avoid-optional-overuse.md2.8 KB
- api-barrel-file-tradeoffs.md2.9 KB
- api-builder-pattern.md3.3 KB
- api-explicit-return-types.md2.7 KB
- api-function-overload-order.md3.1 KB
- api-generic-defaults.md2.8 KB
- api-interface-vs-type.md3.0 KB
- api-minimal-surface.md2.9 KB
- api-module-boundary-types.md3.1 KB
- api-named-over-default-export.md2.6 KB
- api-readonly-public-types.md2.7 KB
- api-versioned-public-api.md3.3 KB
- async-abort-controller.md2.8 KB
- async-avoid-async-foreach.md2.5 KB
- async-avoid-sequential-await.md2.3 KB
- async-await-over-then.md2.4 KB
- async-concurrency-limit.md2.5 KB
- async-for-await-iteration.md2.5 KB
- async-generator-streams.md2.5 KB
- async-immediately-invoked.md2.0 KB
- async-microtask-ordering.md2.7 KB
- async-no-async-constructor.md2.8 KB
131 more files not listed here. See all 171 in the repository.