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.

Learn Python, JavaScript, Java and more with free interactive lessons, real projects and AI-powered help. Beginner-friendly.

Part of the free TypeScript course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

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.

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.

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

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:

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 .

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:

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.

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

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.

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 | :

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.

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:

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.

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] ).

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.

Practice quiz

Which type does TypeScript use for both 28 and 9.99?

  • int
  • float
  • number
  • decimal

Answer: number. TypeScript (like JavaScript) has one number type for integers and decimals alike; there is no separate int or float.

How do you write the type for an array where every element is a number?

number[] (equivalently Array<number>) means an array whose every element is a number. [number] would be a one-element tuple.

What does the tuple type [string, number, boolean] describe?

  • An array of any of those three types in any order
  • Exactly three elements: a string, then a number, then a boolean
  • An object with three keys
  • A union of string, number, and boolean

Answer: Exactly three elements: a string, then a number, then a boolean. A tuple is a fixed-length array with a type per position: index 0 must be a string, 1 a number, 2 a boolean.

What does typing a value as any do?

  • Restricts it to primitive values
  • Turns OFF type checking for that value
  • Forces you to narrow before using it
  • Makes it read-only

Answer: Turns OFF type checking for that value. any switches off type checking entirely, so even nonsense like data.foo.bar compiles and may crash at runtime.

What is the safe alternative to any that forces you to check the type before use?

  • never
  • void
  • unknown
  • object

Answer: unknown. unknown accepts any value but won't let you use it until you narrow it with a check like typeof, keeping the safety net.

What is the never type?

  • A value that is always null
  • The type with no possible values
  • Another name for any
  • A type for empty strings

Answer: The type with no possible values. never is the type with no possible values - for example the return type of a function that always throws or loops forever.

Which is the correct, idiomatic primitive type name in TypeScript?

  • String
  • Str
  • string
  • Text

Answer: string. Type names are lowercase: string, number, boolean. Capitalised String is the wrapper object and is a classic beginner slip.

Given let mode = "dark" vs const mode = "dark", how do their inferred types differ?

  • Both are the literal type "dark"
  • let is the literal "dark"; const widens to string
  • let widens to string; const is the literal "dark"
  • Both widen to string

Answer: Both widen to string. A const holds the narrow literal type "dark", while a let widens to the broader type string because it can be reassigned.

How do you allow a variable to be a string OR explicitly null?

  • string null
  • string | null
  • string & null
  • string?

Answer: string | null. A union with | lets a value be one of several types: string | null permits a string or the value null.

When is it most idiomatic to write an explicit type annotation?

  • On every let and const, always
  • Never - always rely on inference
  • On function parameters and return types
  • Only on boolean variables

Answer: On function parameters and return types. Let inference handle obvious initialised variables; be explicit on function parameters and return types where intent isn't obvious.