install
source · Clone the upstream repo
git clone https://github.com/Intense-Visions/harness-engineering
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/Intense-Visions/harness-engineering "$T" && mkdir -p ~/.claude/skills && cp -r "$T/agents/skills/codex/ts-module-patterns" ~/.claude/skills/intense-visions-harness-engineering-ts-module-patterns-8ce694 && rm -rf "$T"
manifest:
agents/skills/codex/ts-module-patterns/SKILL.mdsource content
TypeScript Module Patterns
Organize TypeScript code with ES modules, barrel exports, path aliases, and declaration files
When to Use
- Structuring a TypeScript project's import/export architecture
- Setting up barrel files (index.ts re-exports) for clean public APIs
- Configuring path aliases to avoid deep relative imports
- Creating or consuming declaration files (.d.ts)
Instructions
- Use named exports over default exports for better refactoring and tree-shaking:
// user.ts export interface User { id: string; name: string; } export function createUser(name: string): User { /* ... */ } // consumer.ts import { User, createUser } from './user';
- Barrel files — re-export from
to create clean module boundaries:index.ts
// features/auth/index.ts export { AuthProvider } from './auth-provider'; export { useAuth } from './use-auth'; export type { AuthState, AuthAction } from './types'; // Consumer imports from the module, not internal files import { AuthProvider, useAuth } from '@/features/auth';
- Configure path aliases in
:tsconfig.json
{ "compilerOptions": { "baseUrl": ".", "paths": { "@/*": ["src/*"], "@/db/*": ["src/db/*"], "@/lib/*": ["src/lib/*"] } } }
Mirror in your bundler config (Next.js, Vite, etc.) so runtime resolution matches TypeScript's.
for type-only exports — prevents runtime import of type-only modules:export type
export type { User, Post } from './types'; export { createUser } from './user';
for type-only imports:import type
import type { User } from './types'; import { createUser } from './user';
Enable
verbatimModuleSyntax in tsconfig to enforce this distinction.
- Declaration files (.d.ts) — provide types for JavaScript modules:
// legacy-module.d.ts declare module 'legacy-module' { export function doWork(input: string): number; export interface Config { verbose: boolean; } }
- Namespace imports for modules with many exports:
import * as Schema from '@/db/schema'; const user = Schema.users; const post = Schema.posts;
-
Avoid circular imports — if module A imports from B and B imports from A:
- Extract shared types into a separate module C
- Use
when only types are needed (type imports are erased and do not cause runtime circularity)import type - Restructure to unidirectional dependencies
-
Package entry points — configure
inexports
for library projects:package.json
{ "exports": { ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" }, "./utils": { "types": "./dist/utils.d.ts", "import": "./dist/utils.js" } } }
Details
TypeScript modules follow the ES module standard. Each file with a top-level
import or export is a module; files without them are scripts (global scope).
(recommended): Forces you to use verbatimModuleSyntax
import type for type-only imports and export type for type-only re-exports. This eliminates ambiguity about which imports are erased during compilation.
Barrel file trade-offs:
- Pros: Clean public API, consumers do not depend on internal file structure
- Cons: Can defeat tree-shaking in some bundlers (import from barrel loads entire module), can create large import chains
- Best practice: Use barrels at feature boundaries, not for every directory
Path aliases require bundler sync: TypeScript's
paths only affect type checking. The runtime module resolver (Node.js, Vite, webpack) must also be configured to resolve the same aliases.
options:moduleResolution
— traditional Node.js resolution (index.ts, package.json main)node
/node16
— Node.js ESM resolution (requires file extensions in imports)nodenext
— modern bundler resolution (Vite, webpack, esbuild). Best choice for most frontend projectsbundler
Trade-offs:
- Barrel files improve DX but can hurt bundle size — profile with your bundler's analysis tool
- Path aliases improve readability but add configuration overhead in every tool that processes imports
catches real bugs but requires existing code to be updated withverbatimModuleSyntaximport type
Source
https://typescriptlang.org/docs/handbook/modules.html
Process
- Read the instructions and examples in this document.
- Apply the patterns to your implementation, adapting to your specific context.
- Verify your implementation against the details and edge cases listed above.
Harness Integration
- Type: knowledge — this skill is a reference document, not a procedural workflow.
- No tools or state — consumed as context by other skills and agents.
Success Criteria
- The patterns described in this document are applied correctly in the implementation.
- Edge cases and anti-patterns listed in this document are avoided.