Code of the Day

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.

Data Types & Type Systems6 min read
By the end of this lesson you will be able to:
  • 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 language every expression has a type that is known at compile time. The 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 assignable

In a 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 before

The 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 coercion

Weak 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

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 &str

Inference is purely a quality-of-life feature. The type discipline is the same; you just type less.

Knowledge check

  1. 1.
    A language where every variable's type is resolved before any code runs is best described as:
  2. 2.
    A language that uses type inference (like Rust or TypeScript) is therefore dynamically typed.
Finished reading? Mark it complete to track your progress.

On this page