Agent skill
typescript
Configures TypeScript projects, defines types and interfaces, writes generics, and implements type guards. Use when setting up tsconfig.json, creating type definitions, or ensuring type safety in JS/TS codebases.
Install this agent skill to your Project
npx add-skill https://github.com/knoopx/pi/tree/main/agent/skills/typescript
SKILL.md
TypeScript
Type-safe JavaScript development with bun and vitest in this project.
Quick Start
bun init --typescript # Initialize project
bunx tsc --noEmit # Type check
vitest run # Run tests
Project Configuration
This project uses bun with ESNext targets. Reference config in templates/tsconfig.json:
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"lib": ["ESNext"],
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"isolatedModules": true,
"baseUrl": ".",
"paths": { "@/*": ["src/*"] }
},
"include": ["src"],
"exclude": ["node_modules"]
}
Decision Guide: Interfaces vs Types
Use this when choosing between interface and type:
- Interface when: defining object shapes that may be extended, or when declaration merging is needed (e.g., augmenting third-party types)
- Type when: defining unions, intersections, mapped types, conditional types, or function signatures
// Interface — extendable object shape
interface User {
id: number;
name: string;
email: string;
}
// Type — unions and computed types
type Status = "loading" | "success" | "error";
type CreateUser = (data: Partial<User>) => Promise<User>;
// Discriminated union — use type, not interface
type Result<T> = { ok: true; value: T } | { ok: false; error: Error };
Type Safety Patterns
Branded Types for Domain Primitives
Use branded types when a plain string or number could be confused across domains:
type UserId = string & { readonly __brand: "UserId" };
type OrderId = string & { readonly __brand: "OrderId" };
function createUserId(id: string): UserId {
// validate format here
return id as UserId;
}
// Compiler prevents: getUser(orderId) — type mismatch
function getUser(id: UserId): Promise<User> {
/* ... */
}
Making Invalid States Unrepresentable
Model states as discriminated unions so illegal combinations are compile errors:
type AsyncState<T> =
| { status: "idle" }
| { status: "loading" }
| { status: "success"; data: T }
| { status: "error"; error: Error };
// Impossible to have data without status: "success"
function render(state: AsyncState<User[]>) {
switch (state.status) {
case "success":
return renderTable(state.data);
case "error":
return renderError(state.error);
// exhaustiveness: TS errors if a case is missing
}
}
Type Guards with Runtime Validation
function isUser(value: unknown): value is User {
return (
typeof value === "object" &&
value !== null &&
"id" in value &&
"name" in value
);
}
Result Type for Error Handling
Prefer Result<T> over try/catch for composable error handling:
type Result<T, E = Error> = { ok: true; value: T } | { ok: false; error: E };
function safeParse(json: string): Result<unknown> {
try {
return { ok: true, value: JSON.parse(json) };
} catch (error) {
return { ok: false, error: error as Error };
}
}
Workflow
- Set up project:
bun init --typescript, copytemplates/tsconfig.json - Write types first: Define interfaces/types at module boundaries before implementation
- Type check:
bunx tsc --noEmit— fix errors before proceeding - Watch mode:
tmux new -d -s tsc 'tsc --watch'for continuous feedback - Test:
vitest runto validate behavior matches types - Gradual adoption: Use
allowJs: true+checkJs: truewhen migrating JS files incrementally
Constraints
- Always use
strict: true— never weaken strictness per-file with// @ts-ignore; use// @ts-expect-errorwith a comment explaining why - Avoid
any— useunknownand narrow with type guards - Type at boundaries (API inputs/outputs, function signatures) — let inference handle internals
- Path aliases: use
@/*mapped tosrc/*for imports
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
conventional-commits
Writes and reviews Conventional Commits commit messages (v1.0.0) to support semantic versioning and automated changelogs. Use when drafting git commit messages, PR titles, release notes, or when enforcing a conventional commit format (type(scope): subject, BREAKING CHANGE, footers, revert).
nix-flakes
Creates reproducible builds, manages flake inputs, defines devShells, and builds packages with flake.nix. Use when initializing Nix projects, locking dependencies, or running nix build/develop commands.
skill-authoring
Writes effective pi skills with proper structure, concise content, and progressive disclosure. Use when creating new skills, improving existing skills, or reviewing skill quality.
gtkx
Build GTK4 desktop applications with GTKX React framework. Use when creating React components that render as native GTK widgets, working with GTK4/Libadwaita UI, handling signals, virtual lists, menus, or building Linux desktop UIs.
nu-shell
Processes structured data through pipelines, filters tables, transforms JSON/CSV/YAML, and defines custom commands. Use when scripting with typed parameters or working with tabular data.
nix
Runs packages temporarily, creates isolated shell environments, and evaluates Nix expressions. Use when executing tools without installing, debugging derivations, or working with nixpkgs.
Didn't find tool you were looking for?