TypeScript 联合类型:string | number 的使用
基本概念理解
了解 TypeScript 联合类型的基本概念。联合类型是一种允许变量具有多种类型之一的特性,使用 | 操作符连接不同类型。string | number 表示一个值可以是字符串或数字类型。
声明 一个 string | number 类型的变量:
let value: string | number;
value = "Hello"; // 有效
value = 42; // 有效
value = true; // 错误,不能赋值为布尔值
类型收窄技术
使用 typeof 操作符收窄联合类型。typeof 是 TypeScript 内置的类型保护机制,可以帮助在运行时确定值的实际类型。
编写 类型收窄函数:
function processValue(value: string | number): string {
if (typeof value === 'string') {
// 在这个块中,TypeScript 知道 value 是字符串类型
return value.toUpperCase();
} else {
// 在这个块中,TypeScript 知道 value 是数字类型
return value.toString();
}
}
注意 类型收窄只在条件判断中有效,确保每个条件分支都处理了所有可能的类型。
实用场景与示例
表单数据处理
处理 用户表单提交的数据,其中某些字段可能是字符串也可能是数字:
type FormData = {
username: string;
age: string | number;
email: string;
};
function validateAge(age: string | number): boolean {
if (typeof age === 'string') {
const parsedAge = parseInt(age, 10);
return !isNaN(parsedAge) && parsedAge > 0;
} else {
return age > 0;
}
}
function submitForm(data: FormData) {
if (validateAge(data.age)) {
// 提交表单
console.log("表单提交成功");
} else {
console.log("年龄无效");
}
}
// 使用示例
submitForm({
username: "john_doe",
age: "25", // 可以是字符串
email: "john@example.com"
});
submitForm({
username: "jane_doe",
age: 30, // 可以是数字
email: "jane@example.com"
});
API 响应处理
处理 从 API 获取的数据,某些字段可能是字符串或数字:
interface ApiResponse {
id: string | number;
name: string;
score: string | number;
}
function processApiResponse(response: ApiResponse): string {
const id = typeof response.id === 'number' ? `#${response.id}` : response.id;
const score = typeof response.score === 'number'
? `分数: ${response.score}`
: `分数: ${parseInt(response.score, 10)}`;
return `${id} - ${response.name} - ${score}`;
}
// 使用示例
const response1: ApiResponse = {
id: 123,
name: "Alice",
score: 95
};
const response2: ApiResponse = {
id: "ABC-456",
name: "Bob",
score: "88"
};
console.log(processApiResponse(response1)); // #123 - Alice - 分数: 95
console.log(processApiResponse(response2)); // ABC-456 - Bob - 分数: 88
数组元素处理
操作 包含字符串和数字的数组:
function sumOrConcatenate(values: (string | number)[]): string | number {
if (values.every(val => typeof val === 'number')) {
// 所有元素都是数字,进行求和
return (values as number[]).reduce((sum, val) => sum + val, 0);
} else if (values.every(val => typeof val === 'string')) {
// 所有元素都是字符串,进行连接
return (values as string[]).join('');
} else {
// 混合类型,返回连接后的字符串
return values.map(val => val.toString()).join('-');
}
}
// 使用示例
console.log(sumOrConcatenate([1, 2, 3])); // 6 (数字)
console.log(sumOrConcatenate(["a", "b", "c"])); // "abc" (字符串)
console.log(sumOrConcatenate([1, "2", 3])); // "1-2-3" (字符串)
类型断言与类型守卫
使用 类型断言(Type Assertion)当确定值的类型时:
function formatValue(value: string | number): string {
// 使用类型断言告诉 TypeScript value 是字符串
return (value as string).toUpperCase();
}
创建 自定义类型守卫函数:
function isString(value: string | number): value is string {
return typeof value === 'string';
}
function process(value: string | number) {
if (isString(value)) {
console.log(value.length); // 只有在这里才能安全访问 .length
} else {
console.log(value * 2); // 只有在这里才能进行数学运算
}
}
常见错误与解决方案
错误:尝试在联合类型上调用特定方法
// 错误示例
function processValue(value: string | number) {
// 错误:数字类型没有 toUpperCase 方法
return value.toUpperCase();
}
解决 使用类型收窄或类型保护:
// 解决方案
function processValue(value: string | number) {
if (typeof value === 'string') {
return value.toUpperCase();
} else {
return value.toString();
}
}
错误:使用类型比较而非值比较
// 错误示例
function checkType(a: string | number, b: string | number) {
// 错误:比较的是类型而非值
return typeof a === typeof b;
}
解决 比较实际的值而非类型:
// 解决方案
function checkType(a: string | number, b: string | number) {
// 正确:比较实际值
return a === b;
}
最佳实践
-
优先使用类型收窄 而不是类型断言,类型收窄更安全。
-
创建类型守卫函数 当需要多次检查同一类型时。
-
为联合类型创建别名 提高代码可读性:
type ID = string | number;
function processId(id: ID): string {
if (typeof id === 'string') {
return `ID-${id}`;
}
return `ID-${id.toString()}`;
}
- 使用联合类型接口 为复杂对象定义联合类型:
type User = {
name: string;
id: string | number;
age: number;
};
type Admin = {
name: string;
id: string | number;
level: number;
};
type UserOrAdmin = User | Admin;
function processUser(user: UserOrAdmin) {
console.log(user.name);
if ('age' in user) {
console.log(`用户年龄: ${user.age}`);
} else {
console.log(`管理员级别: ${user.level}`);
}
}
- 避免过度使用联合类型 如果可能,考虑使用更精确的类型系统如枚举或联合类型与字面量类型的组合:
// 更好的方式
type AgeUnit = 'years' | 'months' | 'days';
type Age = {
value: number;
unit: AgeUnit;
};
应用 联合类型可以大大提高代码的灵活性,特别是在处理不确定数据类型或API响应时。记住 始终考虑类型的边界情况,并确保所有可能的类型都被适当处理。

暂无评论,快来抢沙发吧!