我正try 在代码库中键入一个遗留函数,该函数调用某些对象上的方法(并应用错误处理等).下面的部分(玩具)解决方案适用于约束action类型,但不能给出准确的返回类型(返回any).

entityGetter函数应该接受EntityA或EntityB,并且只接受以'get'开头的可调用键.后一部分工作正常,这告诉我获得正确的返回值必须是可能的;但是我不明白为什么action在访问entity时没有正确的类型.

我已经try 为T和A赋值默认值,这似乎不会改变任何东西.

是否有可能在不对entityGetter函数的(运行时)接口进行任何更改的情况下使其工作?理想情况下,根本不需要更改JS,但允许对函数体进行一些更改.

type TEntityA = {
  getA: () => string;
  getB: () => string;
  setC: (arg0: string) => string;
  valD: number;
  getE: () => object;
};
type TEntityB = {
  getA: () => string;
  getB: () => number;
  setC: (arg0: string) => string;
  valD: string;
};
type TEntities = TEntityA | TEntityB;

// type KeysOfType = https://stackoverflow.com/a/64254114

type TGetterKeys<T extends TEntities> = keyof Pick<T, keyof T & (`get${string}`)>;
type TCallableKeys<T extends TEntities> = KeysOfType<T, () => unknown>;
type TGetters<T extends TEntities> = TGetterKeys<T> & TCallableKeys<T>;

const entityGetter = <
  T extends TEntities,
  A extends TGetters<T>
>(action: A, entity: T) => entity[action]();
// This expression is not callable. Type 'unknown' has no call signatures.


const entityA = new EntityA();
const entityB = new EntityB();

const AgetA = entityGetter('getA', entityA); // => string
const BgetB = entityGetter('getB', entityB); // => number

const AgetX = entityGetter('getX', entityA);
// Argument of type '"getX"' is not assignable to parameter of type '"getA" | "getB" | "getE"'.

Playground

推荐答案

TS不明白T[A]总是某种函数.所以首先,我们需要说服它在这里(entity[action] as Function)(),以确保这是可调用的.但是ReturnType<Function>any,所以我们还必须为entityGetter定义ReturnType.

出于与上述相同的原因,简单的ReturnType<T[A]>不起作用.TS不能预测(in this generic scenario, bef或e it's called with explicit values and types) T[A]是可调用的,所以它拒绝这样做. 因此,我们必须手动完成:T[A] extends () => infer R? R: never.对于TS来说,这现在是可以预测的.

我也会总结出TGetters个.

type TGetters<T> = {
  [K in keyof T]: T[K & `${"get" | "is"}${string}`] extends (...args: any) => any ? K : never;
}[keyof T];

const entityGetter = <
  T extends TEntities,
  A extends TGetters<T>
>(action: A, entity: T): T[A] extends () => infer R ? R : never =>
  (entity[action] as Function)();

type KeysOfType<T, U> = keyof T & keyof {
  [K in keyof T as T[K] extends U ? K : never]: 1
};

const entityGetter = <
  T extends TEntities,
  A extends KeysOfType<T, () => unknown> & `${"get" | "is"}${string}`
>(action: A, entity: T): T[A] extends () => infer R ? R : never =>
  (entity[action] as Function)();

TS Playground

Sidenote: T extends TEntities acts as a guard, preventing you from calling this function with any object that is not TEntityA | TEntityB even if follows the general pattern (object with getter functions starting with getis). F或 the typing a simple T would suffice.

Typescript相关问答推荐

如何使打包和拆包功能通用?

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

Angular 行为:属性类型从SignalFunction更改为布尔

在NextJS中获得Spotify Oauth,但不工作'

我可以为情态车使用棱角形的路由守卫吗?

表单嵌套太深

使用2个派生类作为WeakMap的键

Fp-ts使用任务的最佳方法包装选项

尽管对象的类型已声明,但未解析的变量<;变量

打字错误推断

如何将类似数组的对象(字符串::匹配结果)转换为类型脚本中的数组?

为什么特定的字符串不符合包括该字符串的枚举?

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

使用或属性编写无限嵌套的接口

从以下内容之一提取属性类型

三重融合视角与正交阵

我的类型谓词函数不能像我预期的那样工作.需要帮助了解原因

如何键入';v型';

在 next.js 13.4 中为react-email-editor使用forwardRef和next/dynamic的正确方法

node_modules/@newrelic/native-metrics:命令失败. node_modules/couchbase:命令失败.退出代码:127