因此,我有了这个工厂模式,我可以在其中创建具有正确类型的服务类的新实例.为此,我需要一个提取单个密钥的类型:

type ServiceSingleKeys<T> = [T] extends (
  T extends 'foo' | 'oof' ? [T] : never
)
  ? T
  : never;

上面的代码来self 找到的一个代码片段,但没有解释这个代码片段.遗憾的是,我丢失了指向该代码片段的链接,因此无法在此提供该链接.

下面是一个更详细的复制品:https://stackblitz.com/edit/typescript-ha1bha?file=index.ts

[T]是做什么的?(在 comments 中回答--&gt;Tuple)

如果首先判断[T]是否扩展到(T extends ServiceKeys ? [T] : never),会达到什么效果?这是如何工作的,或者换句话说,这是在做什么?在我的理解中,我可以用这个来达到同样的效果

type ServiceSingleKeys<T> = T extends 'foo' | 'oof' ? T : never;

推荐答案

代码

type ServiceSingleKeys<T> = [T] extends (
  T extends 'foo' | 'oof' ? [T] : never
) ? T : never;

具有判断整个输入类型T是否可分配给union type "foo" | "oof"的效果.如果是,则返回T.如果不是,则返回never.所以你会得到这样的行为:

type A = ServiceSingleKeys<"foo" | "oof"> // "foo" | "oof"
type B = ServiceSingleKeys<"foo"> // "foo"
type C = ServiceSingleKeys<"foo" | "bar"> // never

类型AB与它们的输入相同,因为"foo" | "oof""foo"都可以赋给"foo" | "oof".类型Cnever,因为"foo" | "bar"not可赋值给"foo" | "oof".


这可以更简单地重写为:

type ServiceSingleKeys<T> = [T] extends ['foo' | 'oof'] ? T : never;

type A = ServiceSingleKeys<"foo" | "oof"> // "foo" | "oof"
type B = ServiceSingleKeys<"foo"> // "foo"
type C = ServiceSingleKeys<"foo" | "bar"> // never

事实上,如果我需要的话,这就是我写这样一个类型的方式.我说不出为什么原版是这样写的.我的guess%是通过试验和错误组装而成的.(如果事实证明这两个版本在功能上不等价,那么maybe那个边缘情况就是原因,但没有更多的信息,我对此持怀疑态度).

在不更改行为的情况下,您无法从支票中移除tuple type包装器:

type NotServiceSingleKeys<T> = T extends 'foo' | 'oof' ? T : never;

type A = NotServiceSingleKeys<"foo" | "oof"> // "foo" | "oof"
type B = NotServiceSingleKeys<"foo"> // "foo"
type C = NotServiceSingleKeys<"foo" | "bar"> // "foo" <-- difference

这是因为选中的类型是纯泛型类型参数的条件类型是distributive conditional type,其中输入类型在求值之前被分解为其单个联合成员,而输出被联接到新的联合中.所以NotServiceSingleKeys<"foo" | "bar">的计算结果是NotServiceSingleKeys<"foo"> | NotServiceSingleKeys<"bar">,它变成了"foo" | never,或者仅仅是"foo".

分布式条件类型通常是人们想要看到的(这允许您获得像the Extract<T, U> utility type这样的联合过滤行为),但是当它不受欢迎时,修复方法通常是将extends判断的两个方面都包装在一个元组中.判断[T] extends ['foo' | 'oof']是非分配的,因为[T]不是纯泛型类型参数,但是判断是相似的,因为元组是协变的(这意味着当且仅当XXX可分配给YYY时,[XXX]才是可分配给[YYY]的).正如上面链接的documentation中所说的:

通常,分布性是所需的行为.为了避免这种行为,您可以用方括号将extends关键字的两边括起来.

同样,我在这里的guess点是,代码的原始版本是一种反复try 的try ,目的是应用该建议,以防止判断意外分发.如果问题中的特定嵌套条件类型还有其他目的,我看不出来.


Playground link to code

Typescript相关问答推荐

物料UI图表仅在悬停后加载行

判断对象A中存在的对象B的键是否存在对象A中键的另一个数组值中

使用FormArray在Angular中添加排序

使用Angular和API请求在CRUD操作后更新客户端数据的良好实践

Redux—Toolkit查询仅在不为空的情况下添加参数

如何提取密钥及其对应的属性类型,以供在新类型中使用?

隐式键入脚本键映射使用

我可以使用TypeScrip从字符串词典/记录中填充强类型的环境对象吗?

Angular 15使用react 式表单在数组内部创建动态表单数组

为什么在leetcode问题的测试用例中,即使我确实得到了比预期更好的解决方案,这段代码也失败了?

如何在Vue中使用Enum作为传递属性的键类型?

有没有一种方法可以确保类型的成员实际存在于TypeScript中的对象中?

为什么上下文类型在`fn|curred(Fn)`的联合中不起作用?

如何将spread operator与typescripts实用程序类型`参数`一起使用

Angular 16将独立组件作为对话框加载,而不进行布线或预加载

字符串文字联合的分布式条件类型

如何在本地存储中声明该类型?

在打印脚本中将对象类型转换为数组

TypeScript中的这些条件类型映射方法之间有什么区别?

如何避免TS2322;类型any不可分配给类型never;使用索引访问时