对TS有些陌生.我想键入一个返回类型为对象属性类型联合的函数,如下所示:

get({age:9,name:"Nick"})
// Return type should be number | string

我试过了:

let get = <T extends {age:number, name:string}>(x:T):T[keyof T]=>{  
   return x.age
}

x.age分的错误:

类型‘Numbers’不能赋给类型‘T[key of T]’

但是,当我从函数返回null as any时,函数的返回类型被正确推断.

为什么我会有这样的错误?

推荐答案

在处理generic个类型的值的操作时,编译器需要进行权衡.要么操作的结果可以保持泛型,这可能会产生非常精确和准确的类型,但这种复杂的泛型类型对于编译器来说很难分析;或者泛型类型可以首先扩大到其constraint,以便结果类型是特定的,这更容易分析,但可能会导致精度损失.编译器使用启发式规则来确定何时传播泛型以及何时扩大到具体.

例如,在内部

let get = <T extends { age: number, name: string }>(x: T) => {
  const age = x.age // what type is age?
  return age;
}

age应该是什么类型的?由于xT类型,并且您使用类型"age"的键对其进行索引,因此x的精确泛型类型是indexed access type T["age"].另一方面,我们知道x的类型是{age: number, name: string}的子类型.所以我们可以将x扩大到那个类型,在这种情况下,x.age的类型是{age: number, name: string}["age"],也就是number.因此,这里有两种明显的可能性:age要么保持泛型,属于T["age"]类型,要么被扩展为更直接可用的特定类型number.

编译器是做什么的?

let get = <T extends { age: number, name: string }>(x: T): T[keyof T] => {
  const age = x.age;
  // const age: number
  return age; // error! Type 'number' is not assignable to type 'T[keyof T]'.
}

它被扩大到number.这种行为在this comment/microsoft/TypeScript#33181中有记录,与您现在看到的问题类似.稍微转述一下:

属性访问和元素访问返回约束的对应属性类型,因此[x.age具有类型number],这就是[return语句]失败的原因.一旦你退回到一些具体的东西,你以后就不能用一般的东西来索引.

也就是说,当您返回age时,编译器将看到您返回了一个类型为number的值.不幸的是,number不一定可以赋值给T[keyof T],如下所示:

interface Centenarian {
  name: string,
  age: 100,
}
declare const oldManJenkins: Centenarian;
const g = get(oldManJenkins);
// const g: string | 100

A Centenarianage总是literal type 100,比number窄.在get()中,编译器将age从"不管T["age"]变成什么"扩展到number,并且number不能赋给string | 100(因为,比方说,99.5number,但不是string | 100).

这就是你得到错误的原因.


至于如何处理它,你可以做一些类似于微软/TypeScrip#33181中所示的事情.显式地对具有所需泛型类型的age变量进行annotate,因此编译器会提示不要将其扩大:

let get = <T extends { age: number, name: string }>(x: T): T[keyof T] => {
  const age: T['age'] = x.age; // okay
  return age; // okay
}

现在可以看到ageT['age']类型的,可以将其赋值给T[keyof T],并且该函数编译时没有错误.

Playground link to code

Typescript相关问答推荐

在TypScript手册中可以视为接口类型是什么意思?

无法从应用程序内的库导入组件NX Expo React Native

typescript抱怨值可以是未定义的,尽管类型没有定义

为什么从数组中删除一个值会删除打字错误?

基于平台的重定向,Angular 为16

具有继承的基于类的react 组件:呈现不能分配给基类型中的相同属性

部分类型化非 struct 化对象参数

在TypeScrip的数组中判断模式类型

打字类型保护递归判断

在包含泛型的类型脚本中引用递归类型A<;-&B

在函数中打字推断记录的关键字

在分配给类型的只读变量中维护const的类型

类型脚本可以推断哪个类型作为参数传递到联合类型中吗?

如何从MAT-FORM-FIELD-MAT-SELECT中移除焦点?

类型推断对React.ForwardRef()的引用参数无效

TypeScrip-根据一个参数值验证另一个参数值

使用&reaction测试库&如何使用提供程序包装我的所有测试组件

如何知道使用TypeScript时是否在临时工作流内运行

Typescript 子类继承的方法允许参数中未定义的键

TS 条件类型无法识别条件参数