1. 泛型函数定义
function transform<T, U>(arr: T[], mapper: (arg: T) => U): U[] {
return arr.map(mapper);
}
2. 上下文类型推导对 T
和 U
的影响
逆变性质的 T
- 逆变意味着在类型赋值中,如果
S
是 T
的子类型,那么 (T) => any
是 (S) => any
的子类型。
- 示例:
// 定义两个类型
type Animal = { name: string };
type Dog = { name: string; breed: string };
// 定义一个逆变函数
function feedAnimal(animal: Animal) {
console.log(`Feeding ${animal.name}`);
}
// 可以将 feedAnimal 赋值给 feedDog,因为 Dog 是 Animal 的子类型,而函数参数类型是逆变的
function feedDog(dog: Dog) {
console.log(`Feeding ${dog.name}, a ${dog.breed}`);
}
let feedFunction: (dog: Dog) => void = feedAnimal;
// 在 transform 函数中,如果传入的 mapper 函数参数类型更具体,T 会推导为更具体的类型
let animals: Animal[] = [{ name: 'Tom' }];
let dogMapper = (dog: Dog) => dog.breed;
// 这里 T 会推导为 Dog,因为 dogMapper 的参数类型是 Dog
let dogBreeds = transform(animals, dogMapper);
协变性质的 U
- 协变意味着在类型赋值中,如果
S
是 T
的子类型,那么 () => S
是 () => T
的子类型。
- 示例:
// 定义两个类型
type Pet = { name: string };
type Dog = { name: string; breed: string };
// 定义一个协变函数
function getPet(): Pet {
return { name: 'Buddy' };
}
// 可以将 getPet 赋值给 getDog,因为 Dog 是 Pet 的子类型,而函数返回值类型是协变的
function getDog(): Dog {
return { name: 'Max', breed: 'Golden Retriever' };
}
let getFunction: () => Pet = getDog;
// 在 transform 函数中,如果 mapper 函数返回值类型更具体,U 会推导为更具体的类型
let dogs: Dog[] = [{ name: 'Max', breed: 'Golden Retriever' }];
let petMapper = (dog: Dog) => ({ name: dog.name });
// 这里 U 会推导为 Pet,因为 petMapper 的返回值类型是 Pet
let pets = transform(dogs, petMapper);
3. 在复杂类型系统中确保类型安全和正确的类型推导
- 明确类型标注:在复杂类型系统中,为了确保类型安全和正确的类型推导,可以对泛型参数进行明确的类型标注。
// 明确标注 T 和 U 的类型
let numbers: number[] = [1, 2, 3];
let stringMapper = (num: number) => num.toString();
let strings = transform<number, string>(numbers, stringMapper);
- 使用类型保护和断言:在 mapper 函数内部,如果涉及到复杂的类型判断和转换,可以使用类型保护和断言。
type Shape = { kind: string };
type Circle = { kind: 'circle'; radius: number };
type Square = { kind:'square'; side: number };
function area(shape: Shape): number {
if (shape.kind === 'circle') {
let circle = shape as Circle;
return Math.PI * circle.radius * circle.radius;
} else {
let square = shape as Square;
return square.side * square.side;
}
}
let shapes: Shape[] = [{ kind: 'circle', radius: 5 }, { kind:'square', side: 4 }];
let areas = transform(shapes, area);
- 利用工具类型:TypeScript 提供了很多工具类型,如
Exclude
、Extract
、NonNullable
等,可以帮助在复杂类型系统中进行类型推导和类型安全检查。
type AllNumbers = number | string;
type OnlyNumbers = Exclude<AllNumbers, string>;
function filterNumbers(arr: AllNumbers[]): OnlyNumbers[] {
return arr.filter((value): value is OnlyNumbers => typeof value === 'number') as OnlyNumbers[];
}
let mixedArray: AllNumbers[] = [1, 'two', 3];
let numbersOnly = transform(mixedArray, filterNumbers);