home / skills / gpolanco / skills-as-context / typescript

typescript skill

/skills/typescript

This skill helps you implement and refactor TypeScript with strict patterns, promoting unknown over any, const types, and robust type guards for safer code.

npx playbooks add skill gpolanco/skills-as-context --skill typescript

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
4.0 KB
---
name: typescript
description: >
  TypeScript strict patterns and best practices.
  Trigger: When implementing or refactoring TypeScript in .ts/.tsx (types, interfaces, generics, const maps, type guards, removing any, tightening unknown).
license: Apache-2.0
metadata:
  author: prowler-cloud
  version: "1.0.0"
  scope: [root]
  auto_invoke: "Writing TypeScript types/interfaces"
allowed-tools: Read
---

## When to Use

Use this skill when:

- Writing TypeScript types or interfaces
- Refactoring JavaScript to TypeScript
- Removing `any` types from codebase
- Creating type-safe utilities
- Implementing type guards
- Working with generics

---

## Critical Patterns

### ALWAYS

- **Use const types** - Create const object first, extract type
- **Use flat interfaces** - One level depth, nest via references
- **Use unknown** over any for truly unknown types
- **Use generics** for flexible, reusable types
- **Use type guards** for runtime type checking
- **Use import type** for type-only imports
- **Use strict mode** in tsconfig.json

### NEVER

- **Never use any** - use unknown or generics instead
- **Never inline nested objects** - create dedicated interfaces
- **Never create direct union types** - use const types pattern
- **Never skip type guards** for external data

### DEFAULTS

- Strict mode enabled in tsconfig.json
- Types defined in types/ or feature-specific directories
- Use `interface` for object shapes, `type` for unions/intersections
- Use kebab-case for file names (user-types.ts)

---

## Const Types Pattern

```typescript
// ✅ ALWAYS: Create const object first, then extract type
const STATUS = {
  ACTIVE: "active",
  INACTIVE: "inactive",
  PENDING: "pending",
} as const;

type Status = (typeof STATUS)[keyof typeof STATUS];

// ❌ NEVER: Direct union types
type Status = "active" | "inactive" | "pending";
```

**Why?** Single source of truth, runtime values, autocomplete, easier refactoring.

---

## Flat Interfaces

```typescript
// ✅ ALWAYS: One level depth, nested objects → dedicated interface
interface UserAddress {
  street: string;
  city: string;
}

interface User {
  id: string;
  name: string;
  address: UserAddress; // Reference, not inline
}

interface Admin extends User {
  permissions: string[];
}

// ❌ NEVER: Inline nested objects
interface User {
  address: { street: string; city: string }; // NO!
}
```

---

## Never Use `any`

```typescript
// ✅ Use unknown for truly unknown types
function parse(input: unknown): User {
  if (isUser(input)) return input;
  throw new Error("Invalid input");
}

// ✅ Use generics for flexible types
function first<T>(arr: T[]): T | undefined {
  return arr[0];
}

// ❌ NEVER
function parse(input: any): any {}
```

---

## Utility Types

```typescript
Pick<User, "id" | "name">; // Select fields
Omit<User, "id">; // Exclude fields
Partial<User>; // All optional
Required<User>; // All required
Readonly<User>; // All readonly
Record<string, User>; // Object type
Extract<Union, "a" | "b">; // Extract from union
Exclude<Union, "a">; // Exclude from union
NonNullable<T | null>; // Remove null/undefined
ReturnType<typeof fn>; // Function return type
Parameters<typeof fn>; // Function params tuple
```

---

## Type Guards

```typescript
function isUser(value: unknown): value is User {
  return (
    typeof value === "object" &&
    value !== null &&
    "id" in value &&
    "name" in value
  );
}
```

---

## Import Types

```typescript
import type { User } from "./types";
import { createUser, type Config } from "./utils";
```

---

## Commands

```bash
# Initialize TypeScript in project
pnpm add -D typescript @types/node

# Create tsconfig.json with strict mode
pnpm tsc --init --strict

# Type check
pnpm tsc --noEmit

# Watch mode
pnpm tsc --watch --noEmit
```

---

## Resources

- **TypeScript Handbook**: [typescriptlang.org/docs](https://www.typescriptlang.org/docs/)
- **Utility Types**: [Utility Types Reference](https://www.typescriptlang.org/docs/handbook/utility-types.html)
- **tsconfig Reference**: [tsconfig.json](https://www.typescriptlang.org/tsconfig)

Overview

This skill captures strict TypeScript patterns and practical best practices for implementing and refactoring .ts/.tsx code. It focuses on eliminating any, enforcing strict mode, using const-backed unions, flat interfaces, generics, and robust type guards to make code safer and easier to maintain.

How this skill works

The skill inspects TypeScript sources and recommends patterns: prefer const objects with extracted union types, flatten interfaces and reference nested shapes, replace any with unknown or generics, and add type guards for external data. It also enforces import type usage and tsconfig strict settings and suggests utility types where appropriate.

When to use it

  • Writing new TypeScript types, interfaces, or generics
  • Refactoring JavaScript into TypeScript or tightening types
  • Removing or replacing any across the codebase
  • Creating reusable, type-safe utilities or APIs
  • Handling external/unsafe data and requiring runtime checks

Best practices

  • Enable strict mode in tsconfig.json and run tsc --noEmit regularly
  • Create const objects first and extract union types from them (as const + typeof pattern)
  • Keep interfaces flat; reference nested shapes via separate interfaces
  • Prefer unknown over any; use generics for flexible APIs
  • Implement explicit type guards for runtime validation of external data
  • Use import type for type-only imports and keep types in a dedicated directory

Example use cases

  • Convert a JS module to TypeScript by defining interfaces, replacing any with unknown, and adding type guards for inputs
  • Define a status enum-like union using a const object and derive the TypeScript union from it for single-source-of-truth values
  • Refactor deeply nested inline object types into flat interfaces with references to improve reusability and readability
  • Implement a generic utility function with type parameters instead of returning any
  • Add import type to remove runtime-only imports from compiled output and reduce bundle size

FAQ

Why prefer const objects + typeof over direct union literals?

Const-backed types provide a single source of truth with runtime values, enable autocomplete, and make refactors safer compared to scattered literal unions.

When should I use unknown instead of any?

Use unknown whenever input is truly untrusted or unspecified; pair it with type guards or assertions before using it as a concrete type. Avoid any because it bypasses type safety.