Claude-skill-registry dev-typescript

FIX TypeScript errors, WRITE better types, and CONVERT JavaScript to TypeScript. Use when encountering type errors, improving type safety, or migrating JS files.

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/dev-typescript" ~/.claude/skills/majiayu000-claude-skill-registry-dev-typescript && rm -rf "$T"
manifest: skills/data/dev-typescript/SKILL.md
source content

TypeScript Skill

When to Use

Activate this skill when:

  • Encountering TypeScript compilation errors (TS2322, TS2345, TS7006, etc.)
  • Improving type safety in existing code
  • Converting JavaScript files to TypeScript
  • Writing new TypeScript interfaces, types, or generics
  • Working with Vue 3 + TypeScript patterns

Finding TypeScript Errors

CRITICAL:

npm run build
does NOT catch all TypeScript errors! Vite only transpiles, it doesn't type-check.

Commands to Find Errors

# Vue projects - ALWAYS run this to find TS errors
npm run type-check:watch

# Or one-time check
npx vue-tsc --noEmit

# Non-Vue TypeScript projects
npx tsc --noEmit

# Check specific file
npx vue-tsc --noEmit src/path/to/file.vue

Verification Workflow

  1. Before claiming code works: Run
    npm run type-check:watch
  2. After fixing errors: Run again to verify all resolved
  3. Before committing: Ensure zero TypeScript errors

Common Gotcha

CommandWhat it checks
npm run build
Syntax only (Vite transpiles) - NOT SUFFICIENT
npm run type-check:watch
Full type checking (vue-tsc) - USE THIS
npm run lint
ESLint rules (separate from TS errors)

Fix Type Errors

TS2322: Type 'X' is not assignable to type 'Y'

Cause: Assigning a value of incompatible type.

// BAD
const count: number = "5"

// GOOD - Option 1: Fix the type
const count: number = 5

// GOOD - Option 2: Change the annotation
const count: string = "5"

// GOOD - Option 3: Parse the value
const count: number = parseInt("5", 10)

TS2345: Argument type mismatch

Cause: Function argument doesn't match parameter type.

// BAD
function process(value: string) { /*...*/ }
process(undefined)

// GOOD - Handle undefined
function process(value: string | undefined) {
  if (!value) return
  // ...
}

// GOOD - Use type guard
function process(value: unknown) {
  if (typeof value === 'string') {
    // value is now string
  }
}

TS7006: Parameter implicitly has 'any' type

Cause: Missing type annotation on parameter.

// BAD
const handler = (event) => { /*...*/ }

// GOOD
const handler = (event: MouseEvent) => { /*...*/ }

// GOOD - For callbacks
array.map((item: string) => item.toUpperCase())

TS2532: Object is possibly 'undefined'

Cause: Accessing property on potentially undefined value.

// BAD
const name = user.profile.name

// GOOD - Optional chaining
const name = user?.profile?.name

// GOOD - Null check
if (user && user.profile) {
  const name = user.profile.name
}

// GOOD - Non-null assertion (when you're certain)
const name = user!.profile!.name

TS2339: Property does not exist on type

Cause: Accessing undeclared property.

// BAD
interface User { name: string }
const user: User = { name: 'John', age: 30 } // age doesn't exist

// GOOD - Add to interface
interface User {
  name: string
  age?: number  // Optional property
}

// GOOD - Use index signature
interface User {
  name: string
  [key: string]: unknown
}

Write Better Types

Use
unknown
Instead of
any

// BAD - any disables type checking
function parse(data: any) {
  return data.value  // No error, but unsafe
}

// GOOD - unknown requires type checking
function parse(data: unknown) {
  if (typeof data === 'object' && data !== null && 'value' in data) {
    return (data as { value: string }).value
  }
  throw new Error('Invalid data')
}

Use
satisfies
for Type Constraints

// BAD - Loses specific type info
const config: Record<string, string> = {
  apiUrl: 'https://api.example.com',
  timeout: '5000'  // We want to catch this isn't a number
}

// GOOD - Maintains inference + validates structure
const config = {
  apiUrl: 'https://api.example.com',
  timeout: 5000
} satisfies Record<string, string | number>

