Strict Mode Is Non-Negotiable
Always enable strict: true in your tsconfig. It catches entire categories of bugs at compile time. The small upfront cost pays off enormously in reduced runtime errors and better IDE support.
Prefer Interfaces Over Types (Usually)
Interfaces are extendable and produce better error messages. Use type aliases for unions, intersections, and mapped types where interfaces can't help.
Discriminated Unions
type Result<T> =
| { success: true; data: T }
| { success: false; error: string };
function handle(result: Result<User>) {
if (result.success) {
// TypeScript knows result.data exists here
console.log(result.data.name);
}
}Zod for Runtime Validation
TypeScript only checks at compile time. Use Zod to validate data at runtime boundaries — API responses, form inputs, environment variables. The combination of TypeScript + Zod gives you end-to-end type safety.
Utility Types You Should Know
Partial<T>— make all properties optionalPick<T, K>— select specific propertiesOmit<T, K>— exclude specific propertiesRecord<K, V>— construct an object type with specific keys and values
