TypeScript Basic Types · Lesson 3 of 17
TypeScript Types: Primitives, Arrays and Tuples
Every value in TypeScript has a type, but you rarely need to write it: TypeScript infers types from the values you assign. This lesson covers the everyday types – primitives, arrays and tuples – and the professional habit of annotating where it helps and letting inference do the rest.
In this lesson
- Primitive types and annotations
- Type inference: when to annotate
- Arrays and readonly arrays
- Tuples
Primitive types
let course: string = "TypeScript";
let lessons: number = 20; // one number type for integers and decimals
let published: boolean = true;
let views: bigint = 9_007_199_254_740_993n;
let nothing: null = null;
let notSet: undefined = undefined;
lessons += 2;
console.log(course, lessons, published, views, nothing, notSet);
console.log(typeof course, typeof lessons, typeof views);
TypeScript 22 true 9007199254740993n null undefined string number bigint
Use the lowercase names (string, number, boolean). String and Number with capitals are JavaScript wrapper objects – almost never what you want in a type.
Type inference
let city = "Pune"; // inferred: string
const country = "India"; // inferred: the literal type "India" (it can't change)
let marks = [78, 91, 64]; // inferred: number[]
let mixed = [1, "two", true]; // inferred: (string | number | boolean)[]
city = "Delhi"; // fine
city = 42; // error: number is not a string
marks.push("85"); // error: string is not a number
console.log(city, country, marks, mixed);
main.ts(7,1): error TS2322: Type 'number' is not assignable to type 'string'. main.ts(8,12): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
When to write types
- Do annotate function parameters – TypeScript can't guess them.
- Do annotate return types of exported functions and public APIs – it documents and protects the contract.
- Do annotate empty arrays and variables declared without a value:
const ids: number[] = []; - Don't annotate obvious locals:
let count: number = 0adds noise.
Arrays
const scores: number[] = [72, 95, 41, 88];
const names: Array<string> = ["Asha", "Ravi"]; // same as string[]
const matrix: number[][] = [[1, 2], [3, 4]];
const passed = scores.filter((s) => s >= 50); // number[] inferred from the callback
const labels = scores.map((s) => `${s}%`); // string[]
console.log(passed, labels, matrix[1], names.length);
const days: readonly string[] = ["Mon", "Tue", "Wed"];
console.log(days.length, days.includes("Tue"));
// days.push("Thu"); // error: Property 'push' does not exist on type 'readonly string[]'
[ 72, 95, 88 ] [ '72%', '95%', '41%', '88%' ] [ 3, 4 ] 2 3 true
readonly arrays (also written ReadonlyArray<string>) remove every mutating method at compile time – ideal for function parameters that shouldn't change their input.
Index access and noUncheckedIndexedAccess
const top3: number[] = [99, 92, 88];
const fourth = top3[3]; // number at compile time… undefined at run time
console.log(fourth, typeof fourth);
undefined undefined
By default TypeScript types top3[3] as number, though it's undefined. With "noUncheckedIndexedAccess": true (recommended in the previous lesson) it becomes number | undefined, forcing you to check.
Tuples
A tuple is an array with a fixed length and a known type at each position:
const point: [number, number] = [3, 4];
const entry: [string, number] = ["Asha", 91];
const [name, score] = entry; // destructuring keeps the types
function minMax(values: readonly number[]): [min: number, max: number] { // labelled tuple
return [Math.min(...values), Math.max(...values)];
}
const [lo, hi] = minMax([42, 7, 93, 18]);
const httpStatus: [code: number, text: string, retry?: boolean] = [503, "Service Unavailable"];
console.log(point, name, score, lo, hi, httpStatus.length);
[ 3, 4 ] Asha 91 7 93 2
React's useState returns a tuple: const [count, setCount] = useState(0) – the first element is the value, the second the setter, each with its own type.
Practice
Try it yourself
Write
average(values: readonly number[]): numberand a tuple-returningsplit(name: string): [first: string, last: string].Show solution
function average(values: readonly number[]): number { return values.length === 0 ? 0 : values.reduce((a, b) => a + b, 0) / values.length; } function split(name: string): [first: string, last: string] { const [first = "", ...rest] = name.split(" "); return [first, rest.join(" ")]; } console.log(average([70, 85, 90]).toFixed(2), split("Asha Verma"));81.67 [ 'Asha', 'Verma' ]
Quick check
What type does let x = [1, 2] get?
number[].
Where should you always write types?
Function parameters (and usually exported return types).
Array vs tuple?
An array has any length of one element type; a tuple has a fixed length with a type per position.
Summary
- Primitive types: string, number, boolean, bigint, null, undefined (lowercase).
- Inference handles most locals; annotate parameters, public returns and empty values.
- T[] arrays, readonly T[] for read-only data, and [A, B] tuples for fixed positions.
Finished this lesson?
Mark it complete to track your progress, then carry on with the next lesson.