10-union-intersection.ts
Typescript/src/01-beginner/10-union-intersection.ts
// ============================================
// 10. Union and Intersection Types
// ============================================
console.log("=== UNION AND INTERSECTION TYPES ===\n");
// Union Types - value can be one of several types
console.log("=== UNION TYPES ===\n");
console.log("Union types allow a value to be one of several types");
console.log("Syntax: type A = Type1 | Type2 | Type3\n");
type StringOrNumber = string | number;
function processValue(value: StringOrNumber) {
// value can be either string or number
if (typeof value === "string") {
return value.toUpperCase();
} else {
return value.toFixed(2);
}
}
console.log("Example: StringOrNumber = string | number");
console.log(` processValue("hello") → "${processValue("hello")}"`);
console.log(` processValue(42) → "${processValue(42)}"`);
console.log(" ✅ Value can be either string OR number");
// Common Union Patterns
console.log("\n=== COMMON UNION PATTERNS ===\n");
type ID = string | number;
console.log("1. ID type: string | number");
const userId: ID = "123";
const numericId: ID = 456;
console.log(` userId: ${userId} (string)`);
console.log(` numericId: ${numericId} (number)`);
type MaybeString = string | null | undefined;
console.log("\n2. MaybeString: string | null | undefined");
const maybe1: MaybeString = "hello";
const maybe2: MaybeString = null;
const maybe3: MaybeString = undefined;
console.log(` maybe1: ${maybe1}`);
console.log(` maybe2: ${maybe2}`);
console.log(` maybe3: ${maybe3}`);
type Status = "pending" | "approved" | "rejected";
console.log("\n3. Status: Literal union type");
const status1: Status = "pending";
const status2: Status = "approved";
console.log(` status1: "${status1}"`);
console.log(` status2: "${status2}"`);
console.log(" ✅ Can only be one of: 'pending' | 'approved' | 'rejected'");
type Result =
| { success: true; data: any }
| { success: false; error: string };
console.log("\n4. Result: Discriminated union");
const successResult: Result = { success: true, data: { id: 1, name: "Alice" } };
const errorResult: Result = { success: false, error: "Not found" };
console.log(` successResult:`, successResult);
console.log(` errorResult:`, errorResult);
console.log(" ✅ Type-safe error handling pattern");
// Discriminated Unions
console.log("\n=== DISCRIMINATED UNIONS ===\n");
type Shape =
| { kind: "circle"; radius: number }
| { kind: "rectangle"; width: number; height: number }
| { kind: "triangle"; base: number; height: number };
function area(shape: Shape): number {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "rectangle":
return shape.width * shape.height;
case "triangle":
return (shape.base * shape.height) / 2;
}
}
console.log("Discriminated unions use a common property to distinguish types:");
const circle: Shape = { kind: "circle", radius: 5 };
const rectangle: Shape = { kind: "rectangle", width: 4, height: 6 };
const triangle: Shape = { kind: "triangle", base: 3, height: 4 };
console.log(` Circle (radius: 5): area = ${area(circle).toFixed(2)}`);
console.log(` Rectangle (4x6): area = ${area(rectangle)}`);
console.log(` Triangle (base: 3, height: 4): area = ${area(triangle)}`);
console.log(" ✅ TypeScript narrows the type based on 'kind' property");
// Intersection Types - value must satisfy all types
console.log("\n=== INTERSECTION TYPES ===\n");
interface Person {
name: string;
age: number;
}
interface Employee {
employeeId: string;
department: string;
}
type Staff = Person & Employee;
const staff: Staff = {
name: "Alice",
age: 30,
employeeId: "E001",
department: "Engineering"
// Must have all properties from both interfaces
};
console.log("Intersection types combine multiple types:");
console.log(" Staff = Person & Employee");
console.log(" ✅ Must have ALL properties from BOTH types");
console.log(` staff:`, staff);
console.log(" Has properties from Person (name, age) AND Employee (employeeId, department)");
// Merging Types
console.log("\n=== MERGING TYPES WITH INTERSECTION ===\n");
type A = { a: number };
type B = { b: string };
type C = A & B; // { a: number; b: string }
const merged: C = { a: 42, b: "hello" };
console.log("Merging types with intersection:");
console.log(` type A = { a: number }`);
console.log(` type B = { b: string }`);
console.log(` type C = A & B → { a: number; b: string }`);
console.log(` merged:`, merged);
// Type Narrowing
console.log("\n=== TYPE NARROWING ===\n");
function process(value: string | number) {
if (typeof value === "string") {
// TypeScript knows value is string here
return value.length;
} else {
// TypeScript knows value is number here
return value.toFixed(2);
}
}
console.log("TypeScript narrows types based on control flow:");
console.log(` process("hello") → ${process("hello")} (string length)`);
console.log(` process(42) → "${process(42)}" (number formatted)`);
console.log(" ✅ TypeScript knows the type in each branch");
// Type Guards
console.log("\n=== TYPE GUARDS ===\n");
interface Dog {
type: "dog";
bark(): void;
}
interface Cat {
type: "cat";
meow(): void;
}
type Pet = Dog | Cat;
function isDog(pet: Pet): pet is Dog {
return pet.type === "dog";
}
function makeSound(pet: Pet) {
if (isDog(pet)) {
pet.bark(); // TypeScript knows it's a Dog
} else {
pet.meow(); // TypeScript knows it's a Cat
}
}
const myDog: Pet = {
type: "dog",
bark() {
console.log(" Woof! Woof!");
}
};
const myCat: Pet = {
type: "cat",
meow() {
console.log(" Meow!");
}
};
console.log("Type guards narrow union types:");
console.log(" Pet = Dog | Cat");
console.log(" isDog(pet): pet is Dog → type predicate");
console.log("\n Calling makeSound with dog:");
makeSound(myDog);
console.log("\n Calling makeSound with cat:");
makeSound(myCat);
console.log(" ✅ TypeScript knows the exact type after type guard");
// Combining Union and Intersection
console.log("\n=== COMBINING UNION AND INTERSECTION ===\n");
type A2 = { a: number } | { b: string };
type B2 = { c: boolean } & A2;
// B2 is: ({ c: boolean; a: number }) | ({ c: boolean; b: string })
const combined1: B2 = { c: true, a: 42 };
const combined2: B2 = { c: false, b: "hello" };
console.log("Combining union and intersection:");
console.log(` type A2 = { a: number } | { b: string }`);
console.log(` type B2 = { c: boolean } & A2`);
console.log(` Result: ({ c: boolean; a: number }) | ({ c: boolean; b: string })`);
console.log(` combined1:`, combined1);
console.log(` combined2:`, combined2);
console.log(" ✅ Intersection distributes over union");
console.log("\n=== DEMO COMPLETE ===");
export { processValue, area, staff, makeSound };
相關文章
01-introduction.ts
01-introduction.ts — typescript source code from the Typescript learning materials (Typescript/src/01-beginner/01-introduction.ts).
閱讀文章 →02-basic-types.ts
02-basic-types.ts — typescript source code from the Typescript learning materials (Typescript/src/01-beginner/02-basic-types.ts).
閱讀文章 →03-variables.ts
03-variables.ts — typescript source code from the Typescript learning materials (Typescript/src/01-beginner/03-variables.ts).
閱讀文章 →04-functions.ts
04-functions.ts — typescript source code from the Typescript learning materials (Typescript/src/01-beginner/04-functions.ts).
閱讀文章 →05-interfaces.ts
05-interfaces.ts — typescript source code from the Typescript learning materials (Typescript/src/01-beginner/05-interfaces.ts).
閱讀文章 →06-classes.ts
06-classes.ts — typescript source code from the Typescript learning materials (Typescript/src/01-beginner/06-classes.ts).
閱讀文章 →