Arrays, records and tuples
Arrays
We can define arrays in TypeScript using the following equivalant syntax:
// Option 1: Bracket syntax
const numbers: number[] = [1, 2, 3, 4, 5];
const stringAndNumbers: (string | number)[] = ['Alice', 25, 'Bob', 30];
// Option 2: Array syntax
const numbers: Array<number> = [1, 2, 3, 4, 5];
const stringAndNumbers: Array<string | number> = ['Alice', 25, 'Bob', 30];
In the above example, numbers
is an array of numbers, and stringAndNumbers
is an array of strings and numbers.
Records
Records represent a key-to-value mapping in JavaScript. Keys can a combination of strings, numbers and symbols. They have a key of a specific type, and a value of a specific type.
type User = {
name: string;
};
// Syntax: Record<key, value>
// key can be either a number or a string
const numberIdToUserMapping: Record<number, User> = {
1: { name: 'Alice' },
2: { name: 'Bob' },
3: { name: 'Charlie' },
};
const stringIdToUserMapping: Record<string, User> = {
a4fd43: { name: 'Alice' },
bcf94e: { name: 'Bob' },
c3f4e2: { name: 'Charlie' },
};
Example: Variants using Records
Records and string unions can form the following powerful construct:
import { CSSProperties } from 'react';
type ThemeMode = "dark" | "light";
type Styles = CSSProperties;
// 1. Create a mapping from ThemeMode to Styles
const themeStyles: Record<ThemeMode, Styles> = {
dark: {
backgroundColor: 'black',
color: 'white',
},
light: {
backgroundColor: 'white',
color: 'black',
},
};
// 2. Use the mapping
const currentTheme: ThemeMode = 'dark';
const currentStyles = themeStyles[currentTheme]; // ok
// Throws an error if the key is invalid
console.log(themeStyles['invalid']); // Error: Property 'invalid' does not exist on type 'Record<ThemeMode, Styles>'
Fun fact: Arrays are records
A fun fact is that an array is also a record. Because they are a key-to-value mapping where the key is a number, and the value is the element at that index.
const numbers: Record<number, number> = [1, 2, 3, 4, 5];
// ^ The above is valid
// However, you should not do this because now these are invalid:
numbers.length; // Error: Property 'length' does not exist on type 'Record<number, number>'
numbers.find(n => n === 2); // Error: Property 'find' does not exist on type 'Record<number, number>'
Tuples
Tuples are arrays with a fixed number of elements, and each element can have a different type. They are defined using the following syntax:
const tuple: [string, number] = ['Alice', 25];
You can get TypeScript to infer the type of a tuple by using the as const
syntax:
const tuple = ['Alice', 25] as const;
// ^ tuple: readonly ["Alice", 25]
const tupleNoConst = ['Alice', 25];
// ^ tupleNoConst: (string | number)[]
Returning multiple values
Tuples can be used to return multiple values from a function:
function useState(): [number, (value: number) => void] {
let state = 0;
return [state, (value: number) => state = value];
}
const [state, setState] = useState();
However, if you have more than 2-3 values, it is recommended to use an object instead of a tuple.
This is as accessing tuples rely on the order of the elements, which can be confusing and error-prone.
// GOOD, NOT CONFUSING
function getUser(): { name: string, age: number, email: string } {
return { name: 'Alice', age: 25, email: 'abc@email.com' };
}
const { name, age, email } = getUser();
// BAD, CONFUSING
function getUser(): [string, number, string] {
return ['Alice', 25, 'abc@email.com'];
}
const [name, age, email] = getUser(); // Correct usage
const [email, age, name] = getUser(); // BAD: WRONG ORDER BUT NO ERROR