在TypeScript中约束函数参数类型,用于创建返回目标函数的管道



我正在构建一个函数管道,它创建一系列检查/保护,然后接受一个函数并返回一个函数,该函数要么提前返回,要么调用被接受的函数。像这样:

// A generic function type. The goal is to have the pipeline work with any type of function
interface GoalFunctionType { (): string }
// Pipeline instantiation and usage `checkSomething` and `provideName` are example functions in the pipeline, they are not implemented here.:
const p = new FunctionPipeline<GoalFunctionType>()
const passedInFunction = ({ name }: { name: string }) => "Hello " + name
const goalFunction: GoalFunctionType = p.checkSomething().provideName().finally(passedInFunction);

沿途的管道检查可以触发finally的早期返回。他们可以选择为传递给finally的函数创建额外的参数,就像上面的provideName一样(但下面的实现还没有到那一步)。

我被finally函数的类型检查器卡住了。我希望类型检查器确保传入

的函数
  • GoalFunctionType
  • 具有相同的返回类型
  • 接受与GoalFunctionType相同的参数
  • 接受额外的,管道生成的参数作为第一个参数中的命名参数(此处未实现)

下面是一个最小的实现(CodeSandbox),它不会编译没有错误/警告:

class FunctionPipeline<FunctionType extends (...args: any[]) => any> {
finally(
fn: (...args: Parameters<FunctionType>) => ReturnType<FunctionType>
): FunctionType {
return (...args) => {
return fn(...args);
};
}
}
interface LoaderFunction {
({ name }: { name: string }): string;
}
const goalFunction = new FunctionPipeline<LoaderFunction>().finally(
({ name }) => {
const result = `Hello ${name}`;
console.log(result);
return result;
}
);
const app = document.getElementById("app");
if (app) app.innerHTML = goalFunction({ name: "World" });

要实现管道生成的参数,finally函数应该更像这样,并希望具有特定的类型:

fn: (pipelineArgs: GeneratedArgsType, ...args: Parameters<FunctionType>) => ReturnType<FunctionType>
): FunctionType {
return (...args) => {
// example: this.generatedArgs == { name: "Nathan" };
return fn(this.generatedArgs, ...args);
};
}

functionPipeline.finally方法有两个编译错误。

第一次返回错误:

Type '(...args: any[]) => ReturnType<FunctionType>' is not assignable to type 'FunctionType'.
'(...args: any[]) => ReturnType<FunctionType>' is assignable to the constraint of type 'FunctionType', but 'FunctionType' could be instantiated with a different subtype of constraint '(...args: any[]) => any'.

第二次返回错误:

(parameter) args: any[]
Argument of type 'any[]' is not assignable to parameter of type 'Parameters<FunctionType>'.

你能帮我找出合适的类型来实现我的目标吗?以下是CodeSandbox中的最小示例。如果你想看更多的代码,看看这个更长的例子,它提供了更多的应用程序上下文和在Remix框架中的用法。

您的版本正在产生一个错误,因为当您调用finally()时,不能保证FunctionPipeline<F>可以产生一个泛型F的值。F可以是任何函数类型的任何子类型,包括带有额外属性的函数:

function foo(x: string) {
return x.length;
}
foo.strProp = "hey";
const gf = new FunctionPipeline<typeof foo>;
gf.finally(foo).strProp.toUpperCase() // no compiler error, but:
// 💥 RUNTIME ERROR 💥 gf.finally(...).prop is undefined

在运行时爆发,因为typeof foo已知具有strProp属性,而您的finally()实现没有。哎呀。


你其实并不关心函数类型F;只有它的参数元组类型和返回类型。如果您在调用new FunctionPipeline<F>()时不依赖于手动指定类型参数,那么我认为您应该重构以使用您关心的两种类型:

class FunctionPipeline<A extends any[], R> {
finally(
fn: (...args: A) => R
): (...args: A) => R {
return (...args) => {
return fn(...args);
};
}
}
const goalFunction = new FunctionPipeline<[{ name: string }], string>().finally(
({ name }) => {
const result = `Hello ${name}`;
console.log(result);
return result;
}
);

这更直接,因为,例如,finally()产生(...args: A) => R类型的函数。


但是,假设这是不可接受的,另一种方法是从F中计算AR,并完全抛弃F。这样的:

class FunctionPipeline<F extends (...args: any[]) => any> {
finally(
fn: (...args: Parameters<F>) => ReturnType<F>
): (...args: Parameters<F>) => ReturnType<F> {
return (...args) => {
return fn(...args);
};
}
}

因此,FunctionPipeline<F>finally()方法返回类型为(...args: Parameters<F>) => ReturnType<F>的值,而不声明返回类型为F的值。这解决了我在foo:

中显示的问题。
function foo(x: string) {
return x.length;
}
foo.strProp = "hey";
const gf = new FunctionPipeline<typeof foo>;
gf.finally(foo).strProp // compiler error! 
// Property 'strProp' does not exist on type '(x: string) => number'

现在编译器不认为gf.finally(foo)strProp属性。您的示例代码也可以工作:

const goalFunction = new FunctionPipeline<LoaderFunction>().finally(
({ name }) => {
const result = `Hello ${name}`;
console.log(result);
return result;
}
);

Playground链接到代码

相关内容

  • 没有找到相关文章

最新更新