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: Point
coordinates: 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): void
logArrayElements<function (type parameter) T in logArrayElements<T extends any[]>(elements: T): void
T extends any[]>(elements: T extends any[]
elements: function (type parameter) T in logArrayElements<T extends any[]>(elements: T): void
T) {
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.forEach(element: any
element => 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
```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.log(element: any
element));
}
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: string
key: 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: number
a: 1, b: number
b: 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) Rest
Rest] ? function (type parameter) Rest
Rest : [];
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.