通过 属性 从对象获取函数结果或值

Get function result or value from an object by property

TS Playground

我正在尝试找出实用函数 getProp 的正确类型,该函数将通过键和 return 该索引处的原始值或函数的结果对对象进行索引该指数。

declare interface App {
  name: () => string
  version: string
  id: number
}

const getProp = function<C extends App, S extends keyof App>(context: C, prop: S) : C[S] extends (...args: any[]) => any ? ReturnType<C[S]> : C[S] {
  const method = context[prop];
  if (typeof method === 'function') {
    return method(); // Error: Type 'string' is not assignable to type 'C[S] extends (...args: any[]) => any ? ReturnType<C[S]> : C[S]'.
  } else {
    return method; // Error: Type '() => string' is not assignable to type 'C[S] extends (...args: any[]) => any ? ReturnType<C[S]> : C[S]'.
  }
};

declare var app : App;

// These are all correct despite the errors
getProp(app, "id") // => number
getProp(app, "name") // => string
getProp(app, "version") // => string

此处显示的类型“有效”,但 TypeScript 在指示的行上给我错误。有没有更好的方法来正确地输入这个而不会出错,或者我应该直接把我的老朋友放在一起 //@ts-ignore?

当我们使用 Generic Functions 时,实际类型实例化发生在我们调用它时,getProp<App, 'name'>

因此 Typescript 编译器需要确保我们使用的类型必须兼容并遵循约束。

由于 getProp 的 Return 类型是有条件的,我们可以使用 assertion/aliasing 通知打字稿编译器该分支将到达的状态

在您的代码中,编译器抱怨您可能在声明时 return 正在编写某些代码(可能与 return 值不匹配)!

因此通过别名,我们确保类型将是这种 Return类型!

为了避免重复我创建了一个新的类型别名PropReturnType<T>

declare interface App {
  name: () => string
  version: string
  id: number
}

type PropReturnType<T> = { [K in keyof T]: T[K] extends (...args: any[]) => any ? ReturnType<T[K]> : T[K] }


const getProp = function<C extends App, S extends keyof C>(context: C, prop: S): PropReturnType<C>[S]{
  const method = context[prop];
  if (typeof method === 'function') {
    return method();
  } else {
    return method as PropReturnType<C>[S];
  }
};



declare var app: App;

getProp(app, "id") // => number
getProp(app, "name") // => string
getProp(app, "version") // => string

CODE PLAYGROUND