// config.apiUrl is typed as string, not string | number

Discriminated Unions for State

// Model different states with a discriminant property
type LoadingState =
  | { status: 'idle' }
  | { status: 'loading' }
  | { status: 'success'; data: string[] }
  | { status: 'error'; error: Error }

function handle(state: LoadingState) {
  switch (state.status) {
    case 'idle':
      return 'Ready'
    case 'loading':
      return 'Loading...'
    case 'success':
      return state.data.join(', ')  // TypeScript knows data exists
    case 'error':
      return state.error.message    // TypeScript knows error exists
  }
}

Template Literal Types

// Create dynamic string types
type EventName = 'click' | 'focus' | 'blur'
type Handler = `on${Capitalize<EventName>}`
// Result: 'onClick' | 'onFocus' | 'onBlur'

// API routes
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE'
type ApiRoute = `/api/${string}`
type Endpoint = `${HttpMethod} ${ApiRoute}`
// e.g., 'GET /api/users'

Generics with Constraints

// Constrain generic to specific shape
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key]
}

// With default type
function createStore<T = Record<string, unknown>>(initial: T) {
  let state = initial
  return {
    get: () => state,
    set: (value: T) => { state = value }
  }
}

Utility Types Reference

// Partial - Make all properties optional
type PartialUser = Partial<User>

// Required - Make all properties required
type RequiredUser = Required<User>

// Pick - Select specific properties
type UserName = Pick<User, 'firstName' | 'lastName'>

// Omit - Remove specific properties
type PublicUser = Omit<User, 'password' | 'secretKey'>

// Record - Create object type with specific keys
type Flags = Record<'dark' | 'compact', boolean>

// ReturnType - Extract function return type
type Result = ReturnType<typeof fetchData>

// Parameters - Extract function parameters
type Args = Parameters<typeof fetchData>

// NonNullable - Remove null and undefined
type DefiniteValue = NonNullable<string | null | undefined>  // string

// Extract/Exclude - Filter union types
type Numbers = Extract<string | number | boolean, number>  // number
type NotNumbers = Exclude<string | number | boolean, number>  // string | boolean

Convert JavaScript to TypeScript

Migration Workflow

  1. Rename file:
    .js
    .ts
    (or
    .vue
    stays same)
  2. Add explicit types to function parameters
  3. Define interfaces for objects and props
  4. Handle null/undefined with optional chaining or guards
  5. Run
    npm run build
    to find remaining errors

Common Conversions

// JavaScript
function fetchUser(id) {
  return fetch(`/api/users/${id}`).then(r => r.json())
}

// TypeScript
interface User {
  id: string
  name: string
  email: string
}

async function fetchUser(id: string): Promise<User> {
  const response = await fetch(`/api/users/${id}`)
  return response.json() as Promise<User>
}

Vue 3 + TypeScript

See

references/vue-typescript.md
for:

  • defineProps<T>()
    patterns
  • defineEmits<T>()
    (old and Vue 3.3+ syntax)
  • Pinia store typing
  • Composable return types
  • Template ref typing

Anti-Patterns to Avoid

Never Do This

// DON'T use any
const data: any = fetchData()

// DON'T use @ts-ignore without explanation
// @ts-ignore
brokenCode()

// DON'T use non-null assertion carelessly
user!.profile!.settings!.theme

// DON'T leave implicit any in parameters
array.map(x => x.value)  // x is implicitly any

Do This Instead

// Use unknown + type guards
const data: unknown = fetchData()
if (isValidData(data)) { /* use data */ }

// Use @ts-expect-error with reason
// @ts-expect-error - Legacy API returns number as string
const count: number = parseInt(response.value)

// Use optional chaining
user?.profile?.settings?.theme ?? 'default'

// Always type parameters
array.map((x: Item) => x.value)

tsconfig.json Recommendations

{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "noImplicitOverride": true,
    "exactOptionalPropertyTypes": true,
    "noPropertyAccessFromIndexSignature": true
  }
}

Additional References

  • references/common-errors.md
    - Detailed error solutions
  • references/utility-types.md
    - Full utility type guide
  • references/vue-typescript.md
    - Vue 3 + TypeScript patterns