Logo
Section 1 / 8

使用字面量类型和联合类型

学习如何使用字面量类型精确约束类型,以及如何通过联合类型建模灵活的行为,同时避免因歧义带来的问题。

导入

使用字面量类型和联合类型

学习如何使用字面量类型精确约束类型,以及如何通过联合类型建模灵活的行为,同时避免因歧义带来的问题。

let mode: "dark" = "dark"; // ✅ mode = "light"; // ❌ 错误:"light" 不能赋值给 "dark"

console.log(模式:${mode});

let theme: string = "dark"; // ✅ 但现在允许任意字符串 theme = "neon"; // ✅ TypeScript 不会报错

console.log(主题:${theme});

type Enabled = true; type Level = 3; type Nothing = null;

const isEnabled: Enabled = true; const priority: Level = 3; const value: Nothing = null;

console.log(已启用:${isEnabled},优先级:${priority},值:${value});

const config = { mode: "dark", version: 2, } as const;

type Config = typeof config;

const newConfig: Config = { mode: "light", // ❌ 错误:类型 '"light"' 不能赋值给类型 '"dark"' version: 2, };

function format(input: string | number): string { return 值:${input}; }

console.log(format("Hello")); // ✅ console.log(format(42)); // ✅

function handle(input: string | number) { if (typeof input === "string") { return input.toUpperCase(); // ✅ 安全 }

return input * 2; // ✅ 此时一定是数字 }

console.log(handle("hello"), handle(21));

定义一个自定义类型守卫,用于检查结构并通知类型系统

1type Dog = { bark(): void }; 2type Cat = { meow(): void }; 3type Pet = Dog | Cat; 4 5function isDog(pet: Pet): pet is Dog { 6 return "bark" in pet; 7}

type Dog = { bark(): void }; type Cat = { meow(): void }; type Pet = Dog | Cat;

function isDog(pet: Pet): pet is Dog { return "bark" in pet; }

function handlePet(pet: Pet) { if (isDog(pet)) { pet.bark(); // ✅ 安全:此时 pet 是 Dog 类型 } else { pet.meow(); // ✅ 类型已被收窄为 Cat } }

const fido: Pet = { bark: () => console.log("Woof!") }; const whiskers: Pet = { meow: () => console.log("Meow.") };

handlePet(fido); // 输出:Woof! handlePet(whiskers); // 输出:Meow.

注意:当一个函数满足以下条件时,它就成为类型守卫:

  • 返回布尔值。
  • 具有类似 x is SomeType 的返回类型。
  • 执行运行时检查,以告知 TypeScript 如何进行类型收窄。

type Method = "GET" | "POST" | "PUT" | "DELETE";

function handleRequest(method: "GET" | "DELETE"): string; function handleRequest(method: "POST" | "PUT", body: string): string; function handleRequest(method: Method, body?: string): string { return body ? 正文:${body} : 无正文,方法为 ${method}; }

console.log(handleRequest("GET")); console.log(handleRequest("POST", "data"));

type TextInput = { // 完整的类型定义 };

type FileInput = { // 完整的类型定义 };

// 完整的类型定义 type FormInput = ...

// 在此处编写你的 isFileInput 自定义类型守卫

// 在此处编写你的 processInput 处理函数

// // 以下为测试代码 //

// 示例输入 const inputs: FormInput[] = [ { label: "Username", value: "Alice" }, { label: "Resume", files: ["cv.pdf"] }, { label: "Photos", files: ["img1.png", "img2.png"], multiple: true } ];

// 对每个输入调用你的函数 for (const input of inputs) { console.log(processInput(input)); }

// ❌ 无效输入(取消注释后应触发类型错误) // processInput({ label: "Bad", files: "not-an-array" }); // processInput({ label: "Confusing", value: "text", files: [123] });

显示解决方案

该自定义类型守卫通过检查 files 数组,可靠地识别和处理 FileInput

1type TextInput = { 2 label: string; 3 value: string; 4 maxLength?: number; 5}; 6 7type FileInput = { 8 label: string; 9 files: string[]; 10 multiple?: boolean; 11}; 12 13type FormInput = TextInput | FileInput; 14 15function isFileInput(input: FormInput): input is FileInput { 16 return ( 17 "files" in input && 18 Array.isArray(input.files) && 19 input.files.every(file => typeof file === "string") 20 ); 21} 22 23function processInput(input: FormInput): string { 24 if (isFileInput(input)) { 25 const count = input.files.length; 26 return `正在上传 ${count} 个文件${count === 1 ? "" : "s"}`; 27 } else { 28 return `收到文本:${input.value}`; 29 } 30} 31 32const inputs: FormInput[] = [ 33 { label: "Username", value: "Alice" }, 34 { label: "Resume", files: ["cv.pdf"] }, 35 { label: "Photos", files: ["img1.png", "img2.png"], multiple: true } 36]; 37 38for (const input of inputs) { 39 console.log(processInput(input)); 40} 41 42// ❌ 无效输入(取消注释后应触发类型错误) 43// processInput({ label: "Bad", files: "not-an-array" }); 44// processInput({ label: "Confusing", value: "text", files: [123] });

解释:

  • 第 15–21 行:isFileInput 函数在满足以下条件时返回 true:

    • 输入对象中存在 "files" 键。

    • files 的值是一个数组。

    • 该数组中的每一项都是字符串。

  • 第 23–30 行:processInput 使用自定义类型守卫安全地区分输入形状。如果是文件输入,则格式化上传数量信息;否则必定是文本输入,可安全访问 .value 属性。

显示提示

提示:

  • 记住,类型守卫函数返回布尔值,并在返回 true 时告诉 TypeScript 类型已被收窄。

  • 使用 "files" in input 检查字段是否存在。

  • 然后使用 Array.isArray() 和 every() 确保它是字符串数组。

  • 不要使用 as——让 TypeScript 根据你的守卫推断类型。

  • 在 processInput 中,使用 if (isFileInput(...)) 进行类型收窄。

对象与集合

可辨识联合与穷尽性检查