#!/usr/bin/env python3
"""
Generate comprehensive TypeScript training data for Dev-AI.
Covers: type system, generics, utility types, strict mode, declaration files,
module resolution, discriminated unions, conditional types, framework integration.
Target: 120-150 high-quality entries.
"""
import json
import os
import random
from pathlib import Path
BASE = Path(__file__).parent.parent / "data" / "processed"
def g(issue, bad, good):
return {
"messages": [
{"role": "user", "content": f"Review this TypeScript code:\n\n```typescript\n{bad}\n```"},
{"role": "assistant", "content": f"**Issue: {issue}**\n\n**Fix:**\n```typescript\n{good}\n```"}
],
"source": "typescript_training",
"tags": ["typescript", issue.lower().replace(" ", "_")]
}
entries = []
# =============================================================================
# TYPE SYSTEM FUNDAMENTALS (15 entries)
# =============================================================================
entries.append(g(
"Any type escape hatch",
"function processData(data: any): any {\n return data;\n}",
"function processData<T>(data: T): T {\n return data;\n}\n// Generics preserve type information. any disables type checking."
))
entries.append(g(
"Implicit any from missing annotation",
"function greet(name) {\n return `Hello, ${name}`;\n}",
"function greet(name: string): string {\n return `Hello, ${name}`;\n}\n// Enable noImplicitAny in tsconfig.json"
))
entries.append(g(
"Union type without narrowing",
"function getId(id: string | number): string {\n return id.toString(); // Works but hides type issues\n}",
"function getId(id: string | number): string {\n if (typeof id === 'string') {\n return id; // Type narrowed to string\n }\n return id.toString(); // Type narrowed to number\n}\n// Explicit narrowing makes intent clear"
))
entries.append(g(
"Excess property check bypass",
"interface Config {\n host: string;\n port: number;\n}\nconst c: Config = { host: 'localhost', port: 80, debug: true };",
"// TypeScript allows excess properties in object literals\n// Fix: either add the property or use type assertion with intent\ninterface Config {\n host: string;\n port: number;\n debug?: boolean; // Add if valid\n}"
))
entries.append(g(
"Using interface for everything",
"interface Point {\n x: number;\n y: number;\n}",
"// Use type aliases for unions, intersections, tuples\n// Use interfaces for object shapes with declaration merging\ntype Point = { x: number; y: number };\ntype Position = Point & { z: number };\ntype ColorPoint = Point | { color: string };\n// Interfaces support declaration merging and implements"
))
entries.append(g(
"Missing readonly",
"interface User {\n id: number;\n name: string;\n}\n// id should never change",
"interface User {\n readonly id: number;\n name: string;\n}\n// readonly prevents assignment after initialization"
))
entries.append(g(
"Type assertion with as any",
"const data = rawData as any;\nconst name = data.name;",
"// Fix the type definition instead:\ninterface ResponseData {\n name: string;\n [key: string]: unknown;\n}\nconst data = rawData as ResponseData;\nconst name = data.name;"
))
entries.append(g(
"Never type misunderstanding",
"function throwError(msg: string): never {\n console.log(msg);\n return;\n // Doesn't actually return never\n}",
"function throwError(msg: string): never {\n throw new Error(msg);\n // never means this function never returns\n // Use for: infinite loops, always throwing, exhaustive checks\n}"
))
entries.append(g(
"Missing null in union",
"function findUser(id: number): User {\n return users.find(u => u.id === id);\n // Returns User | undefined\n}",
"function findUser(id: number): User | undefined {\n return users.find(u => u.id === id);\n}\n// Enable strictNullChecks to catch this"
))
entries.append(g(
"Index signature too broad",
"interface Store {\n [key: string]: any;\n}",
"interface Store {\n [key: string]: unknown;\n // Or be specific:\n // [key: string]: string | number | boolean;\n}\n// unknown forces type checking on reads"
))
entries.append(g(
"Type vs Interface for union",
"interface A { type: 'a' }\ninterface B { type: 'b' }\nconst x: A | B = ...;",
"// This is fine, but for complex unions, use type:\ntype Action =\n | { type: 'ADD'; payload: number }\n | { type: 'REMOVE'; payload: string }\n | { type: 'RESET' };\n// type is required for unions, intersections, mapped types"
))
entries.append(g(
"Missing keyof constraint",
"function getProp(obj: object, key: string) {\n return obj[key]; // No type safety\n}",
"function getProp<T, K extends keyof T>(obj: T, key: K): T[K] {\n return obj[key];\n // K is constrained to valid keys of T\n // Return type is T[K]\n}"
))
entries.append(g(
"Using object type",
"function log(obj: object) {\n console.log(obj);\n // object accepts arrays, functions, etc.\n}",
"function log(obj: Record<string, unknown>) {\n console.log(obj);\n // Record<string, unknown> is the type-safe version of \"plain object\"\n}"
))
entries.append(g(
"Non-null assertion abuse",
"const element = document.getElementById('app')!;\nconst text = element.textContent!;\nconst trimmed = text.trim()!;",
"const element = document.getElementById('app');\nif (!element) throw new Error('App element not found');\nconst text = element.textContent;\nif (text === null) return;\nconst trimmed = text.trim();\n// Prefer explicit checks over non-null assertions"
))
entries.append(g(
"Missing explicit return type",
"const calculate = (a: number, b: number) => {\n return a + b;\n}",
"const calculate = (a: number, b: number): number => {\n return a + b;\n};\n// Explicit return types document intent and catch bugs"
))
# =============================================================================
# GENERICS (10 entries)
# =============================================================================
entries.append(g(
"Generic without constraint",
"function first<T>(arr: T[]): T {\n return arr[0];\n // Returns T | undefined\n}",
"function first<T>(arr: readonly T[]): T | undefined {\n return arr[0];\n}\n// Handle empty arrays. readonly works with mutable arrays too."
))
entries.append(g(
"Multiple generic parameters unused",
"function merge<A, B, C>(a: A, b: B): A & B {\n return { ...a, ...b };\n // C is declared but unused\n}",
"function merge<A, B>(a: A, b: B): A & B {\n return { ...a, ...b };\n}\n// Only declare generics you actually use"
))
entries.append(g(
"Generic default value missing",
"function createMap<K, V>(): Map<K, V> {\n return new Map();\n // Callers must always specify types\n}",
"function createMap<K, V = unknown>(): Map<K, V> {\n return new Map();\n}\n// Default V to unknown so callers can omit it"
))
entries.append(g(
"Extends constraint too broad",
"function parse<T extends any>(json: string): T {\n return JSON.parse(json);\n // any defeats type safety\n}",
"function parse<T extends object>(json: string): T {\n return JSON.parse(json);\n}\n// Or better: use a schema validation library\n// Don't lie about types at parse time"
))
entries.append(g(
"Generic array type instead of readonly",
"function logItems(items: string[]): void {\n for (const item of items) {\n console.log(item);\n }\n}\n// Function doesn't mutate the array",
"function logItems(items: readonly string[]): void {\n for (const item of items) {\n console.log(item);\n }\n}\n// readonly signals: this function won't modify the array"
))
entries.append(g(
"Missing infer in conditional type",
"type UnwrapPromise<T> = T extends Promise<unknown>\n ? T : T;\n // Doesn't extract the inner type",
"type UnwrapPromise<T> = T extends Promise<infer U>\n ? U\n : T;\n// infer creates a new type variable from the matched pattern"
))
entries.append(g(
"Generic extends vs implements",
"class Result {\n value: any;\n}\n// Not generic - loses type information",
"class Result<T> {\n constructor(public value: T) {}\n isSuccess(): this is Success<T> {\n return true;\n }\n}\nclass Success<T> extends Result<T> {}\nclass Failure<E> extends Result<E> {}"
))
entries.append(g(
"Not using generic constraint for method params",
"interface Repository {\n find(id: any): any;\n save(entity: any): void;\n}",
"interface Repository<T, ID = number> {\n find(id: ID): T | undefined;\n save(entity: T): void;\n findAll(): T[];\n}\n// Generic interface constrains method types"
))
entries.append(g(
"Generic function in interface",
"interface API {\n get: (url: string) => any;\n post: (url: string, body: any) => any;\n}",
"interface API {\n get<T>(url: string): Promise<T>;\n post<T, B>(url: string, body: B): Promise<T>;\n delete<T>(url: string): Promise<T>;\n}\n// Generic methods preserve type information"
))
entries.append(g(
"Generic utility function missing",
"function isString(val: unknown): boolean {\n return typeof val === 'string';\n}\n// No type narrowing on use",
"function isString(val: unknown): val is string {\n return typeof val === 'string';\n}\n// Type predicate narrows the type in conditionals\n// Usage: if (isString(val)) { val.toUpperCase() }"
))
# =============================================================================
# UTILITY TYPES (12 entries)
# =============================================================================
entries.append(g(
"Manual partial type",
"interface User {\n name: string;\n email: string;\n age: number;\n}\ninterface UserUpdate {\n name?: string;\n email?: string;\n age?: number;\n}\n// Duplicated",
"interface User {\n name: string;\n email: string;\n age: number;\n}\ntype UserUpdate = Partial<User>;\n// Partial makes all properties optional"
))
entries.append(g(
"Manual readonly type",
"interface Config {\n host: string;\n port: number;\n}\ninterface ReadonlyConfig {\n readonly host: string;\n readonly port: number;\n}",
"interface Config {\n host: string;\n port: number;\n}\ntype ReadonlyConfig = Readonly<Config>;\n// Readonly makes all properties readonly"
))
entries.append(g(
"Manual pick type",
"interface User {\n id: number;\n name: string;\n email: string;\n password: string;\n}\ninterface PublicUser {\n id: number;\n name: string;\n email: string;\n}\n// Manually picking fields",
"interface User {\n id: number;\n name: string;\n email: string;\n password: string;\n}\ntype PublicUser = Pick<User, 'id' | 'name' | 'email'>;\n// Pick selects specific properties"
))
entries.append(g(
"Manual omit type",
"interface User {\n id: number;\n name: string;\n email: string;\n password: string;\n}\ntype SafeUser = {\n id: number;\n name: string;\n email: string;\n};",
"interface User {\n id: number;\n name: string;\n email: string;\n password: string;\n}\ntype SafeUser = Omit<User, 'password'>;\n// Omit excludes specific properties"
))
entries.append(g(
"Not using Required",
"interface Options {\n host?: string;\n port?: number;\n}\n// Need a version with all required",
"interface Options {\n host?: string;\n port?: number;\n}\ntype ResolvedOptions = Required<Options>;\n// Required makes all properties required"
))
entries.append(g(
"Manual record type",
"interface StatusMap {\n pending: string;\n active: string;\n inactive: string;\n}",
"type Status = 'pending' | 'active' | 'inactive';\ntype StatusMap = Record<Status, string>;\n// Record creates a mapping type from keys to values"
))
entries.append(g(
"Not using Extract",
"type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';\ntype SafeMethods = 'GET' | 'DELETE';\n// Manually listing subset",
"type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';\ntype SafeMethods = Extract<HttpMethod, 'GET' | 'DELETE'>;\n// Extract filters union types to matching members"
))
entries.append(g(
"Not using Exclude",
"type AllStatus = 'draft' | 'published' | 'archived';\ntype ActiveStatus = 'draft' | 'published';\n// Manually listing subset",
"type AllStatus = 'draft' | 'published' | 'archived';\ntype ActiveStatus = Exclude<AllStatus, 'archived'>;\n// Exclude removes specific members from union"
))
entries.append(g(
"Not using ReturnType",
"function createUser(): User {\n return { id: 1, name: 'Alice' };\n}\ninterface Creator {\n result: User; // Duplicated return type\n}",
"function createUser(): User {\n return { id: 1, name: 'Alice' };\n}\ntype Creator = {\n result: ReturnType<typeof createUser>;\n // Infers User from the function\n}"
))
entries.append(g(
"Not using Parameters",
"function fetchUser(id: number, options: FetchOptions): Promise<User> {\n // ...\n}\n// Need the parameter types elsewhere",
"function fetchUser(id: number, options: FetchOptions): Promise<User> {\n // ...\n}\ntype FetchUserArgs = Parameters<typeof fetchUser>;\n// Extracts [number, FetchOptions] as a tuple type"
))
entries.append(g(
"Manual tuple type",
"interface Coord {\n 0: number;\n 1: number;\n length: 2;\n}",
"type Coord = [x: number, y: number];\n// Tuple types are cleaner for fixed-length arrays\nconst [x, y] = coord;\n// Destructuring works with proper types"
))
entries.append(g(
"Not using Awaited",
"function fetchData(): Promise<Data> { return ...; }\ntype Result = ReturnType<typeof fetchData>;\n// Result is Promise<Data>, not Data",
"function fetchData(): Promise<Data> { return ...; }\ntype Result = Awaited<ReturnType<typeof fetchData>>;\n// Awaited unwraps the Promise, giving Data"
))
# =============================================================================
# DISCRIMINATED UNIONS (10 entries)
# =============================================================================
entries.append(g(
"Non-exhaustive union handling",
"type Shape = { kind: 'circle'; radius: number } | { kind: 'square'; size: number };\nfunction area(s: Shape): number {\n if (s.kind === 'circle') return Math.PI * s.radius ** 2;\n return s.size ** 2;\n // No error if new variant added\n}",
"type Shape = { kind: 'circle'; radius: number } | { kind: 'square'; size: number };\nfunction area(s: Shape): number {\n switch (s.kind) {\n case 'circle': return Math.PI * s.radius ** 2;\n case 'square': return s.size ** 2;\n default: {\n const _exhaustive: never = s;\n throw new Error(`Unhandled shape: ${_exhaustive}`);\n }\n }\n}\n// TypeScript catches missing cases at compile time"
))
entries.append(g(
"Missing discriminant property",
"type Result = { data: T } | { error: string };\n// No discriminant - can't narrow",
"type Result<T, E = Error> =\n | { ok: true; data: T }\n | { ok: false; error: E };\n// 'ok' field discriminates between variants"
))
entries.append(g(
"Narrowing with typeof instead of discriminant",
"type Action = { type: 'ADD'; value: number } | { type: 'SET'; value: string };\nfunction handle(a: Action) {\n if (typeof a.type === 'string') { /* not helpful */ }\n}",
"type Action = { type: 'ADD'; value: number } | { type: 'SET'; value: string };\nfunction handle(a: Action) {\n if (a.type === 'ADD') {\n // a is narrowed to { type: 'ADD'; value: number }\n console.log(a.value); // number\n }\n if (a.type === 'SET') {\n // a is narrowed to { type: 'SET'; value: string }\n console.log(a.value); // string\n }\n}"
))
entries.append(g(
"Optional discriminant field",
"type Message =\n | { type: 'text'; content: string }\n | { type: 'image'; url?: string };\n// url is optional - confusing with discriminant",
"type Message =\n | { type: 'text'; content: string }\n | { type: 'image'; url: string };\n// Discriminant variants should have required fields\n// Use separate optional fields if truly optional"
))
entries.append(g(
"Union with overlapping properties",
"type A = { x: number; y: number };\ntype B = { x: number; z: string };\ntype AB = A | B;\n// Can't distinguish A from B",
"type A = { kind: 'a'; x: number; y: number };\ntype B = { kind: 'b'; x: number; z: string };\ntype AB = A | B;\n// Discriminant 'kind' makes narrowing possible"
))
entries.append(g(
"Not using discriminated union for API responses",
"interface ApiResponse {\n success: boolean;\n data?: User;\n error?: string;\n}\n// Both data and error optional - which is set?",
"type ApiResponse<T, E = string> =\n | { success: true; data: T; error?: never }\n | { success: false; data?: never; error: E };\n// Exactly one of data/error is present"
))
entries.append(g(
"Exhaustiveness check missing in reducer",
"function reducer(state: State, action: Action): State {\n if (action.type === 'INCREMENT') return { count: state.count + 1 };\n return state;\n}",
"function reducer(state: State, action: Action): State {\n switch (action.type) {\n case 'INCREMENT': return { count: state.count + 1 };\n case 'DECREMENT': return { count: state.count - 1 };\n default: {\n const _exhaustive: never = action;\n throw new Error(`Unknown action: ${_exhaustive}`);\n }\n }\n}"
))
entries.append(g(
"Using boolean discriminant",
"type Result = { ok: true; data: Value } | { ok: false; error: Error };\n// Correct, but verbose",
"type Result<T, E = Error> =\n | { ok: true; data: T; error?: never }\n | { ok: false; data?: never; error: E };\n// Generics make it reusable. 'never' prevents the other field"
))
entries.append(g(
"Discriminant typo",
"type Action = { type: 'ADD'; val: number } | { type: 'REMOVE'; val: number };\nfunction handle(a: Action) {\n if (a.type === 'ADDD') { /* typo - never matches */ }\n}",
"// TypeScript should catch this at compile time if using switch + exhaustiveness\nfunction handle(a: Action) {\n switch (a.type) {\n case 'ADD': /* ... */ break;\n case 'REMOVE': /* ... */ break;\n default: {\n const _exhaustive: never = a;\n throw new Error(`Unknown: ${_exhaustive}`);\n }\n }\n}"
))
entries.append(g(
"Narrowing with in operator",
"type Animal = { type: 'dog'; bark(): void } | { type: 'cat'; meow(): void };\nfunction makeSound(a: Animal) {\n if ('bark' in a) a.bark();\n else a.meow();\n}",
"type Animal =\n | { type: 'dog'; bark(): void }\n | { type: 'cat'; meow(): void };\nfunction makeSound(a: Animal) {\n if (a.type === 'dog') a.bark();\n else a.meow();\n // Discriminant narrowing is clearer than 'in'\n}"
))
# =============================================================================
# CONDITIONAL TYPES (8 entries)
# =============================================================================
entries.append(g(
"Manual type branching",
"type StringOrNum = string | number;\ntype Processed<T> = T;\n// Doesn't differentiate based on input type",
"type Processed<T> = T extends string\n ? T extends `${number}`\n ? number\n : T\n : T extends number\n ? string\n : T;\n// Conditional types branch based on type relationships"
))
entries.append(g(
"Not using distributive conditional",
"type NonNullable<T> = T;\n// Doesn't handle unions properly",
"type NonNullable<T> = T extends null | undefined ? never : T;\n// Distributive: applied to each member of union\n// NonNullable<string | null | undefined> => string"
))
entries.append(g(
"Missing extends never branch",
"type ToArray<T> = [T];\n// Works but doesn't handle edge cases",
"type ToArray<T> = T extends never ? [] : [T];\n// Handles the never case explicitly"
))
entries.append(g(
"Complex conditional without helper",
"type GetAPIResponse<T> = T extends 'user'\n ? { name: string; email: string }\n : T extends 'post'\n ? { title: string; body: string }\n : T extends 'comment'\n ? { text: string; author: string }\n : never;\n// Hard to maintain",
"type APIEndpoints = {\n user: { name: string; email: string };\n post: { title: string; body: string };\n comment: { text: string; author: string };\n};\ntype GetAPIResponse<T extends keyof APIEndpoints> = APIEndpoints[T];\n// Lookup type - single source of truth"
))
entries.append(g(
"Not using keyof with conditional",
"type GetKey<T, K> = T[K];\n// Fails if K is not a key of T",
"type GetKey<T, K extends keyof T> = T[K];\n// K is constrained to keys of T\n// Type error if K is invalid"
))
entries.append(g(
"Missing template literal types",
"type Color = 'red' | 'green' | 'blue';\ntype BackgroundColor = 'bg-red' | 'bg-green' | 'bg-blue';\n// Duplicated",
"type Color = 'red' | 'green' | 'blue';\ntype BackgroundColor = `bg-${Color}`;\n// Template literal types generate derived string types"
))
entries.append(g(
"Not using mapped types",
"interface Props {\n name: string;\n age: number;\n email: string;\n}\ninterface PropsOptional {\n name?: string;\n age?: number;\n email?: string;\n}\ninterface PropsReadonly {\n readonly name: string;\n readonly age: number;\n readonly email: string;\n}",
"interface Props {\n name: string;\n age: number;\n email: string;\n}\ntype PropsOptional = { [K in keyof Props]?: Props[K] };\ntype PropsReadonly = { readonly [K in keyof Props]: Props[K] };\n// Mapped types derive from existing types"
))
entries.append(g(
"Not using keyof with mapped type",
"type Mutable<T> = T;\n// Doesn't remove readonly",
"type Mutable<T> = {\n -readonly [K in keyof T]: T[K];\n};\n// -readonly modifier removes readonly flag\n// Also: -?, +?, +readonly, -readonly"
))
# =============================================================================
# STRICT MODE & CONFIG (8 entries)
# =============================================================================
entries.append(g(
"Missing strict in tsconfig",
"{\n \"compilerOptions\": {\n \"target\": \"ES2020\",\n \"module\": \"commonjs\"\n }\n}",
"{\n \"compilerOptions\": {\n \"target\": \"ES2022\",\n \"module\": \"ESNext\",\n \"strict\": true,\n \"esModuleInterop\": true,\n \"skipLibCheck\": true,\n \"forceConsistentCasingInFileNames\": true,\n \"resolveJsonModule\": true,\n \"isolatedModules\": true\n }\n}"
))
entries.append(g(
"Using skipLibCheck true blindly",
"// tsconfig.json\n{\n \"compilerOptions\": {\n \"skipLibCheck\": true\n }\n}\n// Hides type errors in dependencies",
"// skipLibCheck: true is reasonable for performance,\n// but fix root causes:\n// 1. Update dependencies\n// 2. Use @ts-ignore sparingly\n// 3. Prefer type-safe alternatives"
))
entries.append(g(
"Missing noImplicitReturns",
"function getStatus(code: number): string {\n if (code === 200) return 'OK';\n if (code === 404) return 'Not Found';\n // Missing default return - implicit undefined\n}",
"function getStatus(code: number): string {\n if (code === 200) return 'OK';\n if (code === 404) return 'Not Found';\n return 'Unknown'; // Explicit return\n}\n// Enable noImplicitReturns to catch this"
))
entries.append(g(
"Missing noUncheckedIndexedAccess",
"const arr: string[] = ['a', 'b'];\nconst val = arr[2]; // string, but actually undefined",
"const arr: string[] = ['a', 'b'];\nconst val = arr[2]; // string | undefined\n// Enable noUncheckedIndexedAccess in tsconfig.json\n// Array access may return undefined"
))
entries.append(g(
"Using triple-slash directives",
"/// <reference types=\"node\" />\nconst fs = require('fs');",
"// Use proper imports instead:\nimport * as fs from 'node:fs';\n// Add @types/node to devDependencies\n// Remove triple-slash directives"
))
entries.append(g(
"Missing path mapping",
"import { utils } from '../../../../shared/utils';\nimport { models } from '../../../../models';\n// Deep relative imports",
"// tsconfig.json\n{\n \"compilerOptions\": {\n \"baseUrl\": \".\",\n \"paths\": {\n \"@shared/*\": [\"src/shared/*\"],\n \"@models/*\": [\"src/models/*\"]\n }\n }\n}\n// Absolute imports from project root\nimport { utils } from '@shared/utils';\nimport { User } from '@models/user';"
))
entries.append(g(
"Not using ts-node for dev",
"tsc && node dist/index.js\n// Compile then run",
"// tsconfig.json has \"sourceMap\": true\nnpx ts-node src/index.ts\n// Or: tsx src/index.ts (faster)\n// Direct TypeScript execution without compilation step"
))
entries.append(g(
"Missing declaration for .d.ts files",
"// utils.ts\nexport function helper() { return 42; }\n// No declaration file for library consumers",
"// tsconfig.json\n{\n \"compilerOptions\": {\n \"declaration\": true,\n \"declarationMap\": true,\n \"declarationDir\": \"dist/types\"\n }\n}\n// Generates .d.ts files for library consumers"
))
# =============================================================================
# DECLARATIONS & MODULES (8 entries)
# =============================================================================
entries.append(g(
"Ambient type declaration abuse",
"// global.d.ts\ndeclare let __APP_CONFIG__: any;\ndeclare function __VERSION__(): string;",
"// Use proper module imports:\nimport { APP_CONFIG } from './config';\nimport { VERSION } from './version';\n// Ambient declarations bypass type checking"
))
entries.append(g(
"Missing @types package",
"// Using a library without types\nimport * as _ from 'lodash';\n// No type definitions",
"// npm install --save-dev @types/lodash\n// TypeScript will automatically use @types/* packages\n// For libraries without types: create a .d.ts file\n// Or contribute types to DefinitelyTyped"
))
entries.append(g(
"Module augmentation conflict",
"// file1.d.ts\ndeclare module 'express' {\n interface Request { user: User; }\n}\n// file2.d.ts\ndeclare module 'express' {\n interface Request { tenant: Tenant; }\n}\n// Both augment - fine, but be careful",
"// Module augmentation is additive (declaration merging)\n// Both files work together:\ndeclare module 'express' {\n interface Request {\n user?: User;\n tenant?: Tenant;\n }\n}\n// Group related augmentations in one file"
))
entries.append(g(
"Namespace pollution",
"namespace MyApp {\n export class User { }\n export class Product { }\n export class Order { }\n // Everything in one namespace\n}",
"// Use ES modules instead of namespaces:\n// user.ts\nexport class User { }\n\n// product.ts\nexport class Product { }\n\n// order.ts\nexport class Order { }\n\n// Import what you need:\nimport { User } from './user';\nimport { Order } from './order';"
))
entries.append(g(
"Missing export from index",
"// utils.ts\nexport function add(a: number, b: number): number { return a + b; }\nexport function sub(a: number, b: number): number { return a - b; }\n// Consumers import from deep path",
"// index.ts\nexport { add, sub } from './utils';\nexport { multiply, divide } from './math';\n\n// Consumer:\nimport { add, multiply } from './lib';\n// Barrel file for clean imports"
))
entries.append(g(
"Circular dependency",
"// a.ts\nimport { b } from './b';\nexport function a() { return b(); }\n\n// b.ts\nimport { a } from './a';\nexport function b() { return a(); }\n// Circular import",
"// Break circular dependencies:\n// 1. Extract shared types to a separate file\n// 2. Use dependency injection\n// 3. Lazy import for runtime-only dependencies\nimport('./b').then(({ b }) => b());\n// Or use forward references for type-only imports\nimport type { B } from './b';"
))
entries.append(g(
"Not using export type",
"export interface Config {\n host: string;\n port: number;\n}\n// Runtime value exported unnecessarily",
"export type Config = {\n host: string;\n port: number;\n};\n// Or for interfaces that are only used as types:\nexport type { Config } from './config';\n// export type is type-only, stripped at compile time"
))
entries.append(g(
"Missing module resolution",
"// tsconfig.json\n{\n \"compilerOptions\": {\n \"moduleResolution\": \"classic\"\n }\n}\n// Doesn't resolve .js extensions or node_modules",
"// tsconfig.json\n{\n \"compilerOptions\": {\n \"moduleResolution\": \"node16\",\n \"module\": \"Node16\",\n \"resolvePackageJsonImports\": true\n }\n}\n// Node16 matches Node.js module resolution"
))
# =============================================================================
# ERROR HANDLING & SAFETY (10 entries)
# =============================================================================
entries.append(g(
"Empty catch block",
"try {\n parseJSON(data);\n} catch (e) {\n // Swallows all errors\n}",
"try {\n parseJSON(data);\n} catch (e) {\n if (e instanceof SyntaxError) {\n console.error('Invalid JSON:', e.message);\n return fallbackValue;\n }\n throw e; // Re-throw unexpected errors\n}"
))
entries.append(g(
"Catching generic Error",
"try {\n await fetchUser(id);\n} catch (error) {\n console.log('Error');\n // No type narrowing on error\n}",
"try {\n await fetchUser(id);\n} catch (error) {\n if (error instanceof ValidationError) {\n showFormErrors(error.fields);\n } else if (error instanceof NotFoundError) {\n showNotFound();\n } else {\n showError('Unexpected error');\n }\n}"
))
entries.append(g(
"Type guard missing for error",
"function handleError(error: unknown): void {\n console.log(error.message); // Error - unknown has no message\n}",
"function handleError(error: unknown): void {\n if (error instanceof Error) {\n console.error(error.message, error.stack);\n } else {\n console.error('Unknown error:', error);\n }\n}"
))
entries.append(g(
"Custom error missing name property",
"class NotFoundError extends Error {\n constructor(resource: string) {\n super(`${resource} not found`);\n }\n}",
"class NotFoundError extends Error {\n constructor(public resource: string) {\n super(`${resource} not found`);\n this.name = 'NotFoundError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n// name property for instanceof checks\n// setPrototypeOf for proper instanceof behavior"
))
entries.append(g(
"Using Result type instead of exceptions",
"async function getUser(id: number): Promise<User> {\n const user = await db.find(id);\n if (!user) throw new NotFoundError('User');\n return user;\n}",
"type Result<T, E = Error> =\n | { ok: true; data: T }\n | { ok: false; error: E };\n\nasync function getUser(id: number): Promise<Result<User>> {\n const user = await db.find(id);\n if (!user) return { ok: false, error: new NotFoundError('User') };\n return { ok: true, data: user };\n}\n// Result type forces callers to handle both cases"
))
entries.append(g(
"Missing instanceof check in catch",
"try {\n JSON.parse(input);\n} catch (e) {\n // e is unknown in strict mode\n handleParseError(e);\n}",
"try {\n JSON.parse(input);\n} catch (e) {\n if (e instanceof SyntaxError) {\n handleParseError(e.message);\n } else {\n throw e;\n }\n}"
))
entries.append(g(
"Throwing string instead of Error",
"function validate(value: string): void {\n if (value.length === 0) {\n throw 'Value is required';\n }\n}",
"function validate(value: string): void {\n if (value.length === 0) {\n throw new ValidationError('Value is required');\n }\n}\n// Always throw Error instances - strings lack stack traces"
))
entries.append(g(
"Not typing caught error",
"try {\n riskyOperation();\n} catch (e) {\n // e: unknown in strict mode\n log(e.message); // Compile error\n}",
"try {\n riskyOperation();\n} catch (e: unknown) {\n if (e instanceof Error) {\n logger.error(e.message, e.stack);\n } else {\n logger.error('Unknown error:', e);\n }\n}"
))
entries.append(g(
"Missing error cause chaining",
"try {\n await connect();\n} catch (e) {\n throw new Error('Failed to connect');\n // Loses original error details\n}",
"try {\n await connect();\n} catch (e) {\n throw new Error('Failed to connect', {\n cause: e instanceof Error ? e : undefined\n });\n // Error.cause preserves the original error"
))
entries.append(g(
"Error handling in async iterator",
"async function* fetchAll() {\n for (let i = 0; ; i++) {\n yield await fetchPage(i);\n // One failure stops all iteration\n }\n}",
"async function* fetchAll() {\n for (let i = 0; ; i++) {\n try {\n const page = await fetchPage(i);\n if (!page.items.length) break;\n yield* page.items;\n } catch (e) {\n if (e instanceof NotFoundError) break;\n throw e;\n }\n }\n}"
))
# =============================================================================
# FRAMEWORK INTEGRATION (12 entries)
# =============================================================================
entries.append(g(
"Missing React component typing",
"function UserCard(props) {\n return <div>{props.name}</div>;\n // No type checking on props\n}",
"interface UserCardProps {\n name: string;\n email?: string;\n avatar?: string;\n}\n\nfunction UserCard({ name, email, avatar }: UserCardProps) {\n return (\n <div>\n <h2>{name}</h2>\n {email && <p>{email}</p>}\n {avatar && <img src={avatar} alt={name} />}\n </div>\n );\n}"
))
entries.append(g(
"Not using React.FC",
"const UserCard: React.FC<UserCardProps> = ({ name }) => {\n return <div>{name}</div>;\n};\n// FC has implicit children: ReactNode",
"interface UserCardProps {\n name: string;\n children?: React.ReactNode;\n}\n\nfunction UserCard({ name, children }: UserCardProps) {\n return <div>{name}{children}</div>;\n}\n// React.FC is discouraged - explicit props type is clearer"
))
entries.append(g(
"Untyped event handler",
"function handleClick(event) {\n const value = event.target.value;\n // event: any\n}",
"function handleClick(event: React.ChangeEvent<HTMLInputElement>) {\n const value = event.target.value;\n // Properly typed - value is string\n}"
))
entries.append(g(
"Not typing useState",
"const [user, setUser] = useState(null);\n// user: null, no type safety",
"const [user, setUser] = useState<User | null>(null);\n// Explicit type - user is User | null"
))
entries.append(g(
"Not typing useEffect dependencies",
"useEffect(() => {\n fetchUser(userId).then(setUser);\n});\n// Missing dependency array",
"useEffect(() => {\n let ignore = false;\n fetchUser(userId).then(u => {\n if (!ignore) setUser(u);\n });\n return () => { ignore = true; };\n}, [userId]);\n// Dependency array + cleanup for stale closures"
))
entries.append(g(
"Untyped custom hook",
"function useDebounce(value, delay) {\n const [debounced, setDebounced] = useState(value);\n // ...\n}\n// No type inference for consumers",
"function useDebounce<T>(value: T, delay: number): T {\n const [debounced, setDebounced] = useState<T>(value);\n useEffect(() => {\n const timer = setTimeout(() => setDebounced(value), delay);\n return () => clearTimeout(timer);\n }, [value, delay]);\n return debounced;\n}\n// Generic hook - preserves value type"
))
entries.append(g(
"Not typing Express handlers",
"app.get('/users/:id', (req, res) => {\n const id = req.params.id;\n // id is string, no validation\n});",
"import { Request, Response } from 'express';\n\napp.get('/users/:id', (req: Request<{ id: string }>, res: Response) => {\n const id = req.params.id; // Typed\n const numId = parseInt(id, 10);\n if (isNaN(numId)) {\n return res.status(400).json({ error: 'Invalid ID' });\n }\n});"
))
entries.append(g(
"Missing Zod schema validation",
"app.post('/users', (req, res) => {\n const { name, email } = req.body;\n // req.body is any\n createUser({ name, email });\n});",
"import { z } from 'zod';\nconst userSchema = z.object({\n name: z.string().min(1).max(100),\n email: z.string().email()\n});\n\napp.post('/users', (req, res) => {\n const result = userSchema.safeParse(req.body);\n if (!result.success) {\n return res.status(400).json({ errors: result.error.errors });\n }\n createUser(result.data);\n});"
))
entries.append(g(
"Not inferring types from Zod",
"const userSchema = z.object({\n name: z.string(),\n email: z.string().email()\n});\n\ninterface User {\n name: string;\n email: string;\n}\n// Duplicated schema and interface",
"const userSchema = z.object({\n name: z.string(),\n email: z.string().email()\n});\n\ntype User = z.infer<typeof userSchema>;\n// Single source of truth - type derived from schema"
))
entries.append(g(
"Untyped Next.js API route",
"export default async function handler(req, res) {\n // req: any\n res.json({ ok: true });\n}",
"import { NextApiRequest, NextApiResponse } from 'next';\n\nexport default async function handler(\n req: NextApiRequest,\n res: NextApiResponse\n) {\n res.status(200).json({ ok: true });\n}"
))
entries.append(g(
"Not typing context for GraphQL",
"const resolvers = {\n Query: {\n user: (_, { id }) => db.user(id),\n }\n};\n// No type checking on resolvers",
"// With graphql-codegen:\n// 1. Generate types from schema\n// 2. Use typed resolvers\n\nimport { Resolvers } from './generated/types';\n\nconst resolvers: Resolvers = {\n Query: {\n user: (_, { id }, context) => context.db.user(id),\n }\n};"
))
entries.append(g(
"Missing vitest/typed testing",
"import { describe, it, expect } from 'vitest';\ndescribe('add', () => {\n it('adds numbers', () => {\n expect(add(1, 2)).toBe(3);\n });\n});\n// Fine, but no type testing",
"// Add type-level tests:\nimport { expectTypeOf } from 'vitest';\ndescribe('add', () => {\n it('returns number', () => {\n expectTypeOf(add(1, 2)).toBeNumber();\n });\n it('adds numbers', () => {\n expect(add(1, 2)).toBe(3);\n });\n});"
))
# =============================================================================
# PRACTICAL PATTERNS (8 entries)
# =============================================================================
entries.append(g(
"Not using enum for constants",
"const STATUS_ACTIVE = 'active';\nconst STATUS_INACTIVE = 'inactive';\nconst STATUS_PENDING = 'pending';\n// String literals - no type safety",
"enum Status {\n Active = 'active',\n Inactive = 'inactive',\n Pending = 'pending'\n}\n// Or const enum for compile-time only:\n// const enum Status { Active = 'active', ... }\n// Or string union type:\n// type Status = 'active' | 'inactive' | 'pending'"
))
entries.append(g(
"Missing index signature guard",
"const config: Record<string, string> = {};\nconst port = config.port; // string, not number",
"const config: Record<string, string> = {};\nconst port = config['port']; // string\nconst parsedPort = parseInt(port, 10);\n// Record<string, string> means all values are strings\n// Use specific type for known keys"
))
entries.append(g(
"Not using satisfies operator",
"const config = {\n host: 'localhost',\n port: 3000\n};\nconst typedConfig: Config = config;\n// Type assertion - doesn't check config",
"const config = {\n host: 'localhost',\n port: 3000\n} satisfies Config;\n// satisfies checks that config matches Config\n// while preserving the specific type of config"
))
entries.append(g(
"Not using branded types",
"type UserId = number;\ntype ProductId = number;\n// No distinction between them",
"type UserId = number & { __brand: 'UserId' };\ntype ProductId = number & { __brand: 'ProductId' };\n\n// Type-safe constructors:\nconst asUserId = (n: number): UserId => n as UserId;\nconst asProductId = (n: number): ProductId => n as ProductId;\n\n// Can't mix them:\n// findUser(asProductId(1)); // Error!"
))
entries.append(g(
"Missing deep partial",
"interface NestedConfig {\n db: { host: string; port: number };\n cache: { enabled: boolean; ttl: number };\n}\n// Partial only makes top-level optional",
"type DeepPartial<T> = {\n [P in keyof T]?: T[P] extends object\n ? DeepPartial<T[P]>\n : T[P];\n};\n\ninterface NestedConfig {\n db: { host: string; port: number };\n cache: { enabled: boolean; ttl: number };\n}\ntype PartialConfig = DeepPartial<NestedConfig>;"
))
entries.append(g(
"Not using function overloads",
"function parse(input: string | number): string | number {\n return typeof input === 'string' ? parseInt(input) : String(input);\n}\n// Return type is union - caller must check",
"function parse(input: string): number;\nfunction parse(input: number): string;\nfunction parse(input: string | number): string | number {\n return typeof input === 'string' ? parseInt(input) : String(input);\n}\n// Overloads give precise return types per input type"
))
entries.append(g(
"Not using type-only imports",
"import { User, createUser } from './users';\n// If User is only used as a type, it still imports at runtime",
"import type { User } from './users';\nimport { createUser } from './users';\n// Or combined:\nimport { createUser, type User } from './users';\n// Type-only imports are erased at compile time"
))
entries.append(g(
"Not using template literal types for validation",
"type Email = string;\n// No validation at type level",
"type Email = `${string}@${string}.${string}`;\n// Template literal type constrains the shape\n// Note: this is compile-time only, not runtime validation\n// For runtime: use Zod, io-ts, or zod-to-json-schema"
))
# =============================================================================
# SAVE
# =============================================================================
random.seed(42)
random.shuffle(entries)
os.makedirs(BASE, exist_ok=True)
out = BASE / "dev_finetuning_typescript.json"
with open(out, 'w') as f:
json.dump(entries, f, indent=2)
# Count by tag category
categories = {}
for e in entries:
for tag in e['tags']:
if tag != 'typescript':
categories[tag] = categories.get(tag, 0) + 1
print(f"Total entries: {len(entries)}")
print(f"Categories:")
for cat, count in sorted(categories.items(), key=lambda x: -x[1]):
print(f" {cat}: {count}")
print(f"Saved: {out}")