这很难解释,但是我试图在对象类型的子字段中过滤出某种类型。
Exclude<T, U>
似乎不适用于这种嵌套:
type PrivateName = { kind: "PrivateName" };
type Identifier = { kind: "Identifier" };
type NamedNode = {
name: PrivateName | Identifier;
};
type PrivateNamedNode = {
name: PrivateName;
};
declare const privateName: PrivateName;
type NamedNodeNonPrivate = Exclude<NamedNode, PrivateNamedNode>;
// I expected this next line to error, but it doesn't!
const x: NamedNodeNonPrivate = { name: privateName };
为什么最后一行没有出错?我应该打开错误报告吗?
在TS版本上测试:3.5.1、3.3.3、3.1.6和3.0.1。 playground link。
Exclude<T, U>
的定义是
/**
* Exclude from T those types that are assignable to U
*/
type Exclude<T, U> = T extends U ? never : T;
在我的示例中,这似乎是惰性的,因为NamedNode
不扩展{ name: PrivateName }
。
按如下所示更改定义会产生所需的错误消息。不幸的是,这不是重构,我可以手动处理整个代码库:
type NamedNode = {
name: PrivateName
} | {
name: Identifier
}
Curry-Howard同构告诉我们乘积类型像合取,求和类型像析取。
以下内容适用于布尔逻辑:
A & (B or C) implies (A & B) or (A & C)
因此,NamedNode的两个定义相等是合理的。这是缺乏分布性吗?
答案 0 :(得分:1)
{ name: PrivateName | Identifier; };
和{ name: PrivateName } | { name: Identifier }
仅在对象类型仅包含此属性的情况下才等效。常见的用例是联合的不同分支将拥有不同的类型。
无论如何,条件类型会distribute,但只能覆盖裸类型参数。每种类型的属性都不会发生魔术分布。并且由于关系NamedNode extends PrivateNamedNode
不成立,因此不会排除任何类型(由于NamedNode
不是联合,因此不能排除任何类型,即使关系成立,您也会得到never
,因为那么整个类型将被排除,您将一无所有)。
我们可以构建一个从对象属性中排除类型的类型:
type PrivateName = { kind: "PrivateName" };
type Identifier = { kind: "Identifier" };
type NamedNode = {
name: PrivateName | Identifier;
};
type PrivateNamedNode = {
name: PrivateName;
};
declare const privateName: PrivateName;
type ExcludePropertyTypes<T, TExclude extends Partial<T>> {
[P in keyof T]: Exclude<T[P], TExclude[P]>
}
type NamedNodeNonPrivate = ExcludePropertyTypes<NamedNode, PrivateNamedNode>;
// Errors now (as intended)
const x: NamedNodeNonPrivate = { name: privateName };