awesome-everything RU
↑ Back to the climb

Base CS from zero

Why types exist

Crux Types catch ''''read these bits the wrong way'''' mistakes. Static typing catches them at compile time; dynamic typing catches them at run time. Both serve the same fundamental purpose: preventing misinterpretation.
◷ 20 min

So far you have seen that types are interpretation rules for bits, and that the same bits can mean different things under different rules. This raises a practical question: what happens when the wrong interpretation rule is applied? What goes wrong, and how badly?

The answer ranges from “you get garbage output” to “the program crashes” to “security vulnerabilities are created.” And the mechanism in all cases is the same: some code applied a type rule to a bit pattern that was stored with a different rule in mind.

Types exist precisely to prevent this. A type system is the part of a programming language that tracks which interpretation rule belongs to which value, and stops you — either at compile time or at run time — from applying the wrong one. Understanding why types exist means understanding what misinterpretation costs.

Goal

After this lesson you can explain the core problem that types solve (misinterpretation of bits), describe what a type error is in machine terms, distinguish static and dynamic type checking as two strategies for catching the same category of error, and give a concrete example of what goes wrong without type enforcement.

1

The cost of misinterpretation. When a program reads a value using the wrong type rule, the result is a meaningless bit pattern decoded incorrectly. The program then acts on that wrong value. Depending on what the program does next, the consequences range across a spectrum:

  • Silent wrong output. The program continues, but the numbers are wrong. A temperature read as a code point produces nonsense; a price stored as a string treated as a number produces NaN in JS.
  • Crash. The misread value is used in a way that the runtime detects as invalid, and the program halts with an error.
  • Security vulnerability. In low-level languages (C, C++), misinterpreting a buffer as a different type can overwrite memory outside the intended region — a buffer overflow. This is the root cause of entire classes of exploits.

All three outcomes share one origin: wrong type applied to stored bits.

2

What a type error is. A type error is the event where a program attempts to apply a type rule to a value that was not stored with that rule in mind — and the language system (compiler or runtime) detects this mismatch and reports it.

Notice the definition does not say “the program crashes.” A type error is a detected mismatch. What happens after detection depends on the language:

  • Static typing: the compiler detects the mismatch before the program runs and refuses to compile. The error appears as a compiler message.
  • Dynamic typing: the runtime detects the mismatch at the moment the wrong operation is attempted and throws an exception (or, in the worst case, silently produces a nonsense value without any error).

Both strategies are trying to catch the same underlying problem: bits about to be read with the wrong rule.

3

