skills/jander99/skills/typescript-advanced

typescript-advanced

SKILL.md

TypeScript Advanced Patterns

Expert guidance for writing type-safe TypeScript code using advanced type system features.

What I Do

  • Design generic types with constraints, defaults, and inference
  • Apply utility types (Partial, Pick, Omit, Record, Required, Readonly)
  • Create custom mapped and conditional types
  • Implement type guards and discriminated unions
  • Configure strict mode and debug type errors
  • Augment modules and merge declarations

When to Use Me

Use this skill when you:

  • Create generic functions, classes, or type-safe APIs
  • Fix, debug, or resolve TypeScript type errors
  • Write custom mapped or conditional types
  • Add type guards for runtime narrowing
  • Configure or troubleshoot strict mode settings
  • Avoid any and improve type safety

Generic Patterns

// Constrain generic to require properties
function loggingIdentity<T extends { length: number }>(arg: T): T {
  console.log(arg.length);
  return arg;
}

// Type parameter constrained by another
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

// Default type parameter
type Container<T = string> = { value: T };

Utility Types

interface User { id: number; name: string; email: string; }

type PartialUser = Partial<User>;           // All optional
type RequiredUser = Required<PartialUser>;  // All required
type ReadonlyUser = Readonly<User>;         // All readonly
type UserPreview = Pick<User, "id" | "name">;
type UserWithoutEmail = Omit<User, "email">;

// Function types - define the function first
function fetchUser(id: number): Promise<User> { /* ... */ }

type Params = Parameters<typeof fetchUser>;     // [number]
type Return = ReturnType<typeof fetchUser>;     // Promise<User>
type Unwrapped = Awaited<ReturnType<typeof fetchUser>>;  // User

Type Guards

// typeof and instanceof narrowing
function process(value: string | number) {
  if (typeof value === "string") return value.toUpperCase();
  return value.toFixed(2);
}

// User-defined type predicate
function isFish(pet: Fish | Bird): pet is Fish {
  return (pet as Fish).swim !== undefined;
}

// Discriminated unions with exhaustiveness checking
interface Circle { kind: "circle"; radius: number; }
interface Square { kind: "square"; sideLength: number; }
type Shape = Circle | Square;

function getArea(shape: Shape): number {
  switch (shape.kind) {
    case "circle": return Math.PI * shape.radius ** 2;
    case "square": return shape.sideLength ** 2;
    default: const _: never = shape; return _;  // Exhaustiveness
  }
}

Mapped Types

// Transform all properties
type OptionsFlags<T> = { [P in keyof T]: boolean };

// Remove modifiers
type Mutable<T> = { -readonly [P in keyof T]: T[P] };
type Concrete<T> = { [P in keyof T]-?: T[P] };

// Key remapping with template literals
type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]
};

Conditional Types

// Basic conditional
type IsString<T> = T extends string ? true : false;

// Inferring types
type Flatten<T> = T extends Array<infer Item> ? Item : T;
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

// Distributive behavior (over unions)
type ToArray<T> = T extends any ? T[] : never;
type Result = ToArray<string | number>;  // string[] | number[]

// Prevent distribution (wrap in tuple)
type ToArrayNonDist<T> = [T] extends [unknown] ? T[] : never;
type Result = ToArrayNonDist<string | number>;  // (string | number)[]

Context7 Integration

For up-to-date TypeScript documentation, use Context7 MCP server:

  • Query: "TypeScript generics constraints" | Library: /microsoft/typescript
  • Query: "TypeScript utility types" | Library: /microsoft/typescript
  • Query: "TypeScript conditional types infer" | Library: /microsoft/typescript

Common Errors

"Property does not exist on type" - Use in operator or type guard:

if ("a" in obj) { console.log(obj.a); }

"Type is not assignable to type 'never'" - Missing switch case:

// Add missing case or default with never check

"Object is possibly 'undefined'" - Use optional chaining or type guard:

console.log(user.address?.city);
if (user.address) { console.log(user.address.city); }

Avoiding any:

// Use unknown instead of any
function processData(data: unknown): string {
  if (typeof data === "string") return data.toUpperCase();
  throw new Error("Unsupported type");
}
type AnyObject = Record<string, unknown>;
type AnyFunction = (...args: unknown[]) => unknown;

Strict Mode Configuration

{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true
  }
}

Modern TypeScript (4.9+)

// satisfies - validate type without widening
const config = {
  port: 8080,
  host: "localhost"
} satisfies Record<string, string | number>;
// config.port is number, not string | number

// const type parameters (TS 5.0+)
function createArray<const T extends readonly unknown[]>(items: T): T {
  return items;
}
const arr = createArray([1, 2, 3]);  // readonly [1, 2, 3], not number[]

// using declarations (TS 5.2+)
async function processFile() {
  using file = await openFile("data.txt");
  // file automatically disposed at end of scope
}

Related Skills

References

Reference Use When
research.md Deep dive into patterns and examples
TypeScript Handbook Official documentation
Weekly Installs
1
Repository
jander99/skills
GitHub Stars
1
First Seen
2 days ago
Installed on
amp1
cline1
opencode1
cursor1
kimi-cli1
codex1