home / skills / gentleman-programming / gentleman-skills / typescript

typescript skill

/curated/typescript

This skill enforces TypeScript best practices like const patterns, flat interfaces, and safe generics to improve type safety and maintainability.

This is most likely a fork of the typescript skill from gentleman-programming
npx playbooks add skill gentleman-programming/gentleman-skills --skill typescript

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

Files (1)
SKILL.md
2.4 KB
---
name: typescript
description: >
  TypeScript strict patterns and best practices.
  Trigger: When writing TypeScript code - types, interfaces, generics.
license: Apache-2.0
metadata:
  author: gentleman-programming
  version: "1.0"
---

## Const Types Pattern (REQUIRED)

```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 (REQUIRED)

```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";
```

## Keywords
typescript, ts, types, interfaces, generics, strict mode, utility types

Overview

This skill captures TypeScript strict patterns and pragmatic best practices for types, interfaces, and generics. It focuses on maintainable typing: const-backed unions, flat interfaces, avoiding any, effective utility types, and explicit type guards. Use it as a concise checklist when writing or reviewing TypeScript code.

How this skill works

The skill inspects type definitions and usage patterns and recommends safer, refactor-friendly alternatives. It enforces creating const objects for runtime-backed unions, splitting nested shapes into separate interfaces, preferring unknown and generics over any, and using built-in utility types. It also highlights proper type guard shapes and import type usage for clearer intent.

When to use it

  • When defining finite string unions or enums for runtime values
  • When modeling domain shapes that may be nested or extended
  • When you encounter any in function signatures or returns
  • During code reviews to improve type safety and refactorability
  • When writing library APIs that need precise exported types

Best practices

  • Create a const object and derive the union type: keep a single source of truth with as const and typeof
  • Keep interfaces flat: extract nested objects into dedicated interfaces to improve reuse and readability
  • Avoid any: use unknown for unknown inputs and generics for flexibility; validate before narrowing
  • Favor utility types (Pick, Omit, Partial, Readonly, ReturnType, Parameters) over manual rewrites
  • Write explicit type guards that narrow unknown to concrete interfaces using runtime checks
  • Use import type for type-only imports to clarify intent and enable tooling optimizations

Example use cases

  • Defining status constants for API payloads: const STATUS = { ... } as const; type Status = (typeof STATUS)[keyof typeof STATUS]
  • Designing user/admin models: extract UserAddress and reference it instead of inlining nested objects
  • Replacing any in parsers: accept unknown, validate with isUser, then return a typed User
  • Refactoring API responses: use Pick<User, 'id' | 'name'> for light-weight DTOs
  • Creating robust type guards for data deserialization and runtime validation

FAQ

Why prefer a const object over a direct union type?

A const object is the single source of truth that provides runtime values, better autocomplete, and easier refactoring while deriving the union type from it ensures consistency.

When should I use unknown vs any?

Use unknown for values you must validate before use; it forces explicit narrowing. any skips all checks and should be avoided except for quick prototypes.