据我所知,排除条件类型助手无法正常工作

时间:2019-04-06 03:16:41

标签: typescript

我一直在遍历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仍然具有 all 相同的属性,并且没有什么不同。我还使用了interface而不是type别名,它表现出相同的行为。

看看TypeScript docs,我看到了提供的(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'>;

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

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

好的,我通过Exclude使用keyof T会创建所需密钥的简短列表,而Pick会完成其余的操作。但是,我仍然非常想了解Exclude帮助程序的非理论用例示例,以及它如何独立工作。对我来说,如果Exclude像我想的那样工作,则不需要创建Omit类型。文档中那些人为设计的示例可能适合2个字符串文字列表,但我想知道Excludeinterfacetype结合如何工作吗?还是我误解了它的用法,应该始终将其与keyof?

结合使用

1 个答案:

答案 0 :(得分:1)

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。无需使用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是最常用的联合类型。