typescript-expert
SKILL.md
TypeScript Expert
Advanced type-level programming for robust, self-documenting code.
Generics
// Constrained generic
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
// Generic with default
type ApiResponse<T = unknown> = { data: T; status: number; error?: string };
// Generic class
class Repository<T extends { id: string }> {
private items = new Map<string, T>();
save(item: T) { this.items.set(item.id, item); }
find(id: string): T | undefined { return this.items.get(id); }
}
Conditional Types
// Extract return type based on input
type ApiResult<T> = T extends 'user' ? User : T extends 'post' ? Post : never;
// Infer nested types
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
type Result = UnwrapPromise<Promise<string>>; // string
// Distributive conditional
type NonNullable<T> = T extends null | undefined ? never : T;
Mapped Types
// Make all properties optional
type Partial<T> = { [K in keyof T]?: T[K] };
// Make all properties readonly
type Readonly<T> = { readonly [K in keyof T]: T[K] };
// Remap keys
type Getters<T> = { [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K] };
// { getName: () => string; getAge: () => number }
Type Guards
// Custom type guard
function isUser(value: unknown): value is User {
return typeof value === 'object' && value !== null && 'email' in value;
}
// Discriminated union
type Result<T> = { ok: true; data: T } | { ok: false; error: string };
function handle<T>(result: Result<T>) {
if (result.ok) {
console.log(result.data); // TypeScript knows data exists
} else {
console.log(result.error); // TypeScript knows error exists
}
}
// Assertion function
function assertDefined<T>(value: T | undefined, msg: string): asserts value is T {
if (value === undefined) throw new Error(msg);
}
Utility Types
// Pick specific properties
type UserPreview = Pick<User, 'id' | 'name'>;
// Omit properties
type CreateUserInput = Omit<User, 'id' | 'createdAt'>;
// Required
type StrictConfig = Required<Config>;
// Record
type UserMap = Record<string, User>;
// Extract from union
type StringOrNumber = Extract<string | number | boolean, string | number>;
// Parameters and ReturnType
type FnParams = Parameters<typeof myFunction>;
type FnReturn = ReturnType<typeof myFunction>;
Template Literal Types
type EventName = `on${Capitalize<'click' | 'hover' | 'focus'>}`;
// "onClick" | "onHover" | "onFocus"
type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
type Endpoint = `/${string}`;
type Route = `${HTTPMethod} ${Endpoint}`;
Strict Patterns
// Exhaustive switch
function assertNever(x: never): never {
throw new Error(`Unexpected value: ${x}`);
}
type Status = 'active' | 'inactive' | 'pending';
function handleStatus(status: Status) {
switch (status) {
case 'active': return 'green';
case 'inactive': return 'red';
case 'pending': return 'yellow';
default: return assertNever(status); // Compile error if case missed
}
}
// Branded types (prevent mixing IDs)
type UserId = string & { __brand: 'UserId' };
type PostId = string & { __brand: 'PostId' };
function getUser(id: UserId) { /* ... */ }
// getUser(postId) → compile error
tsconfig Strict Settings
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"noImplicitOverride": true
}
}
Notes
unknownoverany— forces type checking before use.- Prefer discriminated unions over optional properties for state modeling.
as constmakes literal types:const x = [1, 2] as const→readonly [1, 2].- Avoid type assertions (
as). If you need one, you probably need a type guard instead. - Use
satisfiesto validate without widening:const config = {...} satisfies Config.
Weekly Installs
2
Repository
thinkfleetai/th…t-engineFirst Seen
12 days ago
Security Audits
Installed on
opencode2
claude-code2
github-copilot2
codex2
kimi-cli2
gemini-cli2