AgentStack
SKILL verified Apache-2.0 Self-run

Typescript Patterns

skill-jignesh-ponamwar-skills-mcp-typescript-patterns · by Jignesh-Ponamwar

>

No reviews yet
0 installs
17 views
0.0% view→install

Install

$ agentstack add skill-jignesh-ponamwar-skills-mcp-typescript-patterns

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Typescript Patterns? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

TypeScript Patterns Skill

1. Strict tsconfig (Always Start Here)

{
  "compilerOptions": {
    "strict": true,               // enables all strict checks
    "noUncheckedIndexedAccess": true,  // arr[0] returns T | undefined
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "exactOptionalPropertyTypes": true,
    "useUnknownInCatchVariables": true,
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "lib": ["ES2022"],
    "outDir": "./dist",
    "rootDir": "./src",
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true
  }
}

2. Utility Types - Essential Reference

interface User {
  id: number
  name: string
  email: string
  age?: number
}

// Partial - all properties optional
type UpdateUser = Partial

// Required - all properties required
type StrictUser = Required

// Pick - select specific properties
type UserSummary = Pick

// Omit - exclude specific properties
type PublicUser = Omit

// Readonly - prevent mutation
type ImmutableUser = Readonly

// Record - typed object with known key shape
type UserById = Record
type RolePermissions = Record

// Extract / Exclude
type StringOrNumber = string | number | boolean
type OnlyStrNum = Extract   // string | number
type NoString = Exclude               // number | boolean

// ReturnType / Parameters
function getUser(id: number): Promise { ... }
type GetUserReturn = Awaited>      // User
type GetUserParams = Parameters               // [id: number]

// NonNullable
type MaybeUser = User | null | undefined
type DefiniteUser = NonNullable                   // User

3. Generics - Patterns

// Generic function
function first(arr: readonly T[]): T | undefined {
  return arr[0]
}

// Generic with constraint
function getProperty(obj: T, key: K): T[K] {
  return obj[key]
}

// Generic interface with default
interface ApiResponse {
  data: T
  status: number
  message: string
}

// Multiple type parameters
function merge(a: A, b: B): A & B {
  return { ...a, ...b }
}

// Generic class
class Repository {
  private items = new Map()

  save(item: T): void {
    this.items.set(item.id, item)
  }

  findById(id: number): T | undefined {
    return this.items.get(id)
  }

  findAll(): T[] {
    return [...this.items.values()]
  }
}

4. Discriminated Unions - Type-Safe State Machines

// Instead of flags (error-prone):
// type Result = { loading: boolean; data?: User; error?: Error }

// Use discriminated union:
type Result =
  | { status: 'idle' }
  | { status: 'loading' }
  | { status: 'success'; data: T }
  | { status: 'error'; error: Error }

// TypeScript narrows correctly in each branch:
function renderUser(result: Result) {
  switch (result.status) {
    case 'idle':    return 
    case 'loading': return 
    case 'success': return    // data is User here
    case 'error':   return 
  }
}

// API response pattern
type ApiResult =
  | { ok: true; data: T }
  | { ok: false; error: string; statusCode: number }

async function fetchUser(id: number): Promise> {
  const response = await fetch(`/api/users/${id}`)
  if (!response.ok) {
    return { ok: false, error: await response.text(), statusCode: response.status }
  }
  return { ok: true, data: await response.json() }
}

// Consuming:
const result = await fetchUser(1)
if (result.ok) {
  console.log(result.data.name)  // TypeScript knows data is User
} else {
  console.error(result.error)    // TypeScript knows error is string
}

5. Branded Types (Nominal Typing)

Prevent mixing up primitive values that have the same underlying type:

// Without brands - easy to mix up IDs:
function getUser(id: number) { ... }
function getOrder(id: number) { ... }
getUser(orderId)  // TypeScript allows this - wrong!

// With brands - type-safe:
type UserId = number & { readonly _brand: 'UserId' }
type OrderId = number & { readonly _brand: 'OrderId' }

function createUserId(id: number): UserId {
  return id as UserId
}

function getUser(id: UserId) { ... }
function getOrder(id: OrderId) { ... }

const userId = createUserId(123)
const orderId = 456 as OrderId
getUser(userId)    // ✅
getUser(orderId)   // ❌ TypeScript error - OrderId is not UserId

6. Conditional Types

// Basic conditional type
type IsArray = T extends any[] ? true : false
type Test1 = IsArray  // true
type Test2 = IsArray    // false

// Infer - extract inner types
type UnwrapPromise = T extends Promise ? U : T
type User = UnwrapPromise>  // { name: string }

type ElementType = T extends (infer U)[] ? U : never
type Elem = ElementType  // string

// Distributive conditional types
type ToArray = T extends any ? T[] : never
type StringOrNumberArray = ToArray  // string[] | number[]

// Non-distributive (wrap in tuple)
type IsUnion = [T] extends [any] ? ([any] extends [T] ? false : true) : false

7. Template Literal Types

type EventName = 'click' | 'focus' | 'blur'
type Handler = `on${Capitalize}`  // 'onClick' | 'onFocus' | 'onBlur'

type CSSUnit = 'px' | 'em' | 'rem' | 'vh' | 'vw'
type CSSValue = `${number}${CSSUnit}`  // e.g. '16px', '1.5rem'

// Route types
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE'
type ApiRoute = `/api/${string}`

// Extract path parameters
type ExtractParams =
  Path extends `${string}:${infer Param}/${infer Rest}`
    ? Param | ExtractParams
    : Path extends `${string}:${infer Param}`
    ? Param
    : never

type Params = ExtractParams  // 'id' | 'postId'

8. Type Narrowing

// typeof
function process(value: string | number) {
  if (typeof value === 'string') {
    return value.toUpperCase()  // string here
  }
  return value * 2  // number here
}

// instanceof
function handleError(err: unknown): string {
  if (err instanceof Error) return err.message
  if (typeof err === 'string') return err
  return 'Unknown error'
}

// User-defined type guard
interface Cat { meow(): void }
interface Dog { bark(): void }

function isCat(animal: Cat | Dog): animal is Cat {
  return 'meow' in animal
}

// Assertion function
function assertDefined(value: T, name: string): asserts value is NonNullable {
  if (value === null || value === undefined) {
    throw new Error(`Expected ${name} to be defined`)
  }
}

9. Eliminating any

// ❌ any - unsafe
function parseData(raw: any) {
  return raw.user.name  // runtime error if shape wrong
}

// ✅ unknown + narrowing - safe
function parseData(raw: unknown): string {
  if (
    typeof raw === 'object' && raw !== null &&
    'user' in raw && typeof (raw as any).user === 'object' &&
    'name' in (raw as any).user
  ) {
    return String((raw as any).user.name)
  }
  throw new Error('Unexpected data shape')
}

// ✅ Zod - best for runtime validation of external data
import { z } from 'zod'

const UserSchema = z.object({
  id: z.number(),
  name: z.string(),
  email: z.string().email(),
})
type User = z.infer

const user = UserSchema.parse(rawData)  // throws on invalid, returns User

Common Mistakes

  • Using any instead of unknown - unknown forces narrowing before use
  • as everywhere - as SomeType suppresses errors; use type guards instead
  • ! non-null assertion without checking - assert only when you've verified the value exists
  • Loose tsconfig - always start with "strict": true
  • Interface vs type for objects - prefer interface for public APIs (extensible), type for unions and computed types
  • Forgetting readonly - immutable arrays should use readonly T[] not T[]
  • Implicit any in catch - use catch (err: unknown) and narrow to Error before accessing .message

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.