Typed Arrays in TypeScript
A typed array ensures every element in an array has the same type. Learn the two syntaxes for array types, how they prevent mixed-element bugs, and when to use each form.
TypeScript typed arrays are arrays where every element must match a declared type. Instead of saying "this is an array of anything," you tell TypeScript "this array holds strings" or "this array holds numbers." The compiler then checks every read and write against that guarantee.
In plain JavaScript, an array can hold a string, a number, and an object all at once. That flexibility is powerful but error-prone.
You might call a string method on what you thought was a string, only to discover the element was actually a number.
Typed arrays give you array flexibility with the safety of static type checking.
Writing an array type
TypeScript gives you two equivalent ways to annotate an array. The bracket syntax is shorter and appears more often in everyday code, while the generic syntax uses the built-in Array type from the standard library.
Both forms produce identical types, so you can pick whichever style your team prefers.
The bracket form puts the element type followed by square brackets. Here is an array of numbers and an array of strings declared with the bracket syntax:
let scores: number[] = [95, 87, 91];
let names: string[] = ["Ada", "Linus", "Grace"];The generic form uses angle brackets with the Array type name. Both declarations below produce the exact same type as the bracket versions above:
let scores: Array<number> = [95, 87, 91];
let names: Array<string> = ["Ada", "Linus", "Grace"];Pick one style and stay consistent across your codebase. The bracket form tends to be more common in open-source TypeScript projects because it is shorter to type and read.
Once you annotate an array, the compiler blocks any element that does not match. This is the core benefit: you find out about type mismatches in your editor, not in production logs. Here is what happens when you try to push a number into a string array:
let names: string[] = ["Ada", "Grace"];
names.push(42);The compiler rejects this call and tells you exactly what went wrong and where. The error message pinpoints the line, the argument, and the type mismatch.
Argument of type 'number' is not assignable to parameter of type 'string'.TypeScript is telling you that a number cannot go into an array that expects strings. If you meant to store numbers, you would have declared the array with the number type instead.
How TypeScript infers array types
You do not always need an explicit annotation. When you initialise an array with values, TypeScript infers the element type from those values automatically. This is the same inference that works for variables and function returns throughout the language.
let items = ["red", "green", "blue"];Hovering over items in your editor shows that TypeScript inferred this as a string array. The compiler now knows every element must be a string, and adding a number with the push method would be blocked.
Inference works well when the initial values clearly communicate the intended type. However, empty arrays need special attention. When you declare an empty array with no annotation, TypeScript has nothing to infer from, so it falls back to a loose type that disables all checking and lets anything in.
Always annotate an empty array with the intended element type so the compiler can protect you from the start:
let data: string[] = [];
data.push("hello");
data.push(42);After the explicit annotation, pushing a number into this string array is a compiler error. The annotation tells TypeScript your intent before any values exist.
How array methods respect the element type
Every array method that returns an element or accepts new elements uses the array's type parameter automatically. This means the standard methods you already use -- finding, mapping, filtering -- all stay type-safe without extra work.
let prices: number[] = [12.99, 24.50, 8.75];
let first = prices[0];
let found = prices.find(p => p > 20);
let doubled = prices.map(p => p * 2);TypeScript knows that first is a number because the array element type is number. The find result is number or undefined because the element might not exist at all. The map result is still a number array because multiplying a number by two produces a number.
The undefined possibility with find is important. TypeScript forces you to handle the case where the element is not found, which is exactly the kind of edge case that causes runtime errors in plain JavaScript.
let found = prices.find(p => p > 100);
if (found !== undefined) {
console.log(found.toFixed(2));
}Without the undefined check, calling the toFixed method directly would be a compiler error. The type system makes these edge cases explicit instead of letting them silently fail at runtime.
Arrays with union element types
Sometimes an array genuinely needs to hold more than one type. Use a union as the element type by wrapping the union in parentheses before the brackets.
let mixed: (string | number)[] = ["hello", 42, "world", 99];
mixed.push("more");
mixed.push(100);
mixed.push(true);The first two push calls succeed because they match the string or number union. The third call with a boolean produces a compiler error because boolean is not part of the declared union.
When you read from a union array, each element has the union type. You must narrow before using methods that only exist on one member. This is the same narrowing pattern used with union types everywhere in TypeScript.
let items: (string | number)[] = ["alpha", 1, "beta"];
for (let item of items) {
if (typeof item === "string") {
console.log(item.toUpperCase());
} else {
console.log(item.toFixed(2));
}
}The typeof check narrows the element inside each branch. In the string branch, you can use string methods like toUpperCase. In the else branch, TypeScript knows only number remains, so number methods are available.
For a deeper explanation of this pattern, see type narrowing with typeof in TypeScript.
Common mistakes
Beginners often forget to annotate an empty array. Without an explicit type, the array becomes a catch-all that disables checking and lets any value through. Always add the element type when the array starts empty.
Another common issue is accessing an index that may not exist. Even a typed array can be empty or shorter than expected, and indexing with a number does not include undefined in the result type by default.
Enable the noUncheckedIndexedAccess compiler option to make index access include undefined, or use array methods that handle missing elements safely, such as at() or find().
For arrays that should never be mutated after creation, consider marking them as readonly to block methods like push, pop, and sort. For more on that topic, see readonly arrays in TypeScript.
Rune AI
Key Insights
- Annotate an array with Type[] or Array<Type> -- both mean the same thing.
- TypeScript infers array types from the initial value, so explicit types are often optional.
- Array methods like push, pop, find, and map all respect the element type.
- Use a union type like (string | number)[] when an array genuinely needs mixed elements.
- Narrow elements from a union array before calling methods that only exist on one member type.
Frequently Asked Questions
What is the difference between Type[] and Array<Type>?
Can I create a typed array with mixed types?
Are array types checked at runtime?
Conclusion
Typed arrays make array-heavy code safer by ensuring every element matches the declared type. Use the bracket or generic syntax, rely on inference when the initial value is clear, and narrow union element types before using member-specific methods. The type system catches mixed-element bugs at compile time so they never reach runtime.
More in this topic
TypeScript Compiler Explained
The TypeScript compiler (tsc) turns .ts files into .js files while checking types. Learn what it does, how to use it, and how it fits your workflow.
Set Up ESLint for TypeScript
Install and configure ESLint with typescript-eslint to catch bugs and enforce consistent code style in your TypeScript project.
Pick Utility Type in TypeScript
Pick creates a new type by selecting only the properties you name from an existing type. Use it to build focused types for specific views, API responses, or function parameters.