Static typing: catch at compile time. In a statically typed language (TypeScript, Java, Rust, C#), the type of every variable is known before the program runs. The compiler tracks type information through every assignment, function call, and expression. If you write code that would apply the wrong type rule — adding a number and a string as if both were numbers — the compiler reports an error and refuses to produce a runnable program.

The cost: you must declare types explicitly (or let the compiler infer them), and you must fix type errors before you can run any code. The benefit: type errors can never occur at run time in a statically typed, type-correct program. The class of bug is eliminated at compile time.

TypeScript is JS with static typing layered on top. The TypeScript compiler reads your type annotations, checks them, and emits plain JavaScript for the runtime to execute.

4

Dynamic typing: catch at run time. In a dynamically typed language (plain JavaScript, Python, Ruby), types are tracked by the runtime, not the compiler. Every value carries a type tag at run time. When an operation is performed, the runtime checks whether the types make sense for that operation — and if not, throws a TypeError exception.

The cost: type errors only appear when the problematic line actually executes. A bug in a rarely-used code path might go undetected for months. The benefit: you write code faster, without declaring types, and the language is more flexible for exploratory or prototype work.

JavaScript is dynamically typed. When you write "hello" - 5 in JS, the runtime does not crash immediately — it attempts to coerce, producing NaN. When you call a method on null, the runtime throws a TypeError. The runtime detected the mismatch, but only at the moment of execution.

Why this works

Why does TypeScript exist if JavaScript already has a runtime type system? Because run-time detection is late detection. In a large codebase, a type bug might sit in a code path exercised only under unusual conditions. A static type checker eliminates the entire category of type mismatch before the code ships, without needing to run every possible code path. This is the productivity argument for static types: catch bugs when you write the code, not when the user runs into them.

num
type
01000001
bits
A value with its type tag (left cell) and its bits (right cell). The type tag says 'number'; the bits encode 65. A type system uses the tag to ensure only numeric operations are applied to these bits.
Worked example

Tracing a type error from cause to consequence.

Scenario A — Dynamic typing, JS (no prevention):

const price = "9.99";   // accidentally a string, not a number
const discount = 0.1;
const final = price - discount;   // JS coerces: "9.99" - 0.1 = 9.89 (works, but by accident)
const tax = price * 1.08;         // "9.99" * 1.08 = 10.7892 (also works, JS coerces)
const label = price + " USD";     // "9.99 USD" — concatenation, not addition

JS silently coerces the string to a number for - and *, so the arithmetic accidentally works. But + concatenates instead of adding. No error was thrown; the wrong type produced a wrong result silently.

Scenario B — Static typing, TypeScript (caught at compile time):

const price: number = "9.99";  // TS error: Type 'string' is not assignable to type 'number'

The TypeScript compiler refuses to compile this. The misassignment is caught at the moment you write it, before any code runs. The error message is:

Type 'string' is not assignable to type 'number'.

Scenario C — Dynamic typing, JS runtime TypeError:

const x = null;
x.toString();   // TypeError: Cannot read properties of null (reading 'toString')

The runtime detected the attempt to call a method on null (which has no methods) and threw a TypeError. This is run-time detection: the error only appears if this line actually executes.

All three scenarios originate in applying the wrong type to a value. Static typing caught it before execution; dynamic typing caught it at execution; no type checking let it pass silently.

Common mistake

“Static typing is safer; dynamic typing is dangerous.” More precisely: static typing catches type mismatches earlier; dynamic typing catches them later (or, in permissive coercion cases like JS, may not catch them at all). Neither strategy eliminates all bugs — only the specific class of type mismatch bugs. Logic errors, algorithmic errors, and incorrect business rules are invisible to the type system. Type safety is one layer of correctness, not the whole story.

Practice 0 / 5

In JavaScript, typeof ("5" - 2) returns 'number' because JS coerces the string. What is the numeric result of '5' - 2?

TypeScript catches type errors at compile time, before the program runs. JavaScript catches them at run time, while the program executes. How many of these two strategies catch errors BEFORE execution? Type the number.

In JavaScript, null.toString() throws a TypeError. If a function contains this line but is never called during testing, how many TypeErrors will the test suite surface from that line? Type the number.

A C program misreads a 4-byte integer buffer as a character string and writes beyond the buffer end. This is called a buffer overflow. How many bytes does a 32-bit integer occupy (the buffer that was misread)?

In TypeScript, every variable's type must be known before the program runs (or inferrable by the compiler). In JavaScript, types are checked at run time. Which language checks types at compile time: TypeScript (1) or JavaScript (2)? Type the number.

Check yourself
Quiz

What is the core problem that type systems are designed to prevent?

Recap

Types exist because bits have no built-in meaning — and misapplying an interpretation rule produces wrong results, crashes, or security vulnerabilities. A type error is a detected mismatch between the stored type and the applied type. Static typing (TypeScript, Java, Rust) detects mismatches before the program runs, at compile time, eliminating the error class entirely for type-correct code. Dynamic typing (JavaScript, Python) detects mismatches at run time, when the wrong operation is actually attempted. Both strategies serve the same purpose: guarding against misinterpretation of bits. The difference is when the guard fires.

Continue the climb ↑Values and types: multiple-choice review
shortcuts expand
search
K
prev piece
k
next piece
j
cycle tier
t
this menu
?
sources4
expand
  1. 01
  2. 02
  3. 03
  4. 04

Trademarks belong to their respective owners. Editorial reference only.