利用高级类型设计灵活且可维护的类型架构
- 映射类型
- 用途:用于基于现有类型创建新类型。例如,当你有一个对象类型,想对其所有属性执行相同操作时,映射类型非常有用。
- 示例:
type User = {
name: string;
age: number;
email: string;
};
// 将User类型的所有属性变为只读
type ReadonlyUser = {
readonly [P in keyof User]: User[P];
};
- 条件类型
- 用途:根据条件选择不同的类型。在处理联合类型或需要根据某个类型条件进行类型转换时很有用。
- 示例:
type IsString<T> = T extends string? true : false;
type Result = IsString<string>; // true
type AnotherResult = IsString<number>; // false
- 类型推断
- 用途:TypeScript 编译器自动根据上下文推断类型。在函数返回值类型、泛型函数等场景下发挥重要作用,减少显式类型声明,提高代码简洁性。
- 示例:
function add(a: number, b: number) {
return a + b;
}
let result = add(1, 2); // result类型被推断为number
项目中可能遇到的类型难题及解决方法
- 难题:在不同模块中,有一个表示用户信息的对象,但部分模块需要只读版本,部分模块需要可写版本,同时要确保类型一致。
- 解决方法:使用映射类型。如上述
User
和 ReadonlyUser
的例子,通过映射类型将 User
类型的所有属性变为只读,确保了不同模块对用户信息类型需求的兼容性和扩展性。
- 难题:一个函数接收不同类型的参数,根据参数类型返回不同类型的值。例如,传入字符串返回字符串长度,传入数字返回其平方。
function processValue<T>(value: T): T extends string? number : T extends number? number : never {
if (typeof value ==='string') {
return value.length as any;
} else if (typeof value === 'number') {
return value * value as any;
}
throw new Error('Unsupported type');
}
let strResult = processValue('test'); // strResult类型为number
let numResult = processValue(5); // numResult类型为number
- 难题:在一个模块中定义了一个通用的函数,它需要接收不同类型的数组,并对数组中的每个元素执行特定操作,但操作因元素类型而异。
type StringProcessor = (s: string) => string;
type NumberProcessor = (n: number) => number;
function processArray<T>(arr: T[], processor: T extends string? StringProcessor : T extends number? NumberProcessor : never): (T extends string? string : T extends number? number : never)[] {
return arr.map((item) => {
if (typeof item ==='string') {
return processor(item) as any;
} else if (typeof item === 'number') {
return processor(item) as any;
}
throw new Error('Unsupported type');
}) as any;
}
let stringArr = ['a', 'b'];
let processedStringArr = processArray(stringArr, (s) => s.toUpperCase());
let numberArr = [1, 2];
let processedNumberArr = processArray(numberArr, (n) => n * 2);