원클릭으로
avoid-anecdotal-types
Use when creating types from example data. Use when types don't match all cases. Use when API responses vary.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Use when creating types from example data. Use when types don't match all cases. Use when API responses vary.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Use when defining global types. Use when augmenting window. Use when typing environment variables. Use when working with build-time constants. Use when configuring type definitions.
Use when migrating JavaScript to TypeScript. Use when gradually adopting TypeScript. Use when working with mixed codebases. Use when converting large projects. Use when teams are learning TypeScript.
Use when writing asynchronous code. Use when tempted to use callbacks. Use when composing multiple async operations.
Use when writing type annotations on variables. Use when TypeScript can infer the type. Use when code feels cluttered with types.
Use when defining array-like types. Use when tempted to use number as index type. Use when understanding array keys.
Use when functions have multiple parameters of same type. Use when parameter order is easy to confuse. Use when designing function signatures.
| name | avoid-anecdotal-types |
| description | Use when creating types from example data. Use when types don't match all cases. Use when API responses vary. |
Don't infer types from a single example.
If you create a type based on one API response or one sample, you'll miss edge cases. Get authoritative type definitions or examine multiple examples to understand the full shape of your data.
Types should be based on specifications, not samples.
One example doesn't show all possibilities.
Remember:
// Created from looking at one API response
interface GeocodingResult {
lat: number;
lng: number;
address: string;
city: string;
country: string;
}
// But the real API might return:
// - Missing city for some locations
// - Multiple results (array)
// - Error responses
// - Different formats for different countries
// From API documentation:
interface GeocodingResult {
lat: number;
lng: number;
formatted_address: string;
address_components: AddressComponent[]; // Not flat city/country!
partial_match?: boolean; // Optional!
}
// Many APIs provide TypeScript types
import { GeocodingResult } from '@google/maps-types';
// Or JSON Schema
import Ajv from 'ajv';
const validate = ajv.compile(geocodingSchema);
// Auto-generated from API specification
import { GeocodingResponse } from './generated/api-types';
// From looking at one user:
interface GitHubUser {
id: number;
login: string;
name: string;
email: string;
company: string;
bio: string;
}
// Reality from GitHub's API docs:
interface GitHubUser {
id: number;
login: string;
name: string | null; // Can be null!
email: string | null; // Can be null!
company: string | null; // Can be null!
bio: string | null; // Can be null!
// ... many more fields, some conditional
}
One example user had all fields filled in. Many don't.
// Dangerous: trusting parsed JSON
const response = await fetch('/api/user');
const user: User = await response.json(); // No runtime validation!
// The server might return:
// - Missing fields
// - Extra fields
// - Different types than expected
// - Error responses
Use runtime validation to ensure data matches your types:
import { z } from 'zod';
const UserSchema = z.object({
id: z.number(),
name: z.string().nullable(),
email: z.string().email().nullable(),
});
type User = z.infer<typeof UserSchema>;
async function fetchUser(id: string): Promise<User> {
const response = await fetch(`/api/user/${id}`);
const data = await response.json();
return UserSchema.parse(data); // Throws if invalid
}
Now you'll catch mismatches at runtime, not in production bugs.
If you can't get a spec, examine multiple examples:
// Look at edge cases:
// - New user (minimal data)
// - Old user (all fields filled)
// - Deleted user (special state)
// - Admin user (extra fields)
// - Different locales
// Build type that handles ALL cases:
interface User {
id: number;
login: string;
name: string | null;
role?: 'user' | 'admin';
deletedAt?: Date;
}
// Your test data:
const user = { name: "Alice", age: 30 };
// But age is optional for some users!
interface User {
name: string;
age?: number; // Should be optional
}
// Your experience:
const response = { data: [...], total: 100 };
// But empty results might differ:
const emptyResponse = { data: [], total: 0 }; // OK
const notFound = { error: "Not found" }; // Oops, no data field!
// Your examples:
{ id: 123 }
{ id: 456 }
// But some systems return:
{ id: "abc-123" } // String in some cases!
Pressure: "I tested it and it works fine"
Response: It works with your test data. What about edge cases?
Action: Get the spec or test with varied data.
Pressure: "There's no API spec available"
Response: Examine multiple responses. Check error cases. Ask the API owners.
Action: Build defensive types with optional fields and validation.
| Excuse | Reality |
|---|---|
| "It works in testing" | Testing shows one path, not all |
| "We control the API" | APIs change, have edge cases |
| "We'll fix it if it breaks" | Users hit edge cases first |
// DON'T: Type from one example
interface User {
id: number;
name: string; // What if null?
email: string; // What if missing?
}
// DO: Type from specification
interface User {
id: number;
name: string | null;
email?: string;
}
// DO: Runtime validation
const UserSchema = z.object({
id: z.number(),
name: z.string().nullable(),
email: z.string().optional(),
});
// DO: Get official types
import { User } from '@api/types';
Types based on samples will miss edge cases.
One API response or one database record doesn't show all possibilities. Use official documentation, generated types, or runtime validation. When creating types from examples, examine multiple cases including edge cases and errors.
Based on "Effective TypeScript" by Dan Vanderkam, Item 42: Avoid Types Based on Anecdotal Data.