例如,我想了解为什么给定一个对象类型

type Obj = {
  readonly color: string;
};

以及获取其 colored颜色 的函数

  const getColor = <const T extends Obj>(obj: T) => obj.color;
  getColor({color:"blue"});

obj.color的类型是string,而不是默认的T["color"].要获得实际值,我们必须显式地键入返回.

  const getColor = <const T extends Obj>(obj: T):T['color'] => obj.color;

但是,如果我们有一些更一般的函数来获取给定关键字的任何属性的值:

  const getPropertyValue = <const T extends Obj, K extends keyof T>(obj: T, key:K) => obj[key];

那么obj[key]的类型就是T[K].

所以我猜这不是一个类型安全问题,或者Typescript 无法识别类型. 所以我想知道这样做是否有原因,以及是否有某种方法,除了显式地键入返回,以索引访问的形式获取返回.

这是可行的,但可能没有必要.

  const getPropertyValue = <const T extends Obj, K extends keyof T>(obj: T, key:K) => obj[key];
  const getColor = <const T extends Obj>(obj:T)=>getPropertyValue(obj,"color")
  const c1 = getColor({color:"blue"}); //returns blue 

我可以认为这可能不是一个泛型问题,因为如果不访问属性并且只返回值,类型就不会扩大.

这里有一个指向typescript playground的链接,带有这个代码.

推荐答案

我不知道它是否有explanation,但无论是好是坏,使用non-generic类型K的键索引到泛型类型T extends Constraint的对象将得到值non-generic类型Constraint[K]而不是T[K]the generic type is widened to its constraint before indexing是真的:

function foo<T extends Constraint>(t: T) {
  const val = t.x; // const val: string
}

这在GitHub问题中被提到了几个地方,比如microsoft/TypeScript#33181上的this comment,或者microsoft/TypeScript#54160上的this comment.

它并没有真的说why是这样的.大概是因为这样做很方便:它使类型保持简单(有时是too简单),这有助于提高编译器性能.但这主要是我的猜测,而不是任何权威的解释.


一般来说,当默认打字类型的启发式行为不受欢迎时,type annotation是一个合理的方法:

function foo<T extends Constraint>(t: T) {
  const val: T["x"] = t.x; // const val: T["x"]
}

是的,这是多余的,但它是有效的,所以它可能是你将得到的最好的.如果出于某种原因,您需要太多这样的索引,这会成为一个问题,您可以将"泛型索引"包装为一个函数并使用它:

interface Wrap<T> {
  get<K extends keyof T>(k: K): Wrap<T[K]>;
  val: T
}
const wrap = <T,>(t: T): Wrap<T> => ({
  get(k) { return wrap(t[k]) },
  val: t
})

function foo<T extends Constraint>(t: T) {
  const val = wrap(t).get("x").val // const val: T["x"]
}

interface DeepConstraint {
  a: { b: { c: { d: { e: { f: { g: number } } } } } }
}
function bar<T extends DeepConstraint>(t: T) {
  const val = wrap(t).get("a").get("b").get("c").get("d")
    .get("e").get("f").get("g").val;
  // const val: T["a"]["b"]["c"]["d"]["e"]["f"]["g"]
}

但这似乎并不值得.

Playground link to code

Typescript相关问答推荐

如何在另一个if条件中添加if条件

错误:note_modules/@types/note/globals.d.ts:72:13 -错误TS 2403:后续变量声明必须具有相同的类型

如何修复VueJS中的val类型不能赋给函数

脉轮ui打字脚本强制执行图标按钮的咏叹调标签-如何关闭

如果我有对象的Typescript类型,如何使用其值的类型?

泛型函数类型验证

如何在函数参数中使用(或模仿)`success`的行为?

Angular中的其他服务仅获取默认值

如何在第一次加载组件时加载ref数组

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

Angular 信号:当输入信号改变值时,S触发取数的正确方式是什么?

如何使用WebStorm调试NextJS的TypeScrip服务器端代码?

如何创建由空格连接的多个字符串的类型

将超类型断言为类型脚本中的泛型参数

TypeScrip-如何自动推断UNION变量的类型

回调函数中的TypeScrip类型保护返回Incorect类型保护(具有其他未定义类型的返回保护类型)

无法缩小深度嵌套对象props 的范围

为什么数字是`{[key:string]:UNKNOWN;}`的有效密钥?

为什么`map()`返回类型不能维护与输入相同数量的值?

组件使用forwardRef类型脚本时传递props