首页 > 解决方案 > 据我了解,排除条件类型助手不起作用

问题描述

我一直在倾注 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是一个type已定义但没有isAdmin属性的。结果是type standardUser仍然具有所有相同的属性并且没有什么不同。我还使用了 aninterface而不是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被定义为一个类型,只有idboolean类型应该被排除在外。我对这个原始助手的理解再次被取消。

我也尝试了一个enum,这也没有减去任何值,并且新类型与所有可用值保持相同:

enum Fruits {
   apple,
   pear,
   bananna      
}
// Doesn't work below as redFruit still has all values
type redFruit = Exclude<fruits, 'pear' | 'bananna'>;

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

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

好的,我Exclude通过keyof T将创建所需键的候选清单并Pick完成其余的工作。但是,我仍然非常想了解帮助程序的非理论用例示例Exclude以及它如何独立工作。对我来说,如果Exclude按照我的想法工作,我就不需要创建Omit类型。文档中的那些人为示例可能非常适合 2 个字符串文字列表,但我想知道如何Excludeinterfaceor typeplease 结合使用?还是我误解了它的用法,它应该始终与keyof?

标签: typescript

解决方案


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 帮助器的非理论用例示例以及它如何独立工作。对我来说,如果 Exclude 像我认为的那样工作,我就不需要创建 Omit 类型。

不幸的是,它并没有像你想象的那样工作。实际上,Exclude它不是理论上的,而是允许您构建所需类型的低级构建块,例如Omit. 在不使用中间类型 like的情况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; }

但我想知道 Exclude 如何与接口或类型结合使用

一般来说,根据所有可能值的集合来考虑类型是有帮助的。例如,User可以将类型视为一组具有所有 4 个属性的所有对象:“id”、“name”、“phone”、“isAdmin”,每个属性都有适当的类型。所以本质上它与 4 种类型的交集相同:

{id: string} & {name: string} & {phone: string} & {isAdmin: boolean}

如果要删除isAdmin属性,实际上是在扩展符合新类型的对象集 - 为了符合,对象现在必须只有 3 个属性,而不是全部 4 个。换句话说,“普通”用户集更大而不是一组“管理员”用户。因此Exclude,“缩小”类型在这里没有直接帮助。Exclude在联合类型上效果最好,其中keyof最常用。


推荐阅读