使用字面量类型和联合类型
学习如何使用字面量类型精确约束类型,以及如何通过联合类型建模灵活的行为,同时避免因歧义带来的问题。
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(...))进行类型收窄。