Skip to main content

    Lesson 2 • Beginner

    Basic Types 🧱

    By the end of this lesson you'll be able to label every value in your code with the right type — text, numbers, true/false, lists, and fixed-shape tuples — and you'll know exactly when to reach for any, unknown, or never. This is the foundation that makes every later TypeScript feature click.

    What You'll Learn

    • Use the primitive types string, number, and boolean correctly
    • Type arrays two ways — number[] and Array<T> — and know they're identical
    • Build tuples: fixed-length arrays with a type for each position
    • Choose between any, unknown, and never (and why any defeats the point)
    • Handle null and undefined deliberately, not by accident
    • Decide when to let TypeScript infer a type vs writing it explicitly, plus literal types

    💡 Real-World Analogy

    A type is a label on a container. A jar labelled "Sugar" (string) is meant for text; a measuring cup labelled "Cups" (number) is for numbers; a light switch (boolean) is only ever on or off. Plain JavaScript lets you pour anything into any jar and only finds out it was wrong when something breaks at runtime. TypeScript checks the labels before your program runs and stops you pouring salt into the sugar jar. An array is a row of identical jars; a tuple is a labelled tray where slot 1 is always sugar, slot 2 is always flour, in that exact order.

    📊 The Core Types at a Glance

    TypeHoldsExample
    stringTextlet n: string = "Al";
    numberWhole or decimallet a: number = 9.99;
    booleantrue / falselet ok: boolean = true;
    number[]List of one typelet s: number[] = [1, 2];
    [string, number]Fixed tuple["Al", 28]
    null / undefinedAbsence of a valuelet x: string | null = null;

    Note: TypeScript type names are lowercase — use string, not String. The capitalised String is the rarely-needed wrapper object, and using it is a classic beginner slip.

    1. Primitive Types

    A primitive is a single, simple value. The three you'll use constantly are string (text), number (any number — TypeScript has no separate int/float), and boolean (only true or false). In a .ts file you write the type after a colon: let age: number = 28;. Read this worked example, run it, then you'll write your own.

    Worked example: string, number, boolean

    Read every comment, run it, and check the output matches.

    Try it Yourself »
    JavaScript
    // A variable's TYPE fixes what kind of value it can hold.
    // In TypeScript you can write the type after a colon:  let name: string = "Alice";
    // The console.logs below run as plain JavaScript so you can see the values.
    
    // string — text, in "double" or 'single' quotes or `backticks`
    let firstName = "Alice";              // type: string
    let greeting = `Hello, ${firstName}!`; // backticks let you embed ${...}
    console.log(greeting);                // Hello, Alice!
    
    // number — ONE type for integer
    ...

    Your turn. The program below is almost complete — fill in the three blanks marked ___ using the hints in the comments, then run it.

    🎯 Your turn: declare three values

    Fill in the ___ blanks, then check your output against the expected lines.

    Try it Yourself »
    JavaScript
    // 🎯 YOUR TURN — replace each ___ then press "Try it Yourself".
    
    // 1) A string called "city" set to a city name
    let city = ___;        // 👉 text in "double quotes"
    
    // 2) A number called "year" set to the current year
    let year = ___;        // 👉 a whole number, e.g. 2026
    
    // 3) A boolean called "isOpen" set to true
    let isOpen = ___;      // 👉 true or false (no quotes)
    
    // These already work once your variables exist:
    console.log(`In ${year}, the shop in ${city} is open: ${isOpen}`);
    
    // ✅ E
    ...

    🔎 Deep Dive: Inference vs Explicit Annotations

    You don't always have to write the type. When you give a variable a value, TypeScript infers (figures out) the type for you. Both lines below mean exactly the same thing — the second is just less typing:

    let city: string = "London";  // explicit annotation
    let city = "London";          // inferred — TypeScript knows it's string
    
    // Either way, this is now an ERROR:
    city = 42;  // ❌ Type 'number' is not assignable to type 'string'

    When to be explicit: on function parameters and return types, and whenever the value doesn't make the type obvious. When to let it infer: simple let/const with an obvious literal. Idiomatic TypeScript leans on inference — don't clutter let count = 0; with a redundant : number.

    🔎 Deep Dive: Literal Types

    A type can be narrower than "any string" — it can be one specific value, called a literal type. Combine a few with | ("or") and you get a tidy set of allowed options, like a dropdown menu:

    let direction: "up" | "down";  // only these two strings are allowed
    direction = "up";              // ✅ fine
    direction = "left";            // ❌ '"left"' is not assignable
    
    // This is also why const is "tighter" than let:
    const mode = "dark";  // type is the literal "dark", not just string
    let theme = "dark";   // type widens to string

    Literal types let you describe rules like "status is 'active', 'paused', or 'closed'" and have the compiler reject anything else — far safer than a loose string.

    2. Arrays & Tuples

    An array holds many values of the same type. You write the type as number[] ("array of number") or, identically, as Array<number> using a generic — pick whichever you find clearer. A tuple is a fixed-length array where each position has its own type, like [string, number, boolean]; it's perfect for a value that always has the same shape, such as a coordinate or a row of data.

    Worked example: arrays & tuples

    See typed lists and fixed-shape tuples in action.

    Try it Yourself »
    JavaScript
    // === Arrays — many values of the SAME type ===
    // In TypeScript:  let scores: number[] = [95, 87, 92];
    // The "number[]" means "an array where every element is a number".
    let scores = [95, 87, 92];
    console.log("scores:", scores);          // scores: [ 95, 87, 92 ]
    console.log("first:", scores[0]);        // first: 95
    console.log("how many:", scores.length); // how many: 3
    
    let names = ["Alice", "Bob"];            // string[]
    names.push("Charlie");                   // adding another string is 
    ...

    Now you try. Build a couple of arrays and a tuple, then run the program to check the output.

    🎯 Your turn: arrays and a tuple

    Fill in the ___ blanks, then check against the expected output.

    Try it Yourself »
    JavaScript
    // 🎯 YOUR TURN — fill in the blanks marked with ___
    
    // 1) An array of three numbers (the type would be number[])
    let temps = ___;          // 👉 e.g. [18, 21, 19]
    
    // 2) Add one more reading to the end of the array
    temps.push(___);          // 👉 any number, e.g. 22
    
    // 3) A tuple describing a product: [name (string), price (number)]
    let product = ___;        // 👉 e.g. ["Coffee", 2.5]
    
    console.log("readings:", temps);
    console.log(`${product[0]} costs £${product[1]}`);
    
    // ✅ Expected output (e
    ...

    3. any vs unknown vs never

    These three special types are about the edges of the type system. any switches checking off — the value can be anything and TypeScript stops protecting you (use it as a last resort). unknown is the safe sibling: it can hold anything too, but TypeScript forces you to narrow (prove) the type before you use it. never is the type with no possible values — the return type of a function that always throws or loops forever.

    Worked example: any, unknown, never

    See why any is dangerous and how unknown forces a type check.

    Try it Yourself »
    JavaScript
    // === any — turns OFF type checking (use as a last resort) ===
    // "any" tells TypeScript "trust me, don't check this".
    // It compiles, but you lose every safety net:
    let data = 42;
    data = "hello";   // with type 'any' this is allowed
    data = [1, 2, 3]; // ...and so is this
    console.log("data is now:", data); // data is now: [ 1, 2, 3 ]
    // The danger: data.foo.bar would COMPILE but 💥 crash at runtime.
    
    // === unknown — the SAFE alternative to any ===
    // You CAN store anything in an 'unknown', but
    ...

    🔎 Deep Dive: null and undefined

    Both represent "no value", but with different intent. undefined means "this was never set". null means "intentionally empty — there's deliberately nothing here". You allow a value to be missing by adding it to the type with |:

    let nickname: string | null = null;   // explicitly "no nickname yet"
    nickname = "Ace";                     // later, give it a value
    
    let middleName: string | undefined;   // not set yet -> undefined
    
    // With "strictNullChecks" on (the recommended default), TypeScript
    // makes you handle the empty case before using the value:
    if (nickname !== null) {
      console.log(nickname.toUpperCase()); // ✅ safe — we checked first
    }

    This is one of TypeScript's biggest wins: it turns "cannot read property of null" runtime crashes into compile-time errors you fix before shipping.

    📝 The Type Syntax (read-only reference)

    This is what the same ideas look like with full TypeScript annotations. You can't run it here (it's TS-only syntax), but it's worth reading closely:

    // Primitives
    let firstName: string = "Alice";
    let age: number = 28;
    let isActive: boolean = true;
    
    // Arrays — two identical ways to write the type
    let scores: number[] = [95, 87, 92];
    let scores2: Array<number> = [95, 87, 92];
    
    // Tuple — one type per position, fixed length
    let user: [string, number, boolean] = ["Alice", 28, true];
    
    // Absence of a value
    let nickname: string | null = null;
    let middleName: string | undefined;
    
    // Literal types — a fixed set of allowed values
    let status: "active" | "paused" | "closed" = "active";
    
    // The special types
    let anything: any = 42;          // checking OFF (avoid)
    let safe: unknown = getInput();  // must narrow before use
    function fail(msg: string): never { throw new Error(msg); }

    Pro Tips

    • 💡 Reach for unknown, never any, when a value's type is genuinely uncertain. unknown keeps the safety net; any removes it everywhere it spreads.
    • 💡 Let TypeScript infer simple values: let count = 0; is cleaner than let count: number = 0;. Be explicit on function signatures.
    • 💡 Use lowercase type names: string, number, boolean — not String, Number, Boolean.
    • 💡 Use a tuple, not an array, when the length and the meaning of each slot are fixed (e.g. [lat, lng]).

    Common Errors (and the fix)

    • "TS2322: Type 'number' is not assignable to type 'string'" — you put a value of the wrong type into a typed variable, e.g. let name: string = 42;. Fix the value ("42") or fix the type (number).
    • "TS2345: Argument of type 'string' is not assignable to parameter of type 'number'" — you passed the wrong type into a function call, like double("5") where double(n: number) was expected. Convert it first: double(Number("5")).
    • Why any defeats the point: let x: any = ...; makes x.anything.goes compile happily and then crash at runtime. any silences the very checker you installed TypeScript for — use unknown and narrow instead.
    • "TS2532: Object is possibly 'undefined'" — you used a value that might be empty. Check it first: if (x !== undefined) { ... }, or use x?.prop.
    • Capital-letter types: let s: String = "hi"; compiles but is wrong style. Use the lowercase primitive string.

    📋 Quick Reference — Types

    TypeMeansExample
    stringTextlet s: string = "hi"
    numberAny numberlet n: number = 42
    booleantrue / falselet b: boolean = true
    T[] / Array<T>List of one typelet a: number[] = [1, 2]
    [A, B]Fixed tuple["Al", 28]
    "a" | "b"Literal unionlet m: "on" | "off"
    null / undefinedNo valuestring | null
    anyChecking off (avoid)let x: any = 1
    unknownSafe "anything"let x: unknown = f()
    neverNo possible valuefunction fail(): never

    Frequently Asked Questions

    Q: Is there really no separate type for integers and decimals?

    Correct — like JavaScript, TypeScript has one number type for both 28 and 9.99. (There's also bigint for very large whole numbers, but you'll rarely need it.)

    Q: What's the real difference between any and unknown?

    Both can hold any value. With any you can then do anything with it and TypeScript won't complain (unsafe). With unknown you must first check the type (typeof, Array.isArray, etc.) before using it, so mistakes are caught at compile time.

    Q: When should I write the type and when should I let TypeScript guess?

    Let it infer simple initialised variables (let name = "Al"). Be explicit on function parameters, function return types, and any case where the value alone doesn't make the intended type obvious.

    Q: Array or tuple — how do I choose?

    Use an array when you have any number of items of the same type (number[]). Use a tuple when the length is fixed and each slot means something specific ([name, age]).

    Mini-Challenge: Ticket Summary

    No blanks this time — just a brief and a starting outline. Build it, run it, and check your output against the example in the comments. This is the kind of small, typed program real apps are made of.

    🎯 Mini-Challenge: build a ticket summary

    Declare the values yourself and print the formatted total.

    Try it Yourself »
    JavaScript
    // 🎯 MINI-CHALLENGE: Movie ticket summary
    // In a real .ts file you'd annotate the types; here just build the values.
    //
    // 1. A string "title" (the film name) and a number "tickets".
    // 2. A number "pricePerTicket" (e.g. 12.5).
    // 3. A boolean "isMember" (members get 10% off — your choice true/false).
    // 4. Work out total = tickets * pricePerTicket, then apply the
    //    member discount if isMember is true.
    // 5. Print:  "<tickets> tickets for <title>: £<total>"
    //
    // ✅ Example (3 tickets, £12.
    ...

    🎉 Lesson Complete

    • ✅ Primitives: string (text), number (any number), boolean (true/false)
    • ✅ Arrays are T[] or Array<T>; tuples are fixed-length with a type per slot
    • any turns checking off — prefer unknown and narrow before use
    • never is the type with no values; null/undefined are handled with | unions
    • ✅ Let TypeScript infer obvious types; be explicit on function signatures
    • ✅ Literal types ("on" | "off") restrict a value to a fixed set of options
    • Next lesson: Interfaces & Type Aliases — describe the shape of whole objects

    Sign up for free to track which lessons you've completed and get learning reminders.

    Previous

    Cookie & Privacy Settings

    We use cookies to improve your experience, analyze traffic, and show personalized ads. You can manage your preferences below.

    By clicking "Accept All", you consent to our use of cookies for analytics and personalized advertising. You can customize your preferences or reject non-essential cookies.

    Privacy PolicyTerms of Service

    Install LearnCodingFast

    Learn faster with the app on your home screen.