Introduction: Why One Type is Boring?
\n\nImagine: you're writing in TypeScript and suddenly encounter a situation where an array needs to store data of different types. For example, a list where each element is either a string or a number. Or a tuple of a name (string) and age (number).
Many beginners (and not only) ask the question: "How to declare an array with multiple types in TypeScript?" This question has thousands of views on Stack Overflow — and for good reason. TypeScript is strict with types, but it provides flexible tools for such cases.
\n\nIn this article, we will cover all the ways to work with arrays of mixed types: from simple union types to tuples and generics. You will learn when to use which approach and how to avoid the any trap.
1. Union Types: The Simplest Way
\n\nThe most obvious option is to use a union type. This is when you say: "The array can contain elements of type string OR number".
// Array that can contain strings or numbers\nconst mixedArray: (string | number)[] = ['hello', 42, 'world', 100];\n\n// Alternative notation (equivalent)\nconst mixedArray2: Array<string | number> = ['hello', 42, 'world', 100];\n\nHere (string | number)[] means: "an array where each element is a string or a number". TypeScript will check that you don't try to add, say, a boolean value or an object.
When is this convenient?
\n- \n
- When the order of elements doesn't matter, only the set of allowed types matters. \n
- When you are working with an API that returns an array of heterogeneous data. \n
- When you store a list of IDs (numbers) and names (strings) in one array. \n
The Pitfall
\nWhen using union types, you cannot guarantee that the first position will be a string and the second a number. The typing only says: "the array contains strings and numbers, but in what order is unknown".
\n\nconst arr: (string | number)[] = [1, 'hello'];\n// TypeScript doesn't know: arr[0] — is it number or string?\nconst first = arr[0]; // type: string | number\n// To use it as a number, you need to narrow the type:\nif (typeof first === 'number') {\n console.log(first.toFixed(2));\n}\n\n2. Tuples: When Order Matters
\n\nIf you need the array to have a fixed structure — for example, first a string, second a number, third a boolean — use tuples (tuple types).
\n\n// Tuple: [string, number, boolean]\nconst person: [string, number, boolean] = ['Alice', 30, true];\n\n// Tuple with labels — more readable\nconst product: [name: string, price: number, inStock: boolean] = ['Laptop', 1200, true];\n\nTuples strictly fix the number of elements and the type at each position. If you try to assign a value of the wrong type or add an extra element, TypeScript will throw an error.
\n\nRest Elements in Tuples
\nSince TypeScript 4.0, it is possible to specify rest elements in tuples. This is convenient when part of the structure is fixed and the rest is variable:
\n\n// First element is a string, the rest are numbers\nconst data: [string, ...number[]] = ['scores', 95, 87, 92];\n\n// Can be combined\nconst logEntry: [timestamp: Date, ...messages: string[]] = [new Date(), 'info', 'user logged in'];\n\n3. Type Alias for Complex Arrays
\n\nWhen the array structure becomes complex, it is convenient to extract the type into a separate alias. This improves code readability and allows you to reuse the type.
\n\ntype UserEntry = [id: number, name: string, email: string, isActive: boolean];\n\nconst users: UserE