TypeScript Unions in Depth
The position of a union changes what the type allows. Parentheses and keyof build different types from the same union members.
A mixed array inside parentheses
The parentheses group the union before the array brackets apply. The array holds strings and numbers side by side.
const values: (string | number)[] = ["pen", 3, "book", 7];
console.log(values.length);
console.log(values[0]);
console.log(values[1]);A union of two whole arrays
Without parentheses, the union sits between two complete array types. A mixed array fits neither side, so the checker refuses it.
type Separate = string[] | number[];
const values: Separate = ["pen", 3, "book"];
console.log(values);keyof keeps only shared keys
keyof reads the keys of a type. On a union it keeps the keys shared by every member, and both shared keys are accepted here.
type UserRec = { id: number; userName: string; email: string };
type OrderRec = { id: number; total: number; email: string };
type SharedKey = keyof (UserRec | OrderRec);
const keyOne: SharedKey = "id";
const keyTwo: SharedKey = "email";
console.log(keyOne);
console.log(keyTwo);A unique key is refused
The key userName exists on one member only. It drops out of the shared key union, and the assignment fails.
type UserRec = { id: number; userName: string; email: string };
type OrderRec = { id: number; total: number; email: string };
type SharedKey = keyof (UserRec | OrderRec);
const key: SharedKey = "userName";
console.log(key);Shapes that share a member
Both shapes carry a title member with the same type. Code reads title on the union without any narrowing check.
type BookItem = { title: string; pages: number };
type FilmItem = { title: string; minutes: number };
type CatalogItem = BookItem | FilmItem;
const items: CatalogItem[] = [
{ title: "Dune", pages: 412 },
{ title: "Arrival", minutes: 116 },
];
for (const item of items) {
console.log(item.title);
}Example
type ArticleRec = { id: number; heading: string; email: string };
type VideoRec = { id: number; duration: number; email: string };
type SharedKey = keyof (ArticleRec | VideoRec);
const fields: (string | number)[] = ["views", 120, "likes", 45];
const keyOne: SharedKey = "id";
const keyTwo: SharedKey = "email";
console.log(fields.length);
console.log(keyOne);
console.log(keyTwo);Key points
- The type (string | number)[] describes one array with mixed string and number members.
- The type string[] | number[] describes a union of two whole arrays.
- A mixed array fits the parenthesised type and fails the union of arrays.
- keyof applied to a union keeps only the keys that every member has.
- A member that belongs to every shape in a union is safe to read directly.
In this example, one array mixes strings and numbers under a parenthesised union, and keyof keeps id and email because both records share those keys.
Note
Placement decides the type, so parentheses belong in the type wherever mixed members are intended.
Try it Yourself
Edit the code, then press Run.
Running is simulated ยท TypeScript Playground
Exercise
Which type accepts the array ["pen", 3, "book"]?