TypeScript 当对象包含上下文敏感函数时,T['prop']的keyof变为never,

9cbw7uwe  于 4个月前  发布在  TypeScript
关注(0)|答案(5)|浏览(54)

Bug报告

🔎 搜索词

不确定如何描述,但包括:

  • 递归类型
  • 函数
  • 参数
  • 泛型

🕗 版本与回归信息

在以下版本上进行过测试:

  • typescript@5.1.3
  • typescript@5.2.0-dev.20230716 dev

⏯ Playground链接

Playground链接

🙁 实际行为

在某些情况下,对象成员函数中参数的存在或缺失会影响该对象类型。

🙂 预期行为

它不应该影响。

wmtdaxz3

wmtdaxz31#

somehow,添加| {}使得推理工作再次进行。
Playground Link

cwdobuhd

cwdobuhd2#

当存在上下文敏感表达式时,出现了问题。
为了简化一些,以消除可能的误导:

type Type<TAcceptedKeys extends string, TType extends Type<TAcceptedKeys, any>> = {
  obj: Record<TAcceptedKeys, 1>;
  keys: (keyof TType['obj'])[];
  triggerFn: (param: number) => void;
}

declare function defineType<TAcceptedKeys extends string>(): <TType extends Type<TAcceptedKeys, TType>>(type: TType) => TType;

const im = defineType<'apple'>();

im({
  obj: {
    apple: 1
  },
  keys: [
    'apple' // type 'string' is not assignable to type 'never'
  ],
  
  // No error:
  // triggerFn: () => {},

  // Error:
  triggerFn: (param) => {}, 
});
s4n0splo

s4n0splo3#

当对象包含上下文敏感函数时,它被标记为不可推断。由于它是不可推断的,第一次推断过程会跳过它。然后 getInferredTypeunknown 分配为推断出的类型,并且由于 keyof unknownnever ,我们观察到当整个事物使用计算出的推断类型示例化时。

q3aa0525

q3aa05254#

当对象包含一个上下文敏感的函数时,它被标记为不可推断。由于它是不可推断的,第一个推断传递会跳过它。然后 getInferredTypeunknown 分配为推断出的类型,由于 keyof unknownnever ,我们观察到当整个东西使用计算出的推断类型示例化时。
@Andarist 这段话是否足以解释为什么 TType['obj'] 会正确推断,而 keyof TType['obj'] 不会?以这个代码片段为例,keys 将是 never ,但 objCopy 将是 { apple: 1; }

type Type<TAcceptedKeys extends string, TType extends Type<TAcceptedKeys, any>> = {
  obj: Record<TAcceptedKeys, 1>;
  keys?: (keyof TType['obj'])[];
  objCopy: TType['obj']
  triggerFn: (param: numbe...

Playground Link

zd287kbt

zd287kbt5#

是的,有点。这是我提到的一个意想不到的副产品。
在这两种情况下,目标类型在推理解析过程中变为 Type<"apple", unknown> ,然后检查当前源是否可以分配给它。如果不能,那么算法会提前返回,因为它认为继续是没有意义的。
对于你的 objCopy ,中间源类型是这个:{ obj: { apple: 1; }; objCopy: null; triggerFn: {}; }(注意 triggerFn 包含一个特殊的通配符~类型,只是将其字符串化为 {} )。如果我们看一下你的目标属性 TType['obj'] ,如果我们用 unknown 示例化 TType ,那么它也是 unknown ,而 null 可以分配给 unknown 。到目前为止一切顺利。
然而,对于你的 keys ,当我们进行相同的示例化时,你的 (keyof TType['obj'])[] 变成了 (keyof unknown)[] 。这可以简化为 never[] ,而你的源( string[] )不能分配给它。
我不是说这是它必须如何工作的方式,只是说它今天是如何工作的。

相关问题