排除条件类型帮助程序无法正常工作,据我了解



我一直在 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'>;

我也很清楚通过keyOfExcludePick结合使用来创建Omit类型,这最终与我上面的第一个示例很好地配合使用,以生成具有预期减去属性的类型:

type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;

好的,我通过keyof T使用Exclude将创建一个所需键的候选列表,其余的Pick完成。但是,我仍然非常想了解Exclude助手的非理论用例示例以及它如何独立工作。对我来说,如果Exclude像我认为的那样工作,我就不需要创建Omit类型。文档中的那些人为示例可能非常适合 2 个字符串文本列表,但我想知道Exclude如何与interfacetype结合使用?还是我误解了它的用法,它应该始终与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是最常用的。

最新更新