定义回调函数类型

时间:2019-03-07 09:51:26

标签: typescript

是否可以定义一个比此类型更安全的函数?

public addBusinessRule(targetProperty: string,
                dependentProperties: string[],
                callback: (dep0: any, dep1: any, ...)): void {
   // some logic...
   callback(..dependentProperties)
};

我的目标是对最后2个参数进行类型检查。所以如果我传递这样的内容:

this.addBusinessRule('mortage',
                     ['purchasePrice', 'investiture', 'ownFunds'],
                     (purchase: number, investiture: number, ownFunds: number) => {
    // some calculations
});

任何人都知道我如何从第二个参数定义字符串数组的大小,该参数具有要匹配从回调函数接受的参数数量。

我能以某种方式使用泛型解决此问题,还是可能对此没有解决方案。

如果有人知道更好的更确切的问题标题,请请求编辑!

2 个答案:

答案 0 :(得分:2)

您可以使用映射的元组将属性的元组转换为属性类型的元组,然后将其用作回调的参数。这将确保回调最多可以具有与dependentProperties中的项目相同数量的参数。不会强制您指定所有参数(这是打字稿定义函数的类型兼容性的方式。)

type Data = {
    mortage: {
        purchasePrice: number,
        investiture: number,
        ownFunds: number,
        otherProp: string
    }
}
type MapTuple<T, K extends keyof T, NK extends Array<keyof T[K]>> = {
    [P in keyof NK]: NK[P] extends keyof T[K] ? T[K][NK[P]] : never
}
class Test {
    public addBusinessRule<K extends keyof Data, NK extends Array<keyof Data[K]>>(targetProperty: K,
        dependentProperties: NK | [], // Get the compiler to infer tuple types
        callback: (...a: MapTuple<Data, K, NK>) => void): void {
        // some logic...
        //callback(..dependentProperties)
    };
    public m() {
        this.addBusinessRule('mortage',
            ['purchasePrice', 'investiture', 'ownFunds', 'otherProp'],
            (purchase, investiture, ownFunds, op) => { // param types infered based on Data typr

            });
    }

}

魔术发生在MapTuple类型中。该类型使用mapped types(从3.1开始,它也支持元组和数组,请参见PR)。此类型采用一个元组NK,它是T[K]的键的元组,并在T[K]中查找每个属性的类型(这涉及条件类型,因为打字稿无法计算尽管可以保证NK[P]T[K]的键)

如果您只想检查参数的数量而不是参数的类型(尽管我认为这对API使用者来说是更糟糕的体验),则可以执行以下操作:

type MapTuple<NK extends Array<any>> = {
    [P in keyof NK]: any
}
class Test {
    public addBusinessRule<NK extends Array<string>>(targetProperty: string,
        dependentProperties: NK | [], // Get the compiler to infer tuple types
        callback: (...a: MapTuple<NK>) => void): void {
        // some logic...
        //callback(..dependentProperties)
    };
    public m() {
        this.addBusinessRule('mortage',
            ['purchasePrice', 'investiture', 'ownFunds', 'otherProp'],
            (purchase, investiture, ownFunds, op) => { // all of type any 

            });
    }
}

答案 1 :(得分:1)

一个简单的示例,仅适用于字符串型参数?

export const addBusinessRule = <T extends any[]>(targetProperty: string, dependentProperties: T, callback: (...a: T[]) => void) => {
  // some logic...
  callback(...dependentProperties)
}

addBusinessRule('asd', ['sdf', 'sdf', 'sdf'], (first, second, third) => {
  console.log('ABC')
})

它为每个参数提供类型。

如果我们要使用不正确的回调,则TS将引发错误。

const callback = (a: number, b: string, c: boolean) => {
  // ...
}

addBusinessRule('asd', ['sdf', 'sdf', 'sdf'], callback)
TS2345: Argument of type '(a: number, b: string, c: boolean) => void' is not assignable to parameter of type '(...a: string[][]) => void'.   Types of parameters 'a' and 'a' are incompatible.     Type 'string[]' is not assignable to type 'number'.