Table of Contents
- 2.1 What is a Collection?
- 2.2 Arrays in JavaScript
- 2.3 Arrays in TypeScript
- 2.4 readonly Arrays
- 2.5 Arrays Summary
- 3.1 Tuples in TypeScript
- 3.2 Tuple Gotchas
- 3.3 Tuple Summary
- 4.1 Sets in JavaScript
- 4.2 Sets in TypeScript
- 4.3 Set Summary
- 5.1 Maps in JavaScript
- 5.2 Maps in TypeScript
- 5.3 Maps Summary
1. Introduction to Collections
This course covers four specific data structures: arrays, tuples, sets and maps. The main goal is to understand how these structures work in TypeScript.
It is important to remember that TypeScript is a superset of JavaScript: it is built on top of the JavaScript language. Some of these collections already existed in JavaScript even before TypeScript appeared. This is why each collection is first presented in its pure JavaScript version, before discussing the specific contributions of TypeScript — this allows you to better distinguish what belongs to one or the other.
2. Arrays
2.1 What is a Collection?
Collections are data structures often integrated directly into programming languages. They allow groups of data to be stored and manipulated much more efficiently than storing them separately in multiple variables.
This course covers four specific data structures:
- Array (array)
- Tuple
- Set
- Map (key-value dictionary)
2.2 Arrays in JavaScript
Creating an array
JavaScript arrays are created by placing values in square brackets []:
let cityNames = ["Orlando, FL", "Atlanta, GA", "New York, NY"];
Access to elements
Access to an element is done with brackets [] after the name of the variable, indicating a numeric index. Arrays are zero-indexed: the first index is 0, the next 1, then 2, and so on. If an array contains three elements, the last index is therefore 2. The length property always contains the number of elements present in the array.
let first = cityNames[0]; // "Orlando, FL"
let len = cityNames.length; // 3
Adding and Mutating Methods
These four methods modify (mutate) the array on which they are called:
| Method | Effect | Returned value |
|---|---|---|
push() | Adds one or more elements at the end | New array length |
unshift() | Add one or more elements to beginning | New array length |
pop() | Remove last element | The deleted item |
shift() | Remove first element | The deleted item |
cityNames.push("Los Angeles, CA"); // retourne 4 (nouvelle longueur)
let removed = cityNames.pop(); // retourne "Los Angeles, CA"
Non-mutating methods
Some developers prefer to use non-mutating alternatives that return a new array rather than modifying the original:
concat(): similar topush(), but returns a new array. “Concatenate” simply means “put things together.”slice(): returns a portion of the array in the form of a new array.toSorted(): returns a sorted version without modifying the original (non-mutant version ofsort()).toReversed(): same forreverse().toSpliced(): same forsplice().
let newCities = cityNames.concat(["Miami, FL", "Chicago, IL"]);
let sliced = cityNames.slice(1, 3);
Search in an array
indexOf(): returns the index of the first occurrence of a value, or-1if it is not found.findIndex(): returns the index of the first element satisfying a test function, or-1.find(): returns the first element satisfying a test function, orundefined.includes(): returnstrueorfalsedepending on whether the value exists in the array.
let idx = cityNames.indexOf("Orlando, FL"); // 0
let found = cityNames.find(c => c === "Atlanta, GA"); // "Atlanta, GA"
Iteration
forEach(): executes a function for each element, without returning a value.for...of: modern loop to iterate over the values of an array.
cityNames.forEach(city => console.log(city));
for (let city of cityNames) {
console.log(city);
}
Transformation and filtering
map(): creates a new array by applying a function to each element. The return type is inferred from the value returned by the callback.filter(): creates a new array containing only the elements satisfying a condition.reduce(): reduces an array to a single value by applying an accumulator function.
let upperCities = cities.map(city => city[0].toUpperCase() + city.slice(1));
let hotCities = temperatures.filter(temp => temp > 80);
Sort
sort()/toSorted(): sorts elements according to the default Unicode order, or according to a custom comparison function.
let sorted = temps.toSorted((a, b) => a - b); // tri numérique croissant
2.3 Arrays in TypeScript
TypeScript adds type safety and stricter rules on top of JavaScript arrays.
Declaration of the type of an array
There are two equivalent syntaxes:
let cityNames: string[] = []; // Syntaxe avec crochets
let cityNames: Array<string> = []; // Syntaxe générique avec angle brackets
These two forms mean exactly the same thing. The syntax with Array<> (angle brackets) is generally used in the context of generics TypeScript.
Type safety on methods
TypeScript checks that the added values match the declared type:
cityNames.push("Miami, FL"); // OK
cityNames.push(50); // Erreur : number n'est pas assignable à string
Method return types
It is necessary to take into account the return types of the methods included in the arrays:
pop()returnsstring | undefined(and not juststring), because the array may be empty. If no element is found, the method returns nothing, hence theundefinedin the union type.find()returnsstring | undefinedfor the same reason.indexOf()/findIndex()return anumber(the value-1if not found).
let city = cityNames.pop();
if (city == undefined) {
console.log("Aucune ville disponible");
} else {
console.log(`Nom de la ville : ${city}`);
}
Type guards for unions with undefined
Whenever a union type includes undefined, a type guard is recommended before using the value. Without this check, TypeScript will generate an error at compile time.
Type inference for find()
The return type of find() can be refined depending on the callback. For example, if the callback compares to a literal value, the compiler may infer a union with a literal value rather than the general type of the array.
let orlando = cityNames.find(cityName => cityName == "Orlando, FL");
// Type inféré : "Orlando, FL" | undefined
2.4 readonly Arrays
TypeScript introduces a feature that does not exist in native JavaScript: the ability to mark an array as readonly.
Syntax
let options: readonly Config[] = [...]; // Syntaxe avec readonly
let options: ReadonlyArray<Config> = [...]; // Syntaxe générique équivalente
The two forms are equivalent. The readonly keyword comes before the type, and the generic form uses ReadonlyArray instead of Array.
Use cases
- Configuration Values: Marking a configuration array as
readonlysignals to other developers that they should not attempt to add or modify these values. - Function parameters: if a function should not mutate the array received as a parameter, using
readonlyin the function signature is a good practice.
interface Config {
option: string;
val: string;
}
function applyConfig(options: readonly Config[]) {
options.pop(); // Erreur TypeScript : impossible de modifier un array readonly
}
Important: check at compile time only
The readonly is a compile-time check only. At runtime, the array remains mutable in JavaScript. This is just an additional way to detect unexpected usage during the compilation phase.
2.5 Summary of Arrays
| Characteristic | ArrayTypeScript |
|---|---|
| Collection type | Ordered collection of data |
| Syntax | type[] or Array<type> (angle bracket/generic) |
| Indexing | By position (numeric), starting from 0 |
| Duplicates | Authorized |
| Length | Dynamic (can grow and shrink) |
| Sort | Yes, with customizable sorting functions |
| Immutability | Mutable by default; immutable version with readonly type[] or ReadonlyArray<type> |
3. Tuples
3.1 Tuples in TypeScript
Definition
tuples are a special kind of array, with fixed length and positional types. Unlike other collections in this course, tuples are unique to TypeScript — they do not exist in the JavaScript language.
Declaration
A tuple is declared by placing types in square brackets []:
let cityData: [string, number, string];
This tuple defines three types in brackets. So it can only hold three values, and each position must match the declared type.
cityData = ["Orlando, FL", 85, "Cloudy"]; // OK
cityData = ["Orlando, FL", "85", "Cloudy"]; // Erreur : string à la place de number en position 1
Difference between Array and Tuple
Even if the syntax resembles that of an array, the difference can be seen in the type annotation:
- Array: a single type followed by brackets →
string[] - Tuple: a fixed set of multiple types, all surrounded by square brackets →
[string, number, string]
Access to values
The values of a tuple are accessible by positional index, 0 being the first element:
let temp = cityData[1]; // 85
Named tuples
TypeScript allows you to name the positions of a tuple for better readability:
let cityData: [city: string, temperature: number, condition: string, note?: string];
Important: Names do not affect how values are accessed. Access remains positional by digital index. The names only serve as documentary labels to help developers understand what data is represented at each position.
Optional values
Tuples can have optional values, indicated by ?. Optional values must be placed at the end of the tuple:
let cityData: [city: string, temperature: number, condition: string, note?: string];
cityData = ["Orlando, FL", 85, "Cloudy"]; // OK
cityData = ["Orlando, FL", 85, "Cloudy", "182 ans cette année"]; // OK aussi
Main use case: return several values from a function
The main use case for tuples is to return multiple values from a function. We can then destructure the returned tuple into individual variables:
function gatherData(): [string, number, string] {
let city = "Orlando, FL";
let temperature = 85;
let condition = "Cloudy";
return [city, temperature, condition];
}
let [city, temperature, condition] = gatherData();
readonly Tuples
Just like arrays, tuples can be marked readonly:
let rTuple: readonly [string, number, string] = ["Atlanta, GA", 75, "Rainy"];
rTuple[1] = 80; // Erreur TypeScript
3.2 Tuple Gotchas
Gotcha 1: Type inference with identical values
If we use type inference (without explicit annotation) and the values all have the same type, the compiler does not infer a tuple — it infers an array:
let singleTypeTuple = ["Orlando, FL", "85 degrees", "Cloudy"];
// TypeScript infère : string[] (et non [string, string, string])
Solution: use as const to force inference as a readonly tuple with literal values:
let singleTypeTuple = ["Orlando, FL", "85 degrees", "Cloudy"] as const;
// TypeScript infère : readonly ["Orlando, FL", "85 degrees", "Cloudy"]
Gotcha 2: Iteration with for...of
The problem with iterating a tuple via for...of is that the type of the loop variable is not inferred element by element. Since the compiler does not know which element is being read, it infers a union type of all the types of the tuple:
let t2: [string, number, string] = ["Orlando, FL", 85, "Cloudy"];
for (let t of t2) {
console.log(t); // t est de type : string | number
}
Recommended solution: use destructuring, which allows the compiler to know precisely the type of each element:
let [n, t, c] = t2;
// n: string, t: number, c: string — types correctement inférés
This behavior also explains why we generally don’t create tuples with many values: if a tuple becomes too large, another data structure is probably more suitable.
3.3 Summary of Tuples
| Characteristic | TypeScript tuple |
|---|---|
| Collection type | TypeScript collection only (does not exist in pure JS) — fixed-length array and mixed types |
| Syntax | [type1, type2, ...] — types of each position written between brackets |
| Indexing | By position (numeric), starting from 0 |
| Duplicates | Allowed, as long as types match position |
| Length | Fixed, defined by declared types |
| Sort | Not applicable (values mapped to fixed positions) |
| Immutability | Mutable by default; immutable version with readonly [type1, type2, ...] |
4. Sets
4.1 Sets in JavaScript
Definition
A Set is a collection that contains only unique values. We create a Set by instantiating the Set class. We can provide initial values by passing an array to the constructor:
let conditions = new Set(["Rainy", "Sunny", "Cloudy", "Rainy", "Rainy"]);
// Le Set ne contiendra que : "Rainy", "Sunny", "Cloudy" (doublons ignorés automatiquement)
Automatic deduplication
If you try to add five values, four of which are identical, the Set will ignore duplicates and will only contain two values. This automatic deduplication is one of the main use cases for Sets: filtering to a set of unique values occurs automatically.
Insertion order
Unlike arrays, the values of a Set are not sortable. They follow the insertion order: the order in which the elements are added is the order in which they are found in the Set.
Main methods
| Method / Property | Description |
|---|---|
add(value) | Adds a value to the Set |
delete(value) | Remove a value from Set |
clear() | Completely empty the Set |
has(value) | Returns true if the value exists, false otherwise |
size | Property containing the number of elements in the Set |
Note: we do not pass an index to access the values of a Set. We use
has()with the value directly.
Iteration
for...of: preferred method for iterating over a Set, captures an element on each iteration.forEach(): alternative available.
for (let condition of conditions) {
console.log(condition);
}
4.2 Sets in TypeScript
Type declaration
To declare a Set in TypeScript, we write the type between angle brackets after the word Set:
let conditions: Set<string> = new Set();
All values in the Set must match this type. The compiler intercepts any attempt to add a different type:
conditions.add("Rainy"); // OK
conditions.add(3); // Erreur : number n'est pas assignable à string
ReadonlySet
For a read-only Set, TypeScript provides the specific type ReadonlySet:
let cond: ReadonlySet<string> = new Set(["Rainy", "Sunny"]);
cond.add("Cloudy"); // Erreur TypeScript
Important: unlike arrays and tuples where we can use the
readonlykeyword, for Sets we must use theReadonlySettype. Thereadonlykeyword alone does not work to make a Set immutable.
Other than these typing considerations, Sets in TypeScript work exactly like JavaScript.
4.3 Summary of Sets
| Characteristic | SetTypeScript |
|---|---|
| Collection type | Collection now insertion order |
| Syntax | new Set<type>() — angle brackets data type |
| Indexing | None — access by value via has() |
| Duplicates | Impossible — attempts to add a duplicate are ignored |
| Length | Dynamic (can grow and shrink) |
| Sort | Unsortable — preserves insertion order |
| Immutability | Mutable by default; immutable version with ReadonlySet<type> |
5. Maps
5.1 Maps in JavaScript
Definition
A Map is a collection that associates values with keys. This is different from arrays which use positional indexes. We create a Map by instantiating the Map class:
let weatherData = new Map();
Keys
Keys can be any JavaScript value, and they must be unique. Note: the word set here has nothing to do with the Set collection — it simply describes the action of setting a key and value on the Map.
Main methods
| Method / Property | Description |
|---|---|
set(key, value) | Adds or updates a key-value pair in the Map |
get(key) | Returns the value associated with the key, or undefined if the key does not exist |
delete(key) | Removes the key-value pair corresponding to the key |
has(key) | Returns true if the key exists, false otherwise |
size | Property containing the number of keys in the Map |
clear() | Empty the Map completely |
weatherData.set('Orlando', { temperature: 85, condition: "Cloudy" });
let cityWeather = weatherData.get('Orlando'); // { temperature: 85, condition: "Cloudy" }
Iteration
for...ofwith destructuring: preferred method for iterating over key-value pairs.
for (let [key, value] of weatherData) {
console.log(key, value);
}
keys(): returns an iterator on keys only.values(): returns an iterator on values only.forEach(): also available, but the order of the arguments in the callback is reversed compared tofor...of— it is(value, key)and not(key, value).
weatherData.forEach((value, key) => { // Note : value avant key dans forEach
console.log(key, value);
});
5.2 Maps in TypeScript
Type declaration
To declare a Map in TypeScript, we must declare the type of the key AND the type of the value, separated by a comma, between angle brackets after the name of the Map class:
let weatherData: Map<string, { temperature: number, condition: string }> = new Map();
The key and value can have different types from each other, but each actual key and value must match its declared type. The compiler catches type incompatibilities:
weatherData.set('Orlando', { temperature: 85, condition: "Cloudy" }); // OK
weatherData.set(100, { temperature: 100, condition: "Sunny" }); // Erreur : clé number (attendu string)
weatherData.set('LA', { temperature: "100", condition: "Sunny" }); // Erreur : temperature string (attendu number)
Return type of get()
Since get() accepts a key to search for a value, and this value might not exist, get() returns a union type between the value type and undefined:
let city = weatherData.get('New York');
// Type inféré : { temperature: number, condition: string } | undefined
if (city == undefined) {
console.log("Ville non trouvée");
} else {
console.log(city.condition, city.temperature);
}
A type guard is therefore necessary before using the result of get().
ReadonlyMap
For a read-only Map, TypeScript provides the specific type ReadonlyMap:
let wd: ReadonlyMap<string, { temperature: number, condition: string }> = new Map([
["Orlando", { temperature: 85, condition: "Cloudy" }],
["New York", { temperature: 75, condition: "Sunny" }],
["Draper", { temperature: 70, condition: "Rainy" }]
]);
wd.set('Los Angeles', { temperature: 90, condition: "Sunny" }); // Erreur TypeScript
The syntax may seem a little strange: we first create a standard Map with its key-value pairs, then we make a copy of it as ReadonlyMap. As with Sets, we cannot use the readonly keyword directly — we must use the ReadonlyMap type.
5.3 Map Summary
| Characteristic | MapTypeScript |
|---|---|
| Collection type | Collection that associates data values with unique keys |
| Syntax | new Map<keyType, valueType>() — key and value types in angle brackets |
| Indexing | By key — we provide a key to access the associated value |
| Duplicates | Duplicate keys impossible; duplicate values allowed |
| Length | Dynamic (can grow and shrink as keys are added or removed) |
| Sort | Unsortable — keys retain insertion order |
| Immutability | Mutable by default; immutable version with ReadonlyMap<keyType, valueType> |
6. Comparative table of the four collections
| Characteristic | Array | Tuple | Set | Map |
|---|---|---|---|---|
| Origin | JavaScript + TypeScript | TypeScript only | JavaScript + TypeScript | JavaScript + TypeScript |
| Structure | Ordered collection | Fixed-length array and mixed types | Unique Values Collection | Key → value association |
| Statement | type[] or Array<type> | [type1, type2, ...] | new Set<type>() | new Map<keyType, valueType>() |
| Indexing | By numerical position (0-based) | By numerical position (0-based) | By value via has() | By key via get() |
| Duplicates | Authorized | Allowed (if types match) | Impossible (self-deduplicate) | Unique keys; duplicate values OK |
| Length | Dynamic | Fixed | Dynamic | Dynamic |
| Sort | Yes (with customizable function) | Not applicable | No (insertion order) | No (key insertion order) |
| Immutable version | readonly type[] / ReadonlyArray<type> | readonly [type1, type2, ...] | ReadonlySet<type> | ReadonlyMap<keyType, valueType> |
7. Demo files
7.1 arrays.ts
Demonstration file covering arrays in TypeScript.
// EXAMPLE: two ways to declare an array
// let cityNames: string[] = [];
let cityNames: Array<string> = []; // alternative way to declare an array
// EXAMPLE: adding to an array
cityNames.push("Los Angeles, CA");
cityNames.push(50); // compiler catches type mismatch
// EXAMPLE: removing from an array - pop() return type is type | undefined
let city = cityNames.pop()
if(city == undefined) {
console.log(`city not available`);
} else {
console.log(`City name: ${city}`);
}
// EXAMPLE: compiler infers literal union for find() return type
let orlando = cityNames.find((cityName) => cityName == "Orlando, FL");
// EXAMPLE: iteration with correctly inferred types of loop variable
cityNames.forEach(cityName => {
console.log(cityName);
});
for(let cityName of cityNames) {
console.log(cityName);
}
// EXAMPLE: map correctly inferring return type
let cities: Array<string> = ["orlando", "draper", "westlake"];
let uppercaseCities = cities.map((city) => {
return city[0].toUpperCase() + city.slice(1);
});
console.log(uppercaseCities);
// EXAMPLE: filter correctly inferring return type
let temperatures: number[] = [88, 75, 95];
let lowerTemps = temperatures.filter((temperature: number) => temperature < 80);
// EXAMPLE: custom sort function with type annotations
let temps: number[] = [88, 75, 95, 100, 102, 101];
temps.toSorted()
temps.toSorted((a: number, b: number) => a - b);
// EXAMPLE: compiler catching an attempt to modify a readonly array
interface Config {
option: string;
val: string;
};
let options: Config[] = [{ option: "yes", val: "no" }, { option: "yes", val: "no" }];
function applyConfig(options: readonly Config[]) {
options.pop();
}
7.2 tuples.ts
Demonstration file covering tuples in TypeScript.
// EXAMPLE: declaring a tuple with named parameters and optional parameter
let cityData: [city: string, temperature: number, condition: string, string?];
// EXAMPLE: assigning data to a tuple
cityData = ["Orlando, FL", 85, "Cloudy"];
cityData = ["Orlando, FL", 85, "Cloudy", "182 years old this year"];
// EXAMPLE: reading from a tuple by positional index
let temp = cityData[1];
// EXAMPLE: returning from a function as a tuple, and destructing that into individual values
function gatherData(): [string, number, string] {
let city = "Orlando, FL";
let temperature = 85;
let condition = "Cloudy"
return [city, temperature, condition];
}
let [city, temperature, condition] = gatherData();
// EXAMPLE: compiler catching attempt at modifying a readonly tuple
let rTuple: readonly [string, number, string] = ["Atlanta, GA", 75, "Rainy"];
rTuple[1] = 80;
// EXAMPLE: narrowing a tuple whose values are all strings into a readonly tuple with literal values
let singleTypeTuple = ["Orlando, FL", "85 degrees", "Cloudy"] as const;
// EXAMPLE: can't iterate tuples with for...of because of ambiguous typing, use destructuring instead
let t2: [string, number, string] = ["Orlando, FL", 85, "Cloudy"];
for(let t of t2) {
console.log(t);
}
let [n, t, c] = t2;
7.3 sets.ts
Demo file covering Sets in TypeScript.
// EXAMPLE: declaring a set
let conditions: Set<string> = new Set();
// EXAMPLE: adding to a set
conditions.add("Rainy");
conditions.add("Sunny");
conditions.add(3); // compiler catches type mismatch
// EXAMPLE: declaring a readonly set
let cond: ReadonlySet<string> = new Set(["Rainy", "Sunny"]);
cond.add("Cloudy"); // compiler catches trying to modify a readonly set
// EXAMPLE: iterating through set values and the loop variable type is correctly inferred
for(let c of conditions) {
console.log(c);
}
7.4 maps.ts
Demonstration file covering Maps in TypeScript.
// EXAMPLE: declaring a map
let weatherData: Map<string, { temperature: number, condition: string }> = new Map();
// EXAMPLE: adding to a map
weatherData.set('Orlando', {"temperature": 85, "condition": "Cloudy"});
weatherData.set('New York', {"temperature": 75, "condition": "Sunny"});
weatherData.set('Draper', {"temperature": 70, "condition": "Rainy"});
weatherData.set(100, {"temperature": 100, "condition": "Sunny"}); // compiler catches type mismatch in key
weatherData.set('Los Angeles', {"temperature": "100", "condition": "Sunny"}); // compiler catches type mismatch in value
// EXAMPLE: getting a value from a key - get() return type is type | undefined
let city = weatherData.get('New York');
if(city == undefined) {
console.log(`City not found`);
} else {
console.log(city.condition, city.temperature);
}
// EXAMPLE: declaring a readonly map
let wd: ReadonlyMap<string, {temperature: number, condition: string}> = new Map([
["Orlando", { "temperature": 85, "condition": "Cloudy" }],
["New York", { "temperature": 75, "condition": "Sunny" }],
["Draper", { "temperature": 70, "condition": "Rainy" }]
]);
wd.set('Los Angeles', {}) // compiler catches trying to modify a readonly map
Search Terms
typescript · foundations · arrays · collections · react · frontend · development · type · methods · tuples · array · declaration · iteration · sets · values · definition · javascript · maps · return · access · gotcha · inference · readonly · tuple