Retrieving Single Elements
This lesson shows how to get one element out of an array: by its position, by a condition, or at random. It also shows how to check whether an element exists at all.
TypeScript has two values that mean "no value": null and undefined. When a lookup can fail, the answer is undefined. There is no second flag next to the value: a missing element is undefined.
Reading a position that does not exist, such as words[5] in an array of 5 elements, does not stop the program. You get undefined. The type string | undefined is there to remind you of that case.
To turn that undefined into a value of your own, use the ?? operator. It takes the right side only when the left side is null or undefined.
Arrays carry most of these tools as methods: at(), find(), findLast(), includes() and every(). For the rest you write a small helper once.
Retrieve by Position
Every element in an array has a position called an index. The first index is 0, so the last one is words.length - 1.
To read an element by position, write the index inside square brackets: words[3]. For the last element there is also words.at(-1), which counts back from the end.
An index that does not exist gives undefined instead of an error. So the honest type of words[i] is string | undefined: maybe a string is there, maybe nothing is.
There is no elementAtOrNull() and no elementAtOrElse() method. You do not need them: the missing element already is undefined, and ?? puts your own value in its place.
Element at a position
words[3] reads the element at index 3. Since counting starts at 0, that is the fourth word, so the program prints four.
Output: TypeScript file and captured output
const words = ["one", "two", "three", "four", "five"]
// Positions start at 0, so index 3 is the fourth element.
console.log(words[3])
First and last element
The first element is words[0]. The last one is words.at(-1): at() accepts a negative index and counts back from the end, so -1 is the last element. The program prints one and then five.
Output: TypeScript file and captured output
const words = ["one", "two", "three", "four", "five"]
// The first element is at index 0. at(-1) counts back from the end.
console.log(words[0])
console.log(words.at(-1))
Element at a position, or undefined
elementAt() only reads items[i] and says so in its return type: T | undefined. Index 1 is in range, so the first call prints two. Index 5 is out of range, so the second call prints undefined — no crash, and no second true or false next to the value.
Output: TypeScript file and captured output
// Reading an index gives the element or undefined — there is no second "ok" value.
function elementAt<T>(items: T[], i: number): T | undefined {
return items[i]
}
const words = ["one", "two", "three", "four", "five"]
console.log(elementAt(words, 1))
// Index 5 does not exist: no crash, just undefined.
console.log(elementAt(words, 5))
Element at a position, or a fallback
words[5] is undefined, so ?? uses the right side and calls fallback(5). The function receives the index, so it can build the message The value for index 5 is undefined.
Output: TypeScript file and captured output
const words = ["one", "two", "three", "four", "five"]
const fallback = (i: number): string => `The value for index ${i} is undefined`
// ?? uses the right side only when the left side is null or undefined.
console.log(words[5] ?? fallback(5))
Retrieve by Condition
Often you do not know the position. You know a condition instead: the first word longer than 3 letters, the last word that starts with "f". The condition is a function that takes one element and returns true or false.
For the first match, arrays have find(). It walks from the start and returns the first element that passes the test, or undefined when nothing does.
findLast() does the same walk in the other direction, from the last index down to 0. The first element it meets is the last match in the array.
Both methods stop as soon as they find a match, so the rest of the array is never tested. And because "nothing matched" is simply undefined, you test the result with !== undefined, or give it a default with ??.
First match
find() returns the first word with s.length > 3. "one" and "two" are too short, so the program prints three.
Output: TypeScript file and captured output
const words = ["one", "two", "three", "four", "five"]
// find() returns the first element that passes the test.
console.log(words.find(s => s.length > 3))
Last match
findLast() starts at the last index and moves down. The first word it meets that starts with "f" is "five", so it stops there and the program prints five.
Output: TypeScript file and captured output
const words = ["one", "two", "three", "four", "five"]
// findLast() walks from the end, so the first hit is the last match.
console.log(words.findLast(s => s.startsWith("f")))
First match, or undefined
No word is longer than 6 letters, so find() returns undefined and ?? "not found" makes the program print not found. In the numbers array the first even number is 2; the !== undefined check is how the program knows something was found.
Output: TypeScript file and captured output
const words = ["one", "two", "three", "four", "five"]
const numbers = [1, 2, 3, 4]
// Nothing matches, so find() returns undefined and ?? supplies the message.
const long: string | undefined = words.find(s => s.length > 6)
console.log(long ?? "not found")
// "Found" is simply "not undefined".
const firstEven: number | undefined = numbers.find(n => n % 2 === 0)
if (firstEven !== undefined) {
console.log(firstEven)
}
Last match, or undefined
findLast() walks the numbers backwards, so the first even number it meets is the last one in the array: 4, at index 3. It is found right away.
Output: TypeScript file and captured output
const numbers = [1, 2, 3, 4]
// findLast() walks backwards and returns undefined when nothing matches.
const lastEven: number | undefined = numbers.findLast(n => n % 2 === 0)
if (lastEven !== undefined) {
console.log(lastEven)
}
Retrieve the First Present Value
Sometimes an array has holes: a few elements are null or undefined, and you want the first real value.
find() does the walking again. The condition is v !== null && v !== undefined.
Written straight into the arrow function, the result would still have type number | string | null | undefined: the test runs at run time, but the type does not change. Move the test into a named function whose return type is v is number | string — such a function is called a type guard — and the result type loses null and undefined on its own.
The array in the example holds a number, a string and null at the same time, so its type is a union of all of them.
First value that is not null or undefined
The first element is null, so it is skipped. The next one is 0, which is neither null nor undefined, so find() returns it and stops — "true" is never tested. Note that 0 is kept: the test asks about null and undefined, not about being true or false.
Output: TypeScript file and captured output
// null and undefined both mean "no value". Every other value counts.
const list: (number | string | null | undefined)[] = [null, 0, "true"]
// Without "v is number | string" the result would keep null and undefined in its type.
const isPresent = (v: number | string | null | undefined): v is number | string =>
v !== null && v !== undefined
const first: number | string | undefined = list.find(isPresent)
console.log(first)
Random Element
To pick a random element, pick a random index. A valid index is a whole number from 0 up to numbers.length - 1.
Arrays have no random() method, so the index goes into square brackets as usual.
Math.random() gives a different number on every run. That is what you want in a real program, but it would make this lesson print something else every time. So the example seeds its own tiny generator with the fixed number 42. The same seed always produces the same index, which makes the result reproducible. Use Math.random() when you do want a different element on each run.
Pick a random element
With seed 42 the generator picks index 1, so the program prints 2. Change the seed and a different number can come out.
Output: TypeScript file and captured output
const numbers = [1, 2, 3, 4]
const MOD = 4294967296
let seed = 42
// A tiny generator of our own: the same seed always gives the same index.
function nextIndex(n: number): number {
seed = (seed * 1664525 + 1013904223) % MOD
return Math.floor((seed / MOD) * n)
}
const i = nextIndex(numbers.length)
console.log(numbers[i])
Check Element Existence
To check whether a value is in an array, use includes(). It returns true or false.
There is no containsAll() method, but every() gives you one in a single line: wanted.every(v => words.includes(v)) is true only when every wanted value is found. every() stops at the first value that is missing.
There is no isEmpty() either. Compare the length: words.length === 0 means empty, words.length > 0 means not empty.
Contains one value
"four" is in the array, so the first check prints true. "zero" is not, so the second prints false.
Output: TypeScript file and captured output
const words = ["one", "two", "three", "four", "five"]
console.log(words.includes("four"))
console.log(words.includes("zero"))
Contains all values
Both "four" and "one" are in the array, so containsAll() returns true. "zero" is missing, so the second call returns false.
Output: TypeScript file and captured output
// every() is true only when the test passes for every wanted value.
function containsAll<T>(items: T[], wanted: T[]): boolean {
return wanted.every(v => items.includes(v))
}
const words = ["one", "two", "three", "four", "five"]
console.log(containsAll(words, ["four", "one"]))
console.log(containsAll(words, ["one", "zero"]))
Empty or not empty
empty has length 0, so empty.length === 0 is true. words has 5 elements, so words.length > 0 is also true.
Output: TypeScript file and captured output
const words = ["one", "two", "three", "four", "five"]
const empty: string[] = []
// An array is empty when its length is 0.
console.log(empty.length === 0)
console.log(words.length > 0)