TypedArray in TypeScript
This article explains TypedArray in TypeScript.
We will explain TypedArray in TypeScript, including practical examples.
YouTube Video
TypedArray in TypeScript
TypedArray is a mechanism for efficiently handling binary data. It is especially useful for low-level binary operations such as large image data, network byte streams, and numeric arrays for WebGL.
How to create an ArrayBuffer
ArrayBuffer represents a fixed-length region of bytes. First, create a buffer and check its size and byte length.
1// Create an ArrayBuffer of 16 bytes
2const buffer: ArrayBuffer = new ArrayBuffer(16);
3console.log("buffer.byteLength:", buffer.byteLength); // 16
- This code creates an empty region of 16 bytes.
ArrayBufferitself does not have read/write functions, so you access it throughTypedArrayorDataView.
Types of TypedArray
There are many types of TypedArray, such as the following. These vary depending on the data type and size they handle.
| TypedArray | Data Type | Bit Size |
|---|---|---|
Int8Array |
8-bit integer | 8 bits |
Uint8Array |
Unsigned 8-bit integer | 8 bits |
Uint8ClampedArray |
Clamped unsigned 8-bit integer | 8 bits |
Int16Array |
16-bit integer | 16 bits |
Uint16Array |
Unsigned 16-bit integer | 16 bits |
Int32Array |
32-bit integer | 32 bits |
Uint32Array |
Unsigned 32-bit integer | 32 bits |
Float32Array |
32-bit floating-point number | 32 bits |
Float64Array |
64-bit floating-point number | 64 bits |
Basic TypedArrays (Uint8Array, Int16Array, Float32Array, etc.)
TypedArray creates a 'typed view' on top of an ArrayBuffer. Below are examples of creating and using some typical TypedArrays.
1// Create a buffer and different typed views over it
2const buf = new ArrayBuffer(8); // 8 bytes
3
4// Create views
5const u8 = new Uint8Array(buf); // 8 x uint8
6const i16 = new Int16Array(buf); // 4 x int16 (since each int16 is 2 bytes)
7const f32 = new Float32Array(buf); // 2 x float32 (4 bytes each)
8
9console.log("Uint8 length:", u8.length);
10console.log("Int16 length:", i16.length);
11console.log("Float32 length:", f32.length);- By creating multiple views on the same
ArrayBuffer, you can read and write the same memory in different types or granularities.lengthof the view is the number of elements, andbyteLengthis the number of bytes.
Writing and reading (byte-level operations)
When you write a value to a TypedArray, the corresponding bytes in memory are updated. You can see the changes when you read the same buffer with a different view.
1// Demonstrate writing via one view and reading via another
2const buffer2 = new ArrayBuffer(4);
3const u8view = new Uint8Array(buffer2);
4const u32view = new Uint32Array(buffer2);
5
6u8view[0] = 0x78;
7u8view[1] = 0x56;
8u8view[2] = 0x34;
9u8view[3] = 0x12;
10
11console.log("Uint8 bytes:", Array.from(u8view)); // [120, 86, 52, 18]
12console.log("Uint32 value (platform endianness):", u32view[0]); // value depends on endianness
- In this example, we write a byte sequence and then read the same area as a 32-bit integer. Note that the output depends on the endianness of the execution environment.
Endianness (byte order) and DataView
DataView is useful if you want to control environment-dependent endianness issues. DataView lets you specify endianness when reading and writing.
1// Use DataView to read/write with explicit endianness control
2const b = new ArrayBuffer(4);
3const dv = new DataView(b);
4
5// write little-endian 32-bit integer
6dv.setUint32(0, 0x12345678, true);
7
8// read as little-endian and big-endian
9const little = dv.getUint32(0, true);
10const big = dv.getUint32(0, false);
11
12console.log("little-endian read:", little.toString(16)); // "12345678"
13console.log("big-endian read:", big.toString(16)); // different value
DataViewis a convenient mechanism for reading and writing bytes in detail. It supports a variety of types, such as signed or unsigned integers and floating-point numbers, and allows you to explicitly specify endianness (byte order), which is very useful when implementing binary protocols.
Difference between subarray and slice
subarray of a TypedArray returns a view sharing the original buffer, while slice returns a new copy. Performance and side effects differ depending on which you use.
1const base = new Uint8Array([1, 2, 3, 4, 5]);
2
3const shared = base.subarray(1, 4); // shares underlying buffer
4const copied = base.slice(1, 4); // copies data
5
6shared[0] = 99;
7console.log("base after shared modification:", base); // shows change
8console.log("copied remains:", copied); // unaffected
- If you modify the shared view, the original array will also change, which may cause unintended side effects. If you want to safely preserve the original array, you can create a copy in advance using
slice().
Copying buffers and type conversion (conversion between TypedArrays)
We will explain how to copy data between TypedArrays and how to paste parts using the set method.
1// Copy and convert between typed arrays
2const src = new Float32Array([1.5, 2.5, 3.5]);
3const dst = new Uint16Array(src.length);
4
5// Convert by mapping (explicit conversion)
6for (let i = 0; i < src.length; i++) {
7 dst[i] = Math.round(src[i]); // simple conversion rule
8}
9console.log("converted dst:", Array.from(dst)); // [2, 2, 4]
10
11// Using set to copy bytes (requires compatible underlying buffer or same element type)
12const src2 = new Uint8Array([10, 20, 30]);
13const dst2 = new Uint8Array(6);
14dst2.set(src2, 2); // copy src2 into dst2 starting at index 2
15console.log("dst2 after set:", Array.from(dst2)); // [0,0,10,20,30,0]
- If the element types differ, you need to convert values, such as rounding or range checking, rather than simply copying.
setallows fast copying betweenTypedArrays of the same element type.
Practical example: A binary protocol parser (simple implementation)
Here, we introduce a simple parser example which reads a fixed-format binary data consisting of a 1-byte type, 2-byte data length, and a following payload.
1// Simple binary message parser:
2// format: [type: uint8][length: uint16 BE][payload: length bytes]
3function parseMessages(buffer: ArrayBuffer) {
4 const dv = new DataView(buffer);
5 let offset = 0;
6 const messages: { type: number; payload: Uint8Array }[] = [];
7
8 while (offset + 3 <= dv.byteLength) {
9 const type = dv.getUint8(offset);
10 const length = dv.getUint16(offset + 1, false); // big-endian
11 offset += 3;
12 if (offset + length > dv.byteLength) break; // truncated
13 const payload = new Uint8Array(buffer, offset, length);
14 messages.push({ type, payload });
15 offset += length;
16 }
17
18 return messages;
19}
20
21// Example usage
22const buf = new ArrayBuffer(1 + 2 + 3 + 1 + 2 + 2); // two messages
23const dv = new DataView(buf);
24let off = 0;
25// first message: type=1, length=3, payload=[1,2,3]
26dv.setUint8(off, 1); dv.setUint16(off + 1, 3, false); off += 3;
27new Uint8Array(buf, off, 3).set([1, 2, 3]); off += 3;
28// second message: type=2, length=2, payload=[9,8]
29dv.setUint8(off, 2); dv.setUint16(off + 1, 2, false); off += 3;
30new Uint8Array(buf, off, 2).set([9, 8]);
31
32console.log(parseMessages(buf));- In this example, the header is read using
DataView, and the payload slice is created withUint8Array. Endianness and buffer length checking are important.
Web API and TypedArray (Example: fetching binary data)
This is a typical example of handling an ArrayBuffer obtained from a network request using TypedArray.
1// Example of fetching binary and mapping to typed array
2async function fetchBinary(url: string) {
3 const res = await fetch(url);
4 const ab = await res.arrayBuffer();
5 const view = new Uint8Array(ab);
6 // process view...
7 console.log("received bytes:", view.length);
8 return view;
9}
10
11// Usage (call in async context)
12// fetchBinary("/path/to/file.bin");
- You can directly pass the
ArrayBufferobtained withResponse.arrayBuffer()to aTypedArray. It is used for images, audio, or custom binary protocols.
Performance tips and common pitfalls
Here are some 'performance tips' and 'common pitfalls' that are helpful to know when using TypedArrays:.
-
Avoid unnecessary copying To process large data efficiently, you can reduce unnecessary copying by creating partial views with
subarrayor sharing the sameArrayBufferacross multiple views. -
Be careful with endianness For network communication or file formats, the ordering of data (byte order) is often specified. Using
DataViewallows you to specify the endianness explicitly when reading and writing, preventing unintended misinterpretation. -
Be aware of value ranges for each type For example,
Uint8can only represent values from 0 to 255. If you input a negative value, truncation or value rollover may occur, so you should define conversion rules as needed. -
Consider stress on garbage collection Frequently recreating large
ArrayBuffers increases the burden of memory management. In performance-critical situations, you may consider designing the code to reuse existing buffers as much as possible.
Summary
TypedArray is a mechanism for handling binary data quickly and efficiently. By combining ArrayBuffer, which reserves a fixed-length byte area, with TypedArray or DataView, which read and write the contents using specific types, you can perform flexible binary operations. Depending on your use case, you can choose between using subarray/slice or DataView, and design your implementation with attention to endianness and copies.
You can follow along with the above article using Visual Studio Code on our YouTube channel. Please also check out the YouTube channel.