首页 > 解决方案 > 验证一个参数(它是一个函数)可以用同级参数调用

问题描述

我正在编写一些高阶函数,并且发现很难编写类型来验证作为参数提供的函数可以与提供的其他参数一起调用。这是一个超级基本的例子:

function(myFunc, arg1, arg2) {
    return myFunc(arg1, arg2) // We want to verify that this is typesafe at compile time
}

还有一个更现实的例子:

// Here's a higher order function that returns a function that alters input in some way
type algorithm = (input: number, options?: any) => number;
const createHandler = (algorithm: algorithm, options?) =>
  (input: number) => algorithm(input, options);

// This algorithm needs no 'options' configuration
const addOne = (input) => input + 1;
// This algorithm requires a specific 'options' configuration
interface PowerOptions { value: number; }
const power = (input: number, {value}: PowerOptions) => input ** value

// Now when I create these handlers, I'd like for the 'options' to be validated against the provided function
const addOneHandler = createHandler(addOne);
const squaredHandler = createHandler(power, { value: 2 });
const cubedHandler = createHandler(power, {});  // This should show a type error as the options are invalid for the algorithm

标签: typescripttypescript-typings

解决方案


关于您的基本示例,您可以通过类型推断来检查所提供函数的类型安全性(在 Typescript Playground 上检查):

const a = function <U, V>(myFunc: (arg1: U, arg2: V) => any, arg1: U, arg2: V) {
    return myFunc(arg1, arg2)
}

const doubleIfTrue = (arg1: number, arg2: boolean) => arg2 ? 2 * arg1 : arg1

console.log(a(doubleIfTrue, 1, true))  // Type OK
console.log(a(doubleIfTrue, 1, "hop")) // Wrong type: number and string provided

在这种情况下,U根据V提供的函数的参数推断类型。


但是您想要实现的目标会变得更加复杂。根据您的代码,我可以整理出一些东西(在 Typescript Playground 上查看):

type algorithm<OPT = null> = (input: number, options?: OPT) => number;
type algorithmOptions<A> = A extends algorithm<infer OPT> ? OPT : null

const createHandler = <A extends algorithm<any>>(algorithm: A, options: algorithmOptions<A>) =>
(input: number) => algorithm(input, options);

// Algorithms
const addOne:algorithm = (input: number) => input + 1;
interface PowerOptions { value: number; }
const power:algorithm<PowerOptions> = (input: number, {value}) => input ** value

// Handlers
const squaredHandler = createHandler(power, { value: 2 }); // correct
const addOneHandler = createHandler(addOne, null); // correct if a second argument is provided

const addOneHandlerFailing = createHandler(addOne, { value: 2 }); // wrong because an argument is provided
const squaredHandlerFailing1 = createHandler(power, {}); // wrong because of argument interface not respected
const squaredHandlerFailing2 = createHandler(power); // wrong because no argument provided

使用一些条件类型来检索算法参数。但也许我走得太远了,你可以找到一种更简单的方法

另一件事:据我所知,在某些情况下,options 参数似乎createHandler不能是可选的,而在另一些情况下是强制性的,而不会将示例变得更复杂。

我希望它有帮助!


推荐阅读