Explain static vs dynamic typing and strong vs weak typing, with an example language for each combination. Why are these two separate questions?
Show the explanation
They answer two different questions:
- When are types checked? Static: at compile time, based on declared or inferred types. Dynamic: at run time, based on the values.
- How strictly are mismatched types handled? Strong: the language refuses or demands an explicit conversion. Weak: it silently converts (coerces) values. This is more of a spectrum than a yes/no.
| Strong | Weak | |
|---|---|---|
| Static | Java, C#, Rust, Haskell, Go | C (implicit conversions, pointer casts, void *) |
| Dynamic | Python, Ruby | JavaScript, PHP (many implicit coercions) |
Examples
- Python is dynamic and strong:
x = 5; x = "five"is fine, because names are not tied to types, but"5" + 5raisesTypeError. - JavaScript is dynamic and weakly typed:
"5" + 5gives"55","5" - 2gives3, and[] + {}gives"[object Object]". - Java is static:
int n = "five";does not compile.String s = "a" + 5;is allowed because string concatenation is a defined rule, not silent coercion. - C is static but weak in places:
int x = 3.9;quietly truncates to 3, and pointer casts let you reinterpret memory.
Trade-offs
- Static typing: catches many bugs before running, gives better IDE help and refactoring, and can produce faster code. The costs are more ceremony (reduced by type inference such as
varandauto) and less flexibility. - Dynamic typing: faster prototyping and less code. The cost is that type errors appear only when that line runs, so you need good tests.
- Gradual typing mixes the two: TypeScript over JavaScript, and Python type hints checked by mypy or pyright. The hints are not enforced at run time by default.
Related: type inference (Rust's let x = 5;, C++'s auto) is still static typing. The compiler just works the type out for you.
Original UniCircle question. Last reviewed 22 Oct 2026.
