Static, dynamic, strong, and weak typing
When type errors are caught, what implicit coercions mean, and why type inference does not make a language dynamic.
- Contrast static typing with dynamic typing and strong typing with weak typing
- Explain what type inference does and why it does not make a language dynamically typed
Static typing vs dynamic typing
In a statically typed language every expression has a type that is known at compile time. The compiler rejects programs where types are used incorrectly — before a single line of machine code runs.
// TypeScript (static)
function add(a: number, b: number): number {
return a + b;
}
add("hello", 42); // compile error: Argument of type 'string' is not assignableIn a dynamically typed language types are attached to values at runtime, not to variables or expressions. The same variable can hold an integer on one execution path and a string on another. Type errors surface only when bad code actually executes.
# Python (dynamic)
def add(a, b):
return a + b
add("hello", 42) # raises TypeError at runtime, not beforeThe trade-off is real. Static typing front-loads the pain (you write annotations) and eliminates whole classes of runtime errors. Dynamic typing gives faster iteration on small programs and makes certain patterns (heterogeneous collections, duck-typed interfaces) more natural — at the cost of needing thorough tests to catch what the compiler would have caught for free.
Strong typing vs weak typing
This axis is orthogonal to static/dynamic. The working definition: a strongly typed language never coerces values to a different type implicitly; a weakly typed language will perform silent coercions to make an operation succeed.
// JavaScript (weakly typed)
console.log("5" - 3); // 2 — string coerced to number
console.log("5" + 3); // "53" — number coerced to string
console.log([] + {}); // "[object Object]" — both coerced to strings# Python (strongly typed)
"5" - 3 # TypeError — no implicit coercionWeak typing is not a flaw — C intentionally allows pointer arithmetic and unchecked casts because it operates close to hardware. But for application programming, implicit coercions are a reliable source of subtle bugs.
The terms "strong" and "weak" are often used loosely and inconsistently in online discussions. When someone says a language is "strongly typed," press them for what they mean specifically: do they mean no implicit coercions? No unchecked casts? Memory safety guarantees? The underlying properties matter more than the label.
Type inference
Type inference is a compiler's ability to deduce types from context so you don't have to write them everywhere. It does not make a language dynamically typed — the types are still resolved at compile time.
// TypeScript — type annotation omitted, but type is still inferred as number
const x = 42; // TypeScript knows x: number
const y = x + "?"; // still a compile error: number + string// Rust — full Hindley–Milner inference; almost never need annotations inside functions
let mut v = Vec::new();
v.push(1); // Rust infers Vec<i32> from this line
v.push("?"); // compile error: expected i32, found &strInference is purely a quality-of-life feature. The type discipline is the same; you just type less.
Knowledge check
- 1.A language where every variable's type is resolved before any code runs is best described as:
- 2.A language that uses type inference (like Rust or TypeScript) is therefore dynamically typed.
Primitive and composite types
The atoms of a type system — how primitives map to machine words, and how composites are built from them and laid out in memory.
Structural typing and generics
Duck typing vs explicit declarations, and writing reusable code that keeps full static type safety through generics.