TypeScript Skill
Quick Start
// Basic type annotations const name: string = "John"; const age: number = 25; const isActive: boolean = true;
// Array types const numbers: number[] = [1, 2, 3]; const users: Array<User> = [user1, user2];
// Object types interface User { id: number; name: string; email?: string; // optional }
// Function types
const greet = (name: string): string => Hello, ${name}!;
const add = (a: number, b: number): number => a + b;
Common Patterns
Interface Definitions
// Basic interface interface Product { id: string; name: string; price: number; }
// Interface with optional properties interface UserProfile { id: number; username: string; avatar?: string; bio?: string; }
// Interface extending another interface AdminUser extends UserProfile { permissions: string[]; role: "admin" | "super_admin"; }
// Generic interface interface ApiResponse<T> { data: T; status: number; message: string; }
// Usage const userResponse: ApiResponse<User> = { data: { id: 1, username: "john", email: "john@example.com" }, status: 200, message: "Success", };
Generic Types and Functions
// Generic function function identity<T>(arg: T): T { return arg; }
// Generic function with constraints interface Lengthwise { length: number; }
function getLength<T extends Lengthwise>(arg: T): number { return arg.length; }
// Generic class class Box<T> { private contents: T;
constructor(value: T) { this.contents = value; }
getValue(): T { return this.contents; } }
// Generic utility function function createApiResponse<T>(data: T, status = 200): ApiResponse<T> { return { data, status, message: status >= 400 ? "Error" : "Success", }; }
Utility Types
// Pick - select specific properties type UserContactInfo = Pick<User, "email" | "phone">;
// Omit - remove specific properties type CreateUserRequest = Omit<User, "id" | "createdAt">;
// Partial - make all properties optional type PartialUser = Partial<User>;
// Required - make all properties required type RequiredUser = Required<UserProfile>;
// Record - create object type with specific keys type StatusMap = Record<"pending" | "approved" | "rejected", string>;
// Extract properties that match a condition type StringProperties<T> = { [K in keyof T]: T[K] extends string ? K : never; }[keyof T];
// Function property extraction type FunctionPropertyNames<T> = { [K in keyof T]: T[K] extends Function ? K : never; }[keyof T];
type FunctionProperties<T> = Pick<T, FunctionPropertyNames<T>>;
Type Guards and Discriminated Unions
// Discriminated union interface LoadingState { status: "loading"; }
interface SuccessState<T> { status: "success"; data: T; }
interface ErrorState { status: "error"; error: string; }
type DataState<T> = LoadingState | SuccessState<T> | ErrorState;
// Type guard function function isLoading<T>(state: DataState<T>): state is LoadingState { return state.status === "loading"; }
function isSuccess<T>(state: DataState<T>): state is SuccessState<T> { return state.status === "success"; }
// Usage function handleDataState<T>(state: DataState<T>) { if (isLoading(state)) { console.log("Loading..."); } else if (isSuccess(state)) { console.log("Data:", state.data); } else { console.log("Error:", state.error); } }
Advanced Type Patterns
// Conditional types type NonNullable<T> = T extends null | undefined ? never : T;
// Mapped types type OptionalFields<T> = { [K in keyof T]?: T[K]; };
// Readonly mapped type type ReadonlyUser = { readonly [K in keyof User]: User[K]; };
// Template literal types
type EventName = on${Capitalize<string>};
type EventHandler = Record<EventName, Function>;
// Recursive utility types type DeepPartial<T> = { [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P]; };
API Response Patterns
// Standard API response interface PaginatedResponse<T> { data: T[]; pagination: { page: number; limit: number; total: number; totalPages: number; }; }
// Error response interface ApiError { code: string; message: string; details?: Record<string, any>; }
// Union of success and error responses type ApiResult<T> = | { success: true; data: T } | { success: false; error: ApiError };
// Type-safe API client class ApiClient { async get<T>(url: string): Promise<ApiResult<T>> { try { const response = await fetch(url); const data = await response.json(); return { success: true, data }; } catch (error) { return { success: false, error: { code: "NETWORK_ERROR", message: error.message }, }; } } }
React/Component Patterns
// Component props with generics interface ListProps<T> { items: T[]; renderItem: (item: T) => React.ReactNode; keyExtractor: (item: T) => string; }
function List<T>({ items, renderItem, keyExtractor }: ListProps<T>) { return ( <div> {items.map(item => ( <div key={keyExtractor(item)}> {renderItem(item)} </div> ))} </div> ); }
// Hook with generic state interface State<T> { data: T | null; loading: boolean; error: string | null; }
function useApi<T>(url: string): State<T> { const [state, setState] = useState<State<T>>({ data: null, loading: true, error: null });
useEffect(() => { fetch(url) .then(res => res.json()) .then(data => setState({ data, loading: false, error: null })) .catch(error => setState({ data: null, loading: false, error: error.message })); }, [url]);
return state; }
Requirements
-
TypeScript 4.5+ (for latest utility types and template literal types)
-
Understanding of basic JavaScript concepts
-
Familiarity with object-oriented programming concepts
-
Knowledge of async/await patterns for API work
Best Practices
-
Use interface for object shapes unless you need union types, then use type aliases
-
Prefer generic types over any for better type safety
-
Use utility types (Pick, Omit, Partial) to transform existing types
-
Create type guards for discriminated unions
-
Leverage conditional types for advanced type transformations
-
Use readonly for immutable data structures
-
Prefer const assertions for literal types and readonly arrays
Common Gotchas
-
any vs unknown : Use unknown when you need type checking before usage
-
[] as any[] : Avoid, use proper typing instead
-
Function overload ordering: Most specific signatures first
-
Generic constraints: Use extends to limit generic types
-
Type inference: Let TypeScript infer when possible, provide explicit types when needed