Sostieni AppuntiFacili con una piccola donazione su PayPal
Dona con PayPalTypeScript’s type system is not limited to plain, single types. Two operators let you combine existing types into new, more expressive ones:
|, which means “one type OR another”;&, which means “all of these types AT ONCE”.They look similar syntactically, but they express opposite ideas. Getting comfortable switching between them is one of the most useful skills in day-to-day TypeScript.
A union type describes a value that can be one of several types. You build it by separating the candidate types with |:
type StringOrNumber = string | number | boolean;
function acceptValue(val: StringOrNumber) {
// val could be a string, a number, or a boolean here
}
WARNING
Despite its name, StringOrNumber here is actually string | number | boolean — three member types, not two. This is a naming quirk you’ll run into constantly in real codebases: type aliases drift as requirements grow, and nobody renames them. Always check the actual definition instead of trusting the name.
Inside acceptValue, TypeScript only lets you use members and methods that are guaranteed to exist on every member of the union. For string | number | boolean that’s basically nothing useful (no shared method beyond what Object provides), so before doing anything type-specific you need to narrow the union down to a single concrete type — the subject of the next lesson.
function acceptValueSafely(val: StringOrNumber) {
if (typeof val === "string") {
console.log(val.toUpperCase()); // OK: val is string here
}
}
An intersection type combines multiple types into a single type that must satisfy all of them simultaneously. You build it with &:
interface BusinessPartner {
name: string;
}
interface ContactDetails {
email: string;
phone: string;
}
type BusinessContract = BusinessPartner & ContactDetails;
const contract: BusinessContract = {
name: "dennis",
email: "dennis@gmail.com",
phone: "123456789"
};
BusinessContract requires every property from both BusinessPartner and ContactDetails. If you forget one — say phone — the compiler rejects the object literal. Intersections are additive: the more types you & together, the more properties the resulting shape needs, not fewer.
TIP
A simple mental model: | narrows the set of possible values you might receive (any one member is enough), while & widens the set of properties a single value must have (every member’s shape is required).
Real-world modeling often mixes both. Consider two kinds of contract holders:
interface Individual {
name: string;
birthday: Date;
}
interface Organization {
companyName: string;
workPhone: string;
}
// A ContractType is EITHER an Individual OR an Organization
type ContractType = Individual | Organization;
// A CompContract must satisfy BOTH shapes at once
type CompContract = Individual & Organization;
ContractType models “this contract belongs to a person OR a company” — a realistic, mutually exclusive scenario. CompContract, on the other hand, forces a single object to carry name, birthday, companyName and workPhone all together — rarely what you actually want for two conceptually different entities, but perfectly valid TypeScript, and a good illustration of how easy it is to reach for the wrong operator by accident.
DANGER
A common mistake is using & when you meant | (or vice versa). If your intersection type becomes impossible to construct in practice, or your union type lets through nonsensical combinations, double-check which operator you actually needed.
Because ContractType is a union, you cannot access birthday or companyName directly — TypeScript doesn’t know which branch you have. You need a type guard to check at runtime and let the compiler narrow the type inside each branch:
function addContract(contract: ContractType) {
if ("birthday" in contract) {
// TypeScript now knows contract is Individual
console.log(contract.name, contract.birthday);
} else {
// and here it knows contract is Organization
console.log(contract.companyName, contract.workPhone);
}
}
The "birthday" in contract check is a form of narrowing: TypeScript inspects the condition and infers a more specific type inside each branch of the if. This is exactly the topic of the next lesson, where we cover typeof, instanceof, the in operator, and custom type guards in depth.
What does the union operator | mean in a type definition?
What does the intersection operator & mean in a type definition?
In `type StringOrNumber = string | number | boolean;`, how many member types does the union actually have?
Why can't you freely access `contract.companyName` inside a function that takes `contract: Individual | Organization`?
What is the effect of `type CompContract = Individual & Organization;` given the two interfaces in this lesson?
Which technique lets TypeScript narrow a union type inside an if branch, as shown with `"birthday" in contract`?
Scenario: You are building a small invoicing module that needs to represent either a private customer or a company customer, and separately, a combined internal record that always needs contact information regardless of customer kind.
Task:
PrivateCustomer (fullName: string, taxCode: string) and CompanyCustomer (companyName: string, vatNumber: string).Customer = PrivateCustomer | CompanyCustomer.ContactInfo (email: string, phone: string).CustomerWithContact that combines ContactInfo with Customer (hint: you can intersect a union with another type — the result is still a union of two combined shapes).printCustomer(customer: Customer) that uses "taxCode" in customer to branch and print the right fields for each case.Learning objective: practice building union and intersection types from scratch and distinguishing when each is appropriate.
Scenario: You are reviewing a legacy TypeScript file (similar to the BusinessContract example) and want to spot type-design smells before they cause bugs.
Task:
Individual and Organization interfaces from this lesson.Individual & Organization. List every property it must contain.Individual | Organization.kind: "individual" | "organization" tag field on each interface (you’ll cover this pattern properly in the next lessons) and note how it removes the ambiguity of the in check.Learning objective: recognize when an intersection produces an impractical type and see a preview of how tagged/discriminated unions solve the same modeling problem more safely.
Prenota una lezione