我一直在 TypeScript 中大量使用示例,试图了解Exclude
条件类型帮助程序如何独立工作。
根据 TypeScript 文档的定义:
排除 – 从 T 中排除可分配给 U 的类型。
如库中所定义:
/**
* Exclude from T those types that are assignable to U
*/
type Exclude<T, U> = T extends U ? never : T;
我看到了很多来自用户的不正确的博客文章,并且尝试了很多代码,如下所示:
type User = {
id: string;
name: string;
phone: number;
isAdmin: boolean;
};
// Doesn't work below as standardUser still has all properties
type standardUser = Exclude<User, 'isAdmin'>;
我希望看到的是standardUser
是一个已定义但没有isAdmin
属性的type
。结果是type standardUser
仍然具有所有相同的属性,没有什么不同。我还使用了interface
而不是type
别名,并且它表现出相同的行为。
查看 TypeScript 文档,我看到 (2) 提供了示例(当然还有使用文字和原语,它们是理论上的
,翻译得不好。type T00 = Exclude<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "b" | "d"
type T02 = Exclude<string | number | (() => void), Function>; // string | number
它们都有效,但我在这里没有翻译成现实世界的应用程序。然后我认为如定义中所述,它排除了可分配给另一个的类型,因此我尝试了以下方法:
type User = {
id: string;
isAdmin: boolean;
};
// Doesn't work below as standardUser still has all properties
type standardUser = Exclude<User, boolean>;
我再次希望看到standardUser
被定义为只有id
的类型,因为应该排除boolean
类型。我再次对这个原始助手的理解是错误的。
我也尝试使用enum
,这也没有减去任何值,并且所有可用值的新类型保持不变:
enum Fruits {
apple,
pear,
bananna
}
// Doesn't work below as redFruit still has all values
type redFruit = Exclude<fruits, 'pear' | 'bananna'>;
我也很清楚通过keyOf
将Exclude
与Pick
结合使用来创建Omit
类型,这最终与我上面的第一个示例很好地配合使用,以生成具有预期减去属性的类型:
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
好的,我通过keyof T
使用Exclude
将创建一个所需键的候选列表,其余的Pick
完成。但是,我仍然非常想了解Exclude
助手的非理论用例示例以及它如何独立工作。对我来说,如果Exclude
像我认为的那样工作,我就不需要创建Omit
类型。文档中的那些人为示例可能非常适合 2 个字符串文本列表,但我想知道Exclude
如何与interface
或type
结合使用?还是我误解了它的用法,它应该始终与keyof?
结合使用
type User = {
id: string;
name: string;
phone: number;
isAdmin: boolean;
};
// Doesn't work below as standardUser still has all properties
type standardUser = Exclude<User, 'isAdmin'>;
Exclude
适用于类型。从您的示例中,您似乎希望Exclude
不处理类型,而是处理属性 -'isAdmin'
是属性名称,而不是示例中的类型。
但是,我仍然非常想了解排除帮助程序的非理论用例示例以及它如何独立工作。对我来说,如果排除像我认为的那样工作,我就不需要创建省略类型。
不幸的是,它并不像您想象的那样工作。实际上,Exclude
不是理论上的,而是低级构建块,它允许您构造所需的类型,例如Omit
。不使用像Omit
这样的中间类型,你可以这样从User
中获取所需的类型:
type standardUser = { [k in Exclude<keyof User, 'isAdmin'>]: User[k] };
以下是逐步构建的相同类型:
type Step1 = keyof User; // get union type of User keys
// type Step1 = "id" | "name" | "phone" | "isAdmin"
type Step2 = Exclude<Step1, 'isAdmin'>; // remove "isAdmin" from union type
// type Step2 = "id" | "name" | "phone"
type Step3 = { [k in Step2]: User[k] }; // mapped type
// see https://www.typescriptlang.org/docs/handbook/advanced-types.html#mapped-types
// same as user, but without 'isAdmin' key
// type Step3 = { id: string; name: string; phone: number; }
但我想知道排除如何与接口或类型结合使用
通常,根据所有可能值的集合来考虑类型是有帮助的。例如,User
类型可以看作是具有所有 4 个属性的所有对象的集合:"id"、"name"、"phone"、"isAdmin",每个对象都有适当的类型。所以本质上它与 4 种类型的交集相同:
{id: string} & {name: string} & {phone: string} & {isAdmin: boolean}
如果要删除isAdmin
属性,实际上正在扩展符合新类型的对象集 - 要符合,对象现在必须只有 3 个属性,而不是全部 4 个属性。换句话说,"普通"用户集大于"管理员"用户集。所以Exclude
,它"缩小"了类型,在这里没有直接帮助。Exclude
最适合联合类型,其中keyof
是最常用的。