Type systems define the rules that govern how values of different types can be used in a programming language, ranging from dynamic runtime checks to static compile-time guarantees. They encompass static vs dynamic typing, structural vs nominal typing, generics and parametric polymorphism, and advanced features like covariance/contravariance, algebraic data types, and type inference. Understanding type systems helps developers write safer, more maintainable code by catching errors early, enabling powerful abstraction patterns, and choosing the right type system trade-offs for their specific problem domain—whether that's the flexibility of gradual typing, the safety of dependent types, or the performance of compile-time type erasure.
What This Cheat Sheet Covers
This topic spans 13 focused tables and 74 indexed concepts, 70 flashcards. Below is a complete table-by-table outline of this topic, spanning foundational concepts through advanced details.
A jump-to index of every table row in this cheat sheet.
An interactive map of every table and concept in this topic.
Table 1: Static vs Dynamic Typing Fundamentals
The vocabulary in this table is the bedrock of every type-system debate—and the words are easy to confuse. Static versus dynamic is about when types are checked (compile time or runtime), while strong versus weak is about how strictly the rules are enforced; the two axes are independent, which is why Python is dynamic-but-strong and C is static-but-weak. Gradual typing and soundness round out the picture.
| Classification | Example | Description | |
|---|---|---|---|
int x = 42;String s = "hello"; | • Types are checked at compile time before execution • the compiler catches type-related errors early, leading to more reliable code | ||
x = 42x = "hello" | • Types are checked at runtime during execution • variables can hold values of different types at different times, offering flexibility but detecting errors later | ||
let age: number = 25;def greet(name: str) -> str: | • Explicit type declarations added to code • required in some statically-typed languages, optional in gradually-typed languages like TypeScript and Python (mypy). | ||
let x = 42;val y = "text" | • Compiler automatically deduces types from context without explicit annotations • common in ML, Haskell, Scala, Rust, and TypeScript | ||
function f(x: any): numberdef process(data): ... | • Combines static and dynamic typing, allowing mixed type checking • enables incremental adoption of static types in dynamic languages (TypeScript, Python with mypy). | ||
Python: "5" + 3 → errorJava: int + String → error | • Strict enforcement of type rules with minimal implicit conversions • type mismatches are caught and reported as errors | ||
JS: "5" + 3 → "53"C: int* p = 0; *p; | • Permissive type system allowing implicit conversions between types • can lead to unexpected behavior but offers flexibility | ||
Rust: no null pointer dereferences Haskell: no runtime type errors | • A sound type system guarantees that well-typed programs cannot cause certain runtime errors • "well-typed programs don't go wrong." |