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

create a function that returns a function that behaves in the same way as the passed in function

提问人:michaelmcandrew 提问时间:11/15/2023 更新时间:11/15/2023 访问量:22

问:

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

// 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

键入 '(...args: any[]) => any' 不可分配给类型 'T'。 '(...args: any[]) => any' 可分配给类型为 'T' 的约束,但 'T' 可以使用约束 'Function' 的不同子类型进行实例化。

这个消息对我来说有点难以理解。你能帮我理解它并提出一些解决这个问题的方法吗?谢谢!!

TypeScript 函数 泛型

评论


答:

2赞 jcalz 11/15/2023 #1

呼叫签名的问题,例如

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

是它声称无论如何都返回与输入类型完全相同的值。JavaScript 中的函数是第一类对象,也可以像任何其他对象一样具有属性。所以你可以这样做:func

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

下面是一个具有字符串值属性的函数。但是,您的实现不会尝试确保将 的每个属性都复制到输出中。因此,编译器会警告您,虽然返回的函数肯定是 ,但它可能不是 .如果忽略该错误,则可能会发生以下问题:sourcepropcreate()funcFunctionT

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.propcreate(source)create


做你正在做的事情的传统方法是明确地只关心输入的调用签名,而不是整个输入类型:也就是说,你希望函数在参数类型和返回类型列表中是泛型的:AR

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 代码链接

评论

0赞 michaelmcandrew 11/15/2023
这是一个非常清晰且写得很好的答案,它确实帮助我了解了正在发生的事情以及如何修复代码。非常感谢@jcalz!