typescript 创建一个函数,该函数返回与传入函数行为相同的函数

lztngnrs  于 2023-11-20  发布在  TypeScript
关注(0)|答案(1)|浏览(203)

我试图创建一个函数,它接受一个函数作为参数,并返回一个与传入函数行为相同的函数(作为一些更复杂的业务逻辑的核心)。

// a function that returns a function that behaves
// in the same way as the passed in function.
export function create<T extends Function>(func: T): T {
  function run(...args: any[]) {
    // Later I plan on doing some stuff here...
    return func(...args);
    // ...and here
  }
  return run;
}

const source = (n:number) =>  n.toString();
const created = create(source);

字符串
上面的代码工作了,我在created()const created: (n: number) => string)上得到了正确的类型注解和完成,但是typescript抱怨说,
Type '(. args:any[])=> any'不能赋值给类型' T '.'(... args:any[])=> any'可以赋值给类型' T '的约束,但是'T'可以用约束' Function '的不同子类型示例化。
这条信息对我来说有点费解。你能帮我理解它并建议一些方法来解决这个问题吗?谢谢!!

m1m5dgzv

m1m5dgzv1#

像这样的呼叫签名的问题是,

function create<T extends Function>(func: T): T { /* ✂ ⋯ ✂ */ }

字符串
它声明返回一个与func输入完全相同类型的值,不管是什么。Functions in JavaScript是一级对象,也可以像其他对象一样拥有属性。所以你可以这样做:

const source = (n: number) => n.toString();
source.prop = "hello";
/* const source: {
  (n: number): string;
  prop: string;
} */


这里source是一个具有字符串值prop属性的函数。但是create()的实现并没有尝试确保func的每个属性都复制到输出中。因此编译器警告您,尽管返回的函数肯定是Function,它可能不是T。如果忽略该错误,则可能发生以下问题:

const created = create(source);
console.log(created(123)) // "123", this is good
try {
  console.log(created.prop.toUpperCase()) // no compiler error, but
} catch (e) {
  console.log(e) // 💥 RUNTIME ERROR: created.prop is undefined
}


编译器会很高兴地允许你访问create.prop,因为create(source)应该返回与create完全一样的东西。
传统的方法是只显式地关心输入的调用签名,而不是整个输入类型:也就是说,你希望函数在参数类型A和返回类型R的列表中是generic

function create<A extends any[], R>(func: (...args: A) => R): (...args: A) => R {
  function run(...args: A) {
    return func(...args);
  }
  return run; // okay
}


现在编译时没有错误。你仍然可以将带有额外属性的函数传递到create()中,但是现在输出值不会假装有这些额外属性,所以你会得到一个编译器错误,这将帮助你避免运行时错误:

const source = (n: number) => n.toString();
source.prop = "hello"; 
const created = create(source);
console.log(created(123)) // "123" // this is good
created.prop; // compiler error 
//      ~~~~
// Property 'prop' does not exist on type '(n: number) => string'


Playground链接到代码

相关问题