Type Systems: Static vs Dynamic & Strong vs Weak 🏷️
Every piece of data processed by a computer has a type—an integer, a floating-point number, a string, a boolean, or a composite structure. A Type System is the set of rules a language uses to verify that you aren’t doing nonsensical things (like trying to multiply a word by a date).
Understanding type systems helps you choose the right tool for a project and catch bugs before they reach production.
1. The Two Big Questions
Type systems are defined by two independent questions:
- When are types checked? Static vs Dynamic
- How strictly are types enforced? Strong vs Weak
flowchart TD subgraph Static["Static Typing (Checked at Compile Time)"] SS["Static + Strong\nGo, Rust, Java, TypeScript, C#\n(Safe, catches bugs in CI/build)"] SW["Static + Weak\nC, C++\n(Fast, allows dangerous memory casts)"] end subgraph Dynamic["Dynamic Typing (Checked at Runtime)"] DS["Dynamic + Strong\nPython, Ruby\n(Flexible, TypeError when types clash)"] DW["Dynamic + Weak\nJavaScript, PHP\n(Coerces types silently: '5' - 2 = 3)"] end
2. Static vs Dynamic: When Are Types Checked?
Static Typing (Go, TypeScript, Rust, Java)
Types are checked before the code runs, during compilation or IDE analysis:
var age int = 25
age = "twenty-five" // ❌ Compile Error: cannot use string as int- Advantages: Catches typos and bugs before code ever deploys. Autocomplete and refactoring in IDEs work reliably.
- Trade-off: Writing type declarations can feel more verbose (though modern languages use type inference like
age := 25to deduce the type automatically).
Dynamic Typing (Python, JavaScript, Ruby)
Types belong to the value, not the variable name. Variables can hold any type and are checked as lines execute:
age = 25 # age is an integer
age = "twenty-five" # Totally fine! Now age holds a string- Advantages: Fast prototyping, concise scripts, fewer lines of code.
- Trade-off: A typo or unexpected
Nonevalue can hide in a rarely calledifbranch until a customer triggers it in production.
3. Strong vs Weak: How Strict Are Conversions?
Strong Typing (Python, Go, Rust, Java)
The language refuses to silently convert incompatible types. You must explicitly cast them:
# In Python (Strong):
result = "Order count: " + 5
# ❌ TypeError: can only concatenate str (not "int") to str
# You must write: "Order count: " + str(5)Weak Typing (JavaScript, PHP)
The language guesses what you meant and silently converts types behind your back:
// In JavaScript (Weak):
"5" - 2 // Result: 3 (Converts "5" to a number!)
"5" + 2 // Result: "52" (Converts 2 to a string!)
[] + {} // Result: "[object Object]"While this can feel forgiving in quick scripts, it causes bizarre production bugs that are difficult to track down.
4. The null / nil / None Problem
In 1965, British computer scientist Tony Hoare invented the null pointer, which he later called his “billion-dollar mistake”.
In many languages, any variable can silently hold null, nil, or None:
- Python:
AttributeError: 'NoneType' object has no attribute 'name' - Java:
NullPointerException - Go:
panic: runtime error: nil pointer dereference
Modern Solutions to Null Safety
- Explicit Guard Checks:
if user == nil { return errors.New("user not found") } - Nullable Types in TypeScript / Kotlin:
function getEmail(user?: User): string | null { return user?.email ?? "no-email@example.com"; // Optional chaining & null coalescing } - Option / Result Enums in Rust:
Rust does not have
nullat all. A value that might be missing must be wrapped inOption<T>(Some(value)orNone), forcing the developer to handle both cases before the code will compile!
Across the Wiki
- Generics Basics — Writing reusable code while preserving type safety.
- Go Type Assertions — Inspecting dynamic types safely in Go.
- Value vs Reference — How types are represented in memory.