Core Type System & Inference

0/6 in this phase0/21 across the roadmap

📖 Concept

TypeScript's type system is structural (also called duck typing) — it cares about the shape of data, not its name or origin. If an object has the right properties, it's compatible, regardless of where it came from.

Primitive types: string, number, boolean, null, undefined, bigint, symbol Special types: any (escape hatch), unknown (safe any), never (impossible), void (no return) Object types: object, {}, arrays, tuples, functions

Type Inference is TypeScript's ability to automatically determine types without explicit annotations. TS uses control flow analysis to narrow types through your code.

Structural Typing vs Nominal Typing:

  • Structural (TypeScript): Two types are compatible if they have the same shape/structure
  • Nominal (Java/C#): Two types are compatible only if they have the same name/declaration

This means in TypeScript, you never need implements for a class to satisfy an interface — if it has the right shape, it works.

Type vs Value space: TypeScript has two parallel worlds — the type space (erased at runtime) and the value space (exists at runtime). interface and type live only in type space; class lives in both.

🏠 Real-world analogy: Structural typing is like a USB port. It doesn't care what brand the cable is — if the plug fits the shape (has the right pins), it works. Nominal typing would require the cable to be from the same manufacturer.

💻 Code Example

codeTap to expand ⛶
1// Primitive types
2let name: string = "Alice";
3let age: number = 30;
4let active: boolean = true;
5let big: bigint = 9007199254740991n;
6let id: symbol = Symbol("id");
7
8// Special types
9let anything: any = 42; // ⚠️ Escape hatch — disables type checking
10anything = "now a string"; // No error — defeats the purpose of TS!
11
12let safe: unknown = 42; // ✅ Safe alternative to any
13// safe.toFixed(); // ❌ Error! Must narrow first
14if (typeof safe === "number") {
15 safe.toFixed(2); // ✅ After narrowing, it's safe
16}
17
18function throwError(msg: string): never {
19 throw new Error(msg); // never returns — the return type is 'never'
20}
21
22function log(msg: string): void {
23 console.log(msg); // void = no meaningful return value
24}
25
26// Type inference in action
27let x = 10; // inferred as 'number'
28let y = [1, 2, 3]; // inferred as 'number[]'
29let z = { name: "Alice" }; // inferred as '{ name: string }'
30
31// const narrows to literal types
32const pi = 3.14; // type: 3.14 (literal), not 'number'
33const greeting = "hello"; // type: "hello" (literal), not 'string'
34
35// Structural typing — shape matters, not name
36interface Point {
37 x: number;
38 y: number;
39}
40function distance(p: Point): number {
41 return Math.sqrt(p.x ** 2 + p.y ** 2);
42}
43// No 'implements Point' needed — just match the shape!
44const myPoint = { x: 3, y: 4, z: 5 }; // Extra property OK
45distance(myPoint); // ✅ Works! Has x and y
46
47// Arrays and tuples
48let nums: number[] = [1, 2, 3];
49let pair: [string, number] = ["age", 30]; // Fixed length & types
50let readonly_arr: readonly number[] = [1, 2, 3];
51// readonly_arr.push(4); // ❌ Error: Property 'push' does not exist

🏋️ Practice Exercise

Mini Exercise:

  1. Declare variables using each primitive type and each special type
  2. Show the difference between any and unknown by trying to call methods on both
  3. Create an object that satisfies an interface WITHOUT explicitly implementing it (structural typing)
  4. Use const vs let declarations and observe how TypeScript infers literal types vs wider types
  5. Create a tuple type for a coordinate [number, number, number] and try adding a 4th element

⚠️ Common Mistakes

  • Using any instead of unknownany disables ALL type checking; unknown forces you to narrow before use

  • Confusing void with undefinedvoid means 'no meaningful return'; a void function CAN return undefined implicitly

  • Not understanding structural typing — coming from Java/C#, developers expect nominal typing and add unnecessary implements clauses

  • Thinking never is the same as voidvoid returns nothing; never means the function NEVER returns (throws or infinite loop)

  • Over-annotating when inference would suffice — let x: number = 5 is redundant; let x = 5 is cleaner and equally type-safe

💼 Interview Questions

🎤 Mock Interview

Practice a live interview for Core Type System & Inference