Skip to content

Exploring the Power of Square Brackets in TypeScript

Published: at 

Introduction

TypeScript, the popular statically-typed superset of JavaScript, offers an array of advanced type manipulation features, enhancing the development experience with strong typing. One of the intriguing aspects of TypeScript is its use of square brackets [] in various contexts. This post will explore the different ways square brackets are used in TypeScript, from array types to indexed access types and beyond.

1. Defining Array Types

At the most basic level, square brackets in TypeScript are used to define array types.

let let numbers: number[]numbers: number[] = [1, 2, 3];
let let strings: string[]strings: interface Array<T>Array<string> = ["hello", "world"];

This syntax specifies that numbers is an array of numbers, and strings is an array of strings.

2. Tuple Types

TypeScript also uses square brackets to define tuples - arrays with fixed lengths and known types at specific indices.

type type Point = [number, number]Point = [number, number];
let let coordinates: Pointcoordinates: type Point = [number, number]Point = [12.34, 56.78];

In this example, Point is a tuple type representing a 2D coordinate.

3. The length Property

Every array in TypeScript has a length property, inferred by the type system.

type type LengthArr<T extends Array<any>> = T["length"]LengthArr<function (type parameter) T in type LengthArr<T extends Array<any>>T extends interface Array<T>Array<any>> = function (type parameter) T in type LengthArr<T extends Array<any>>T["length"];

type 
type foo = 2
foo
= type LengthArr<T extends Array<any>> = T["length"]LengthArr<["1", "2"]>;

TypeScript understands that length is a number, reflecting the array’s size.

4. Indexed Access Types

TypeScript allows accessing the type of a specific index or property using square brackets.

type type Point = [number, number]Point = [number, number];
type 
type FirstElement = number
FirstElement
= type Point = [number, number]Point[0];

Here, FirstElement is the type of the first element in the Point tuple, which is number.

5. Creating Union Types from Tuples

The [] syntax is instrumental in creating union types from tuples.

type type Statuses = ["active", "inactive", "pending"]Statuses = ["active", "inactive", "pending"];
type 
type CurrentStatus = "active" | "inactive" | "pending"
CurrentStatus
= type Statuses = ["active", "inactive", "pending"]Statuses[number];

Using Statuses[number], we create a union type of all tuple elements.

6. Generic Array Types and Constraints

Square brackets are also used to define generic constraints and types.

function function logArrayElements<T extends any[]>(elements: T): voidlogArrayElements<function (type parameter) T in logArrayElements<T extends any[]>(elements: T): voidT extends any[]>(elements: T extends any[]elements: function (type parameter) T in logArrayElements<T extends any[]>(elements: T): voidT) {
  elements: T extends any[]elements.Array<any>.forEach(callbackfn: (value: any, index: number, array: any[]) => void, thisArg?: any): void
Performs the specified action for each element in an array.
@paramcallbackfn A function that accepts up to three arguments. forEach calls the callbackfn function one time for each element in the array.@paramthisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
forEach
(element: anyelement => var console: Console
The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstderr). The global `console` can be used without calling `require('console')`. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v22.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```
@see[source](https://github.com/nodejs/node/blob/v22.x/lib/console.js)
console
.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args) for more information.
@sincev0.1.100
log
(element: anyelement));
}

In this function, T is constrained to be an array type.

7. Mapped Types with Index Signatures

TypeScript’s mapped types can use square brackets to define index signatures, creating dynamic property types.

type 
type StringMap<T> = {
    [key: string]: T;
}
StringMap
<function (type parameter) T in type StringMap<T>T> = { [key: stringkey: string]: function (type parameter) T in type StringMap<T>T };
let let map: StringMap<number>map:
type StringMap<T> = {
    [key: string]: T;
}
StringMap
<number> = { a: numbera: 1, b: numberb: 2 };

StringMap is a type with string keys and T typed values.

8. Advanced Tuple Manipulation

The square brackets enable advanced tuple manipulation like extracting or omitting elements.

type type WithoutFirst<T extends any[]> = T extends [any, ...infer Rest] ? Rest : []WithoutFirst<function (type parameter) T in type WithoutFirst<T extends any[]>T extends any[]> = function (type parameter) T in type WithoutFirst<T extends any[]>T extends [any, ...infer function (type parameter) RestRest] ? function (type parameter) RestRest : [];
type 
type Tail = [2, 3]
Tail
= type WithoutFirst<T extends any[]> = T extends [any, ...infer Rest] ? Rest : []WithoutFirst<[1, 2, 3]>;

Here, WithoutFirst removes the first element from a tuple.

Conclusion

The versatility of square brackets in TypeScript, ranging from defining array and tuple types to advanced type manipulations, showcases TypeScript’s power and flexibility. These features significantly contribute to TypeScript’s ability to enforce strong typing, making code more reliable and maintainable. As TypeScript continues to evolve, its sophisticated type system remains a key reason for its growing popularity among developers.

For those interested in diving deeper and honing their TypeScript skills, the TypeScript Handbook is an excellent resource for exploring these and other features in more detail. Additionally, online platforms like TypeHero provide interactive learning experiences and challenges that can help solidify your understanding of TypeScript and its various techniques, including the use of square brackets for advanced type manipulations. Utilising these resources can greatly enhance your TypeScript proficiency and open up new possibilities in your programming endeavours.