typescript

TypeScript - Type-Safe JavaScript

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "typescript" with this command: npx skills add dsantiagomj/dsmj-ai-toolkit/dsantiagomj-dsmj-ai-toolkit-typescript

TypeScript - Type-Safe JavaScript

Static typing for JavaScript - works across frontend, backend, and full-stack applications

When to Use This Skill

Use this skill when:

  • Writing any JavaScript code that would benefit from type safety

  • Building Node.js backend applications

  • Developing React, Vue, or other frontend frameworks

  • Creating libraries or shared code packages

  • Working with APIs and need to define request/response types

  • Refactoring existing JavaScript to add type safety

Don't use this skill when:

  • Writing simple scripts where type safety adds no value (config files, build scripts)

  • Working with languages other than JavaScript/TypeScript (use their respective skills)

  • Types become more complex than the value they provide (prefer simplicity)

Critical Patterns

Pattern 1: Type Inference vs Explicit Types

When: Writing functions and variables

Good:

// ✅ Let TypeScript infer simple types const name = 'John' // inferred as string const age = 30 // inferred as number const items = [1, 2, 3] // inferred as number[]

// ✅ Explicit types for function signatures (better docs) function processUser(id: string, options?: { admin: boolean }): User { return { id, name: 'John', isAdmin: options?.admin ?? false } }

// ✅ Infer return type for simple functions const double = (n: number) => n * 2 // return type inferred as number

// ✅ Explicit return type for complex functions async function fetchUser(id: string): Promise<User | null> { const response = await fetch(/api/users/${id}) if (!response.ok) return null return response.json() }

Bad:

// ❌ Over-annotating obvious types const name: string = 'John' // unnecessary const age: number = 30 // unnecessary

// ❌ Missing function parameter types function processUser(id, options) { // any, any - loses type safety return { id, name: 'John' } }

// ❌ Missing return types on exported functions export function fetchUser(id: string) { // return type unclear return fetch(/api/users/${id}).then(r => r.json()) }

Why: Let TypeScript infer simple types to reduce noise. Add explicit types for function signatures to improve documentation and catch errors.

Pattern 2: Discriminated Unions for Type Safety

When: Handling multiple possible states or types

Good:

// ✅ Discriminated union with type field type Result<T> = | { status: 'success'; data: T } | { status: 'error'; error: string } | { status: 'loading' }

