繁体   English   中英

Typescript 动态 function 按 object 的键/值

[英]Typescript dynamic function by key/value of object

有没有办法基于初始接口设置 function? 我有一堆 listOfFunctions 和等量的包含函数类型的接口。 我想创建一个返回自定义钩子的工厂。 这个想法是节省大量代码,并使编辑和添加功能到所有钩子变得轻而易举。

FunctionInterface是所有 function 名称及其返回值。

listOfFunctions是一个设置 function (实际代码有一个配置),它返回一个带有函数列表的 object。

theChosenFunction是一个 function 将在允许我与 listOfFunction 函数之一交互的钩子中返回。 这意味着参数(如果需要)。 实际应用程序中的函数是 Promises,它将设置钩子的 state。 在这个例子中,我只返回一个值和 setState。

A)这可能与funcObject[funcName]()并创建一个 function 以使用 typescript 调用吗? 这将需要添加或不添加参数的能力。

B) 有没有办法获取 function 类型的返回值? 所以在这个例子中: type CanThisWork = () => string; 我想提取string 这个想法不是对所有 listOfFunctions 和 FunctionInterfaces 进行重构。 他们有很多。 如果我必须创建一个复杂的工厂,那么我发现这更经济。

interface FunctionInterface {
  noParamsNoReturn: () => void;
  noParamsNumberReturn: () => number;
  propsAndNoReturn: (id: string) => void;
  propsAndReturn: (id: string) => string;
}

const listOfFunctions = (): FunctionInterface => {
  return {
    noParamsNoReturn: () => void,
    noParamsNumberReturn: () => 1,
    propsAndNoReturn: (id: string) => console.log(id),
    propsAndReturn: (id: string) => id,
  }
}

function runThis<T>(funcName: keyof T, funcObject: T): void {
  const [value, setValue] = React.useState();

  // Can I have typescript set up the params here?
  const theChosenFunction = ();
  const theChosenFunction = (props) => {
    // Can I have typescript invoke the function properly here?
    const result = funcObject[funcName](); // funcObject[funcName](props)
    if (result) {
       setValue(result);
    }
  }
  return {
    theChosenFunction
  }
}


const result = runThis<FunctionInterface>('noParamsNoReturn', listOfFunctions());

result.theChosenFunction()
// OR
result.theChosenFunction('someId');

有内置的Parameters<T>ReturnType<T>实用程序类型,它们采用 function 类型T并使用条件类型推断来分别提取参数元组和返回类型:

type SomeFuncType = (x: string, y: number) => boolean;
type SomeFuncParams = Parameters<SomeFuncType>; // [x: string, y: number]
type SomeFuncRet = ReturnType<SomeFuncType>; // boolean

至于你的runThis() function,我倾向于给它以下类型和实现,假设你想要编译和运行的最小更改:

function runThis<K extends PropertyKey, F extends Record<K, (...args: any[]) => any>>(
  funcName: K, funcObject: F
) {
  const [value, setValue] = React.useState();
  const theChosenFunction = (...props: Parameters<F[K]>): void => {
    const result = funcObject[funcName](...props);
    if (result) {
      setValue(result);
    }
  }
  return {
    theChosenFunction
  }
}

我给了 function 两个泛型类型参数: K ,对应于funcName的类型,和F ,对应于funcObject的类型。 约束K extends PropertyKeyF extends Record<K, (...args: any[])=>any>保证funcName将是类似键的类型( stringnumbersymbol ),并且funcObject将在该键处具有函数值属性。

然后,我们可以使theChosenFunction使用spread 和rest 语法,以允许使用可变参数列表调用function。 所以(...props) => funcObject[funcName](...props)将接受任意数量的参数(包括零)并将它们传递给被调用的 function。 TypeScript 将此类参数列表表示为元组类型。 因此,让theChosenFunction的调用签名看起来像(...props: Parameters<F[K]>) => void意味着它将接受与键funcName处的funcObject中的条目相同的参数,并且它不会output 任何东西(因为您的实现没有 output 任何东西)。


让我们看看它是否有效:

const result = runThis('noParamsNoReturn', listOfFunctions());
result.theChosenFunction(); // okay
result.theChosenFunction('someId'); // error! Expected 0 arguments, but got 1.

const anotherResult = runThis('propsAndReturn', listOfFunctions());
anotherResult.theChosenFunction(); // error! Expected 1 arguments, but got 0.
anotherResult.theChosenFunction("someId"); // okay

runThis(listOfFunctions(), 'someId'); // error! '
// FunctionInterface' is not assignable to 'string | number | symbol'.
runThis('foo', listOfFunctions()); // error!  
// Property 'foo' is missing in type 'FunctionInterface'

runThis(
  'bar', 
  { bar: (x: string, y: number, z: boolean) => { } }
).theChosenFunction("hey", 123, true); // okay

在我看来很好。

Playground 代码链接

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM