TypeScript course menu
TypeScript Intersection Types: Combining Types with &
Learn TypeScript intersection types: combining object types with &, building types from reusable pieces, intersections versus interface extends, conflicting properties that become never, and intersections with unions.
On this page
An intersection type, written with &, combines several types into one that has all of their members. Where a union A | B means "A or B", an intersection A & B means "A and B at the same time".
Combining object types#
type HasId = { id: string };
type HasTimestamps = { createdAt: Date; updatedAt: Date };
type Post = { title: string; body: string };
type StoredPost = HasId & HasTimestamps & Post;
const post: StoredPost = {
id: "p-1",
title: "Learning TypeScript",
body: "Intersections combine types.",
createdAt: new Date("2026-10-01T00:00:00Z"),
updatedAt: new Date("2026-10-24T00:00:00Z"),
};
console.log(post.id, post.title, post.updatedAt.toISOString().slice(0, 10));p-1 Learning TypeScript 2026-10-24A StoredPost must have every property from all three types; leaving any out is an error. Small, reusable pieces like HasId and HasTimestamps can be mixed into many types.
Adding properties to an existing type#
Intersections are handy for "this type, plus a little more":
type User = { name: string; email: string };
type UserWithRole = User & { role: "admin" | "member" };
function welcome(user: UserWithRole): string {
return `${user.name} (${user.role})`;
}
console.log(welcome({ name: "Asha", email: "asha@example.com", role: "admin" }));Asha (admin)Intersection or interface extends?#
These two definitions produce types with the same members:
interface Animal { name: string }
interface Dog extends Animal { breed: string }
type AnimalT = { name: string };
type DogT = AnimalT & { breed: string };
const d1: Dog = { name: "Bruno", breed: "Labrador" };
const d2: DogT = d1; // compatible: same shape
console.log(d2.breed);LabradorDifferences:
extendsreports conflicting property types as an error at the declaration, which is clearer.&works with any types, including unions and generic types, so it is more flexible.- Many teams use
interface ... extendsfor object hierarchies and&when composing type aliases.
Conflicting properties#
If two intersected object types give the same property incompatible types, the property becomes never, so no value can satisfy it:
type A = { id: string };
type B = { id: number };
type AB = A & B; // id: string & number, which is never
const x: AB = { id: "1" };error TS2322: Type 'string' is not assignable to type 'never'.Intersecting incompatible primitive types directly, such as string & number, also gives never.
Intersections with unions#
An intersection distributes over a union. This is useful to add common fields to every member of a discriminated union:
type Payment =
| { method: "card"; last4: string }
| { method: "upi"; upiId: string };
type PaymentRecord = Payment & { amount: number; paidAt: string };
const records: PaymentRecord[] = [
{ method: "card", last4: "4242", amount: 499, paidAt: "2026-10-20" },
{ method: "upi", upiId: "ravi@okbank", amount: 120, paidAt: "2026-10-21" },
];
const total = records.reduce((sum, r) => sum + r.amount, 0);
console.log(total, records.map((r) => r.method));619 [ 'card', 'upi' ]Practice#
Practice exercise
Create reusable pieces Priced = { price: number } and Discountable = { discountPercent: number }, and use them to define SaleItem = { name: string } & Priced & Discountable. Write salePrice(item: SaleItem): number and print the sale price of a ₹1,200 item with a 25% discount.
Show solution
type Priced = { price: number };
type Discountable = { discountPercent: number };
type SaleItem = { name: string } & Priced & Discountable;
function salePrice(item: SaleItem): number {
return item.price * (1 - item.discountPercent / 100);
}
const jacket: SaleItem = { name: "Jacket", price: 1200, discountPercent: 25 };
console.log(`${jacket.name}: ₹${salePrice(jacket)}`);Jacket: ₹900Key takeaway#
Finished this lesson?