function handleResult<T>(result: Result<T>) { if (result.status === 'success') { console.log(result.data) // ✅ TypeScript knows data exists } else if (result.status === 'error') { console.log(result.error) // ✅ TypeScript knows error exists } else { console.log('Loading...') // ✅ TypeScript knows no extra fields } }

// ✅ Discriminated union for different shapes type Shape = | { kind: 'circle'; radius: number } | { kind: 'rectangle'; width: number; height: number } | { kind: 'square'; size: number }

function calculateArea(shape: Shape): number { switch (shape.kind) { case 'circle': return Math.PI * shape.radius ** 2 case 'rectangle': return shape.width * shape.height case 'square': return shape.size ** 2 } }

Bad:

// ❌ Union without discriminator type Result<T> = { data?: T error?: string loading?: boolean }

function handleResult<T>(result: Result<T>) { if (result.data) { console.log(result.data) // ❌ Could still have error or loading } }

Why: Discriminated unions provide exhaustive type checking and make illegal states unrepresentable.

Pattern 3: Generic Constraints for Reusable Code

When: Writing reusable functions that work with multiple types

Good:

// ✅ Generic with constraints interface HasId { id: string }

function findById<T extends HasId>(items: T[], id: string): T | undefined { return items.find(item => item.id === id) // ✅ TypeScript knows T has id }

// ✅ keyof constraint function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key] // ✅ Type-safe property access }

const user = { name: 'John', age: 30 } const name = getProperty(user, 'name') // string const age = getProperty(user, 'age') // number

Bad:

// ❌ Generic without constraints function findById<T>(items: T[], id: string): T | undefined { return items.find(item => item.id === id) // ❌ Error: 'id' doesn't exist on T }

// ❌ Using any loses type safety function getProperty(obj: any, key: string): any { return obj[key] // ❌ No type safety }

Why: Generic constraints ensure type safety while maintaining flexibility.

Pattern 4: Utility Types for Type Transformations

When: Deriving new types from existing ones

Good:

interface User { id: string name: string email: string password: string role: 'admin' | 'user' createdAt: Date }

// ✅ Pick for selecting specific properties type UserPreview = Pick<User, 'id' | 'name'>

// ✅ Omit for excluding sensitive data type PublicUser = Omit<User, 'password'>

// ✅ Partial for optional updates function updateUser(id: string, updates: Partial<User>) { // ✅ Can update any subset of user fields }

// ✅ Record for maps/dictionaries type UserMap = Record<string, User> const users: UserMap = { 'user1': { id: '1', name: 'John', /* ... */ }, }

// ✅ ReturnType to extract function return type function getUser() { return { id: '1', name: 'John', email: 'john@example.com' } }

type UserType = ReturnType<typeof getUser>

Bad:

// ❌ Manually duplicating types type UserPreview = { id: string // ❌ Duplicated from User name: string // ❌ Duplicated from User }

Why: Utility types keep types DRY and automatically sync with source types.

For more examples and patterns, see references/examples.md and references/patterns.md.

Anti-Patterns

❌ Anti-Pattern 1: Overusing any

Don't do this:

// ❌ Using any defeats the purpose of TypeScript function processData(data: any): any { return data.map((item: any) => item.value) }

Why it's bad: Disables TypeScript's type checking, loses IntelliSense.

Do this instead:

// ✅ Use specific types or generics function processData<T extends { value: any }>(data: T[]): any[] { return data.map(item => item.value) }

// ✅ Or use unknown for truly unknown data function processUnknown(data: unknown) { if (typeof data === 'string') { return data.toUpperCase() // ✅ Type narrowed to string } throw new Error('Invalid data') }

❌ Anti-Pattern 2: Type Assertions Instead of Type Guards

Don't do this:

// ❌ Unsafe type assertion function processUser(data: any) { const user = data as User // ❌ No runtime check console.log(user.name.toUpperCase()) // ❌ Runtime error if not a User }

Why it's bad: Type assertions bypass type checking and can cause runtime errors.

Do this instead:

// ✅ Type guards for runtime validation function isUser(data: unknown): data is User { return ( typeof data === 'object' && data !== null && 'id' in data && 'name' in data ) }

function processUser(data: unknown) { if (isUser(data)) { console.log(data.name.toUpperCase()) // ✅ Type-safe } }

❌ Anti-Pattern 3: Ignoring Strict Mode

Don't do this:

// tsconfig.json { "compilerOptions": { "strict": false // ❌ Disables all strict checks } }

Why it's bad: Disables TypeScript's most valuable features.

Do this instead:

// tsconfig.json { "compilerOptions": { "strict": true, // ✅ Enable all strict checks "noUncheckedIndexedAccess": true, "noImplicitOverride": true } }

For more anti-patterns and solutions, see references/best-practices.md.

Quick Reference

// Type annotations let name: string = 'John' let numbers: number[] = [1, 2, 3]

// Interfaces interface User { id: string name: string email?: string // Optional }

// Type aliases type ID = string | number type Status = 'idle' | 'loading' | 'success'

// Functions function add(a: number, b: number): number { return a + b }

// Generics function identity<T>(value: T): T { return value }

// Utility types Partial<T> // All properties optional Required<T> // All properties required Readonly<T> // All properties readonly Pick<T, K> // Select properties Omit<T, K> // Exclude properties Record<K, T> // Object with keys K and values T

Learn More

  • Examples: references/examples.md - Complete code examples for basic and advanced usage

  • Advanced Patterns: references/patterns.md - Mapped types, conditional types, advanced generics

  • Configuration: references/configuration.md - tsconfig.json, compiler options

  • Advanced Types: references/advanced-types.md - Template literals, branded types

  • Best Practices: references/best-practices.md - Code organization, naming conventions

  • Node.js Patterns: references/nodejs.md - Express, Prisma, tRPC integration

External References

  • TypeScript Documentation

  • TypeScript Handbook

Maintained by dsmj-ai-toolkit

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

patterns

No summary provided by upstream source.

Repository SourceNeeds Review
General

zustand

No summary provided by upstream source.

Repository SourceNeeds Review
General

react

No summary provided by upstream source.

Repository SourceNeeds Review
General

vercel-ai-sdk

No summary provided by upstream source.

Repository SourceNeeds Review