最近在看 XFlow 的源码,发现里面出现了 Record、Omit、ReturnType 这些奇怪的东西,查了官方文档后才知道它们被称为 Utility Types,可以理解为内置的一些工具函数,会基于传入的泛型参数返回新的 ts 类型。这里做了一些简单的整理和说明,更具体的可以参考官方文档。
定义
TypeScript 提供了几种 Utility Types 来促进常见的类型转换。这些 Utility Types 可以在全局作用域使用
Partial
构造一个类型,其中Type 的所有属性都设置为可选。可以理解为 Partial<Type> 返回 Type 的所有子类型
interface Todo {
title: string;
description: string;
}
function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>) {
return { ...todo, ...fieldsToUpdate };
}
const todo1 = {
title: "organize desk",
description: "clear clutter",
};
const todo2 = updateTodo(todo1, {
description: "throw out trash",
});
Record<Keys, Type>
构造一个对象类型,其属性键为 Keys,其属性值为 Type。 Record<Keys, Type> 可用于将一种类型的属性映射到另一种类型。
interface CatInfo {
age: number;
breed: string;
}
type CatName = "miffy" | "boris" | "mordred";
const cats: Record<CatName, CatInfo> = {
miffy: { age: 10, breed: "Persian" },
boris: { age: 5, breed: "Maine Coon" },
mordred: { age: 16, breed: "British Shorthair" },
};
cats.boris;
在 XFlow 中如果通过 object 的方式定义映射类型会给出警告。
正确的方式应该是写成这样,是不是感觉简洁了很多,而且表意性更强
Pick<Type, Keys>
这个顾名思义,就是挑选出一些属性,看了例子就很好理解了。
interface Todo {
title: string;
description: string;
completed: boolean;
}
type TodoPreview = Pick<Todo, "title" | "completed">;
const todo: TodoPreview = {
title: "Clean room",
completed: false,
};
这里的type TodoPreview = Pick<Todo, "title" | "completed">其实就等价于
type TodoPreview = {
title: string;
completed: boolean;
}
Omit<Type, Keys>
omit 在英语中是弹出的意思,那这里也就很好理解了,就是移除掉一些属性,同样看例子
interface Todo {
title: string;
description: string;
completed: boolean;
createdAt: number;
}
type TodoPreview = Omit<Todo, "description">;
const todo: TodoPreview = {
title: "Clean room",
completed: false,
createdAt: 1615544252770,
};
type TodoInfo = Omit<Todo, "completed" | "createdAt">;
const todoInfo: TodoInfo = {
title: "Pick up kids",
description: "Kindergarten closes at 5pm",
};
同样地,type TodoPreview = Omit<Todo, "description">等价于一下写法
type TodoPreview = {
title: string;
completed: boolean;
createdAt: number;
}
ReturnType
构造一个由函数 Type 的返回类型组成的类型。
declare function f1(): { a: number; b: string };
type T0 = ReturnType<() => string>;
//相当于 type T0 = string
type T1 = ReturnType<(s: string) => void>;
//相当于 type T1 = void
type T2 = ReturnType<<T>() => T>;
//相当于 type T2 = unknown
type T3 = ReturnType<<T extends U, U extends number[]>() => T>;
//相当于 type T3 = number[]
type T4 = ReturnType<typeof f1>;
//相当于
/*
type T4 = {
a: number;
b: string;
}
*/
type T5 = ReturnType<any>;
//相当于 type T5 = any
type T6 = ReturnType<never>;
//相当于 type T6 = never
type T7 = ReturnType<string>; //警告: Type 'string' does not satisfy the constraint '(...args: any) => any'.
type T8 = ReturnType<Function>;
//警告:
// Type 'Function' does not satisfy the constraint '(...args: any) => any'.
// Type 'Function' provides no match for the signature '(...args: any): any'.
有的朋友可能觉得有些多次一举了,可以看一下 XFlow 中的用到的地方
这样使用一个函数的好处是不是就体现出来了,因为作为一个函数,里面可以进行一些逻辑处理。就像 React 中的 useState,也支持通过通过两种方式定义初始值
const [count, setCount] = useState(0);
const [state, setState] = useState(() => {
const initialState = someExpensiveComputation(props);
return initialState;
});