gpt4 book ai didi

typescript - 如何创建严格的 Exclude 实用程序类型?

转载 作者:行者123 更新时间:2023-12-04 14:46:13 26 4
gpt4 key购买 nike

背景

我一直在使用 ExcludeExtract实用程序类型,但遇到过我只想匹配确切类型而不是子类型的情况。

到目前为止

我已经成功创建了一个 StrictExtract只提取完全匹配的类型的实用程序类型 - 尽管可能有更简单的方法来做到这一点?

type StrictExtract<T, U> =
T extends unknown ?
U extends T ?
T extends U ?
T :
never :
never :
never;

示例

type objOne = {
prop1: string;
prop2: number;
}

type objTwo = {
prop1: string;
prop2: number;
prop3: Function;
}
type ext1 = Extract<objOne | objTwo | string | number, string | number | objOne>
// string | number | objOne | objTwo

type stext1 = StrictExtract<objOne | objTwo | string | number, string | number | objOne>
// string | number | objOne

那么Extract在哪里呢?也会匹配 objTwo因为它是 objOne 的子类型, StrictExtract接受相同类型的参数,但只提取完全匹配的类型。

所以 StrictExclude将镜像 Exclude<Type, Union> 的输入参数, 但只会排除完全匹配的类型。

type excl = Exclude<objOne | objTwo | string | number, objOne | string | number>
// never

type strExcl = StrictExclude<objOne | objTwo | string | number, objOne | string | number>
// should result in objTwo

Playground

问题

我尝试使用相同的方法来计算 StrictExclude 的逻辑但现在一直在兜圈子。

我从以下实用程序开始,以了解每个条件的输出内容。我可以看到如何计算需要删除哪些类型 - 主要是通过使用 StrictExtract - 但不是如何从 T 的联合中删除那些确切的类型……(╯°□°)╯︵┻━┻

type StrictExclude<T, U> =
T extends unknown ?
(U extends T ?
(T extends U ?
(tok: T) => U :
(ux: U) => T) :
(u2x: U) => T) :
never;

问题

如何创建一个严格排除实用程序类型,它只删除完全匹配的类型,而不是子类型?

最佳答案

首先,您似乎希望 StrictExclude<T, U> 中的 Tdistribute 跨越 unions ,因此如果 TA | B | C ,则 StrictExclude<A | B | C, U> 相当于 StrictExclude<A, U> | StrictExclude<B, U> | StrictExclude<C, U> 。因此,作为第一步,我们可以将其写成分布式条件类型:

type StrictExclude<T, U> = T extends unknown ? StrictExcludeInner<T, U> : never;

在哪里 StrictExcludeInner<T, U>非联合 类型 T 执行所需的操作。 U 类型可能仍然是一个联合,我们需要仔细考虑在这种情况下该怎么做。目标是获取(非联合)T 并将其与 U 的每个联合元素进行比较;如果我们发现任何这样的元素,其中 TU可相互赋值(因此 T extends UU extends T 都为真),那么我们要返回 never 。另一方面,如果 T 不能与 U任何联合元素相互分配,那么我们要返回 T 。正是这种“相互可分配性”似乎就是您在 StrictExclude 中所说的“严格”的意思。

例如,假设 DEF 是不同的非联合类型,其中没有一对可以相互分配,那么 StrictExcludeInner<D, D | E> 应该是 never ,但是 StrictExcludeInner<F, D | E> 应该是 F

我们可以这样写:

type _StrictExcludeInner<T, U> = 0 extends (
U extends T ? [T] extends [U] ? 0 : never : never
) ? never : T;

让我们首先检查其中的中间部分:

U extends T ? [T] extends [U] ? 0 : never : never

这是 U 中的分布式条件类型。 (我通过编写 T 而不是 [T] extends [U] 抑制了 T extends U 中联合的分配性,但是由于我们期望 T 不是联合,所以它并不重要。) U 的每个联合元素都可以与 T 相互分配将结束将 0 贡献给最终类型,而 U 中不能与 T 相互分配的每个元素最终都会将 never 贡献给最终类型。由于 0 | never0 ,因此当且仅当 U extends T ? [T] extends [U] ? 0 : never : never 的至少一个元素可与 0 相互分配时,U 将计算为 T。否则它将计算为 never

现在让我们看看 StrictExcludeInner<T, U> 的完整类型。如果 U 至少有一个与 T 相互分配的元素,则它将计算为 0 extends (0) ? never : T ...因为 0 extends 0 为真,这将根据需要计算为 never 。另一方面,如果 U 没有与 T 相互赋值的元素,那么它将计算为 0 extends (never) ? never : T 。由于 0 extends never 为假,这将计算为 T ,同样符合要求。


好的,让我们用你的例子来测试它:

type strExcl = StrictExclude<objOne | objTwo | string | number, objOne | string | number> // objTwo

type six = StrictExclude<MyClassOne | MyClassTwo, MyClassOne | MyClassTwo> // never
type seven = StrictExclude<MyClassOne | MyClassTwo | string, MyClassOne | MyClassTwo> // string
type eight = StrictExclude<MyClassOne | MyClassTwo | string, MyClassOne> // string | MyClassTwo
type nine = StrictExclude<MyClassOne | MyClassTwo | string, MyClassTwo> // string | MyClassOne

这些都评估为您期望的类型。万岁!

Playground link to code

关于typescript - 如何创建严格的 Exclude 实用程序类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69994553/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com