TypeScript Expert Skill
Master TypeScript's type system for safer, more maintainable code
🎯 PRINCIPLES
1. Type Safety First
- •Prefer strict mode (
strict: true) - •Avoid
any- useunknownfor truly unknown types - •Enable all strict checks in tsconfig.json
2. Interface vs Type
typescript
// ✅ Use interface for objects that will be extended
interface User {
id: string;
name: string;
}
interface AdminUser extends User {
permissions: string[];
}
// ✅ Use type for unions, intersections, primitives
type Status = 'pending' | 'active' | 'inactive';
type ID = string | number;
type Callback<T> = (value: T) => void;
3. Generics Best Practices
- •Name meaningfully:
TItem,TKey,TValue, not justT - •Add constraints when possible:
<T extends object> - •Default generics for common cases:
<T = unknown>
📘 UTILITY TYPES
Built-in Utility Types
typescript
// Pick - Select specific properties type UserPreview = Pick<User, 'id' | 'name'>; // Omit - Exclude properties type CreateUserInput = Omit<User, 'id' | 'createdAt'>; // Partial - Make all properties optional type UpdateUserInput = Partial<User>; // Required - Make all properties required type CompleteUser = Required<User>; // Readonly - Make all properties readonly type ImmutableUser = Readonly<User>; // Record - Create object type with specific keys type UserRoles = Record<string, string[]>; // ReturnType - Extract function return type type GetUserResult = ReturnType<typeof getUser>; // Parameters - Extract function parameters type GetUserParams = Parameters<typeof getUser>; // Awaited - Unwrap Promise type type ResolvedUser = Awaited<Promise<User>>;
Custom Utility Types
typescript
// DeepPartial - Recursive partial
type DeepPartial<T> = T extends object ? {
[P in keyof T]?: DeepPartial<T[P]>;
} : T;
// NonNullable properties
type NonNullableFields<T> = {
[P in keyof T]: NonNullable<T[P]>;
};
// Extract function properties
type FunctionProperties<T> = {
[K in keyof T as T[K] extends Function ? K : never]: T[K];
};
🔄 DISCRIMINATED UNIONS
typescript
// ✅ Result Pattern
type Result<T, E = Error> =
| { success: true; data: T }
| { success: false; error: E };
function processResult<T>(result: Result<T>) {
if (result.success) {
// TypeScript knows: result.data exists
return result.data;
} else {
// TypeScript knows: result.error exists
throw result.error;
}
}
// ✅ State Machine Pattern
type RequestState<T> =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success'; data: T }
| { status: 'error'; error: Error };
function renderState<T>(state: RequestState<T>) {
switch (state.status) {
case 'idle':
return 'Ready to load';
case 'loading':
return 'Loading...';
case 'success':
return `Data: ${state.data}`;
case 'error':
return `Error: ${state.error.message}`;
}
}
🛡️ TYPE GUARDS
typescript
// ✅ Type Predicate Functions
function isString(value: unknown): value is string {
return typeof value === 'string';
}
function isUser(value: unknown): value is User {
return (
typeof value === 'object' &&
value !== null &&
'id' in value &&
'name' in value
);
}
// ✅ Assertion Functions
function assertIsUser(value: unknown): asserts value is User {
if (!isUser(value)) {
throw new Error('Value is not a User');
}
}
// ✅ Using with narrowing
function processValue(value: string | number | User) {
if (isString(value)) {
// value is string here
return value.toUpperCase();
}
if (typeof value === 'number') {
// value is number here
return value.toFixed(2);
}
// value is User here
return value.name;
}
🗺️ MAPPED TYPES
typescript
// Basic Mapped Type
type Nullable<T> = {
[P in keyof T]: T[P] | null;
};
// With Key Remapping (as clause)
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
type Setters<T> = {
[K in keyof T as `set${Capitalize<string & K>}`]: (value: T[K]) => void;
};
// Filter by Value Type
type StringProperties<T> = {
[K in keyof T as T[K] extends string ? K : never]: T[K];
};
// Example Usage
interface User {
id: number;
name: string;
email: string;
}
type UserGetters = Getters<User>;
// {
// getId: () => number;
// getName: () => string;
// getEmail: () => string;
// }
❓ CONDITIONAL TYPES
typescript
// Basic Conditional type IsString<T> = T extends string ? true : false; // Extract element type from array type ElementType<T> = T extends (infer E)[] ? E : T; // Unwrap Promise type UnwrapPromise<T> = T extends Promise<infer U> ? U : T; // Function return type extraction type ExtractReturn<T> = T extends (...args: any[]) => infer R ? R : never; // Distributive Conditional Types type ToArray<T> = T extends unknown ? T[] : never; type Result = ToArray<string | number>; // string[] | number[] // Prevent Distribution with tuple type ToArrayNonDist<T> = [T] extends [unknown] ? T[] : never; type Result2 = ToArrayNonDist<string | number>; // (string | number)[]
📝 TEMPLATE LITERAL TYPES
typescript
// Event Names
type EventName = `on${Capitalize<'click' | 'focus' | 'blur'>}`;
// 'onClick' | 'onFocus' | 'onBlur'
// CSS Properties
type CSSValue = `${number}${'px' | 'em' | 'rem' | '%'}`;
const width: CSSValue = '100px'; // ✅
const height: CSSValue = '50%'; // ✅
// Route Parameters
type Route = `/${string}` | `/${string}/${string}`;
// Object Path Types
type PathKeys<T, Prefix extends string = ''> = T extends object
? {
[K in keyof T & string]: T[K] extends object
? `${Prefix}${K}` | PathKeys<T[K], `${Prefix}${K}.`>
: `${Prefix}${K}`;
}[keyof T & string]
: never;
⚠️ ANTI-PATTERNS
❌ Avoid any
typescript
// ❌ BAD: any defeats type safety
function process(data: any) {
return data.value; // No type checking
}
// ✅ GOOD: Use unknown + narrowing
function processSafe(data: unknown) {
if (isValidData(data)) {
return data.value; // Type-safe
}
throw new Error('Invalid data');
}
❌ Avoid Non-null Assertion
typescript
// ❌ BAD: Can throw at runtime
const user = getUser()!;
// ✅ GOOD: Handle null explicitly
const user = getUser();
if (!user) {
throw new Error('User not found');
}
// user is non-null here
❌ Avoid @ts-ignore
typescript
// ❌ BAD: Hides real type errors // @ts-ignore const value = problematicFunction(); // ✅ GOOD: Fix the type or use @ts-expect-error with comment // @ts-expect-error: Legacy function returns wrong type, will fix in v2 const value = problematicFunction();
❌ Avoid Type Assertions When Narrowing Works
typescript
// ❌ BAD: Forces type
const element = document.getElementById('app') as HTMLDivElement;
// ✅ GOOD: Narrow with guards
const element = document.getElementById('app');
if (element instanceof HTMLDivElement) {
// element is HTMLDivElement here
}
🎨 GENERICS PATTERNS
Generic Constraints
typescript
// Constrain to objects with specific shape
function getProperty<T extends object, K extends keyof T>(
obj: T,
key: K
): T[K] {
return obj[key];
}
// Constrain to constructables
function createInstance<T extends new (...args: any[]) => any>(
Constructor: T,
...args: ConstructorParameters<T>
): InstanceType<T> {
return new Constructor(...args);
}
Generic Defaults
typescript
interface Repository<T, ID = string> {
find(id: ID): Promise<T | null>;
save(entity: T): Promise<T>;
}
// Uses default ID type
class UserRepository implements Repository<User> {
// ID is string
}
// Override default
class ProductRepository implements Repository<Product, number> {
// ID is number
}
Variadic Tuple Types
typescript
function concat<T extends unknown[], U extends unknown[]>(
arr1: [...T],
arr2: [...U]
): [...T, ...U] {
return [...arr1, ...arr2];
}
const result = concat([1, 2], ['a', 'b']);
// Type: [number, number, string, string]
📎 QUICK REFERENCE
| Pattern | When to Use |
|---|---|
interface | Object shapes, extensible contracts |
type | Unions, intersections, computed types |
Pick<T, K> | Need subset of properties |
Omit<T, K> | Need all except some properties |
Partial<T> | Update/patch operations |
Required<T> | Ensure all fields present |
ReturnType<T> | Extract function return |
Parameters<T> | Extract function params |
| Type Guard | Runtime type narrowing |
| Discriminated Union | State machines, result types |
| Mapped Type | Transform all properties |
| Conditional Type | Type-level conditionals |
🔗 RELATED SKILLS
- •type-safety - Runtime validation & strict mode
- •react-patterns - React + TypeScript patterns