You can use mapped tuples to transform the tuple of properties into a tuple of property types which can then be used as the arguments to the callback. This will ensure the callback can have at most the same number of parameters as items in dependentProperties. It will not force you to specify all arguments (this is how typescript defines type compatibility for functions).
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
});
}
}
The magic happens in the MapTuple type. The type uses mapped types (which since 3.1 support tuples and arrays as well, see PR). This type takes a the tuple NK which is a tuple of keys of T[K], and looks up the type of each property in T[K] (this involves a conditional type because typescript can't figure out NK[P] is a key of T[K] although it is guaranteed to be)
If you just want the number of parameters to be checked and not their type (although I would argue that is a worse experience for your API consumers) you can do the following:
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
});
}
}
'mortage'that is an object with the properties['purchasePrice', 'investiture', 'ownFunds'](an maybe other) ?