英文:
Enforce function parameter length to be same as array / tuple length
问题
我正在编写一个函数,它接受两个参数,A 和 B。
A 是一个长度为 L 的字符串数组。
B 是一个接受 L 个数字作为参数并返回一个数字的函数。
```javascript
const acceptSameLength = (stringArray: string[], fnc: (...inputNums: number[]) => number) => ({
names: stringArray,
calcFunc: fnc
})
我想要确保两者之间的 L 保持一致。
acceptSameLength(['foo'], (a: number) => 123) // 应该正常工作
acceptSameLength(['foo'], (a: number, b: number) => 123) // 应该报错
acceptSameLength(['foo', 'bar'], (a: number) => 123) // 应该报错
因为函数接受数字作为参数,但数组是字符串,所以我不能简单地使用
T extends string[], U extends T
这在类型一致的情况下可以工作,我的尝试使用 Exclude
的联合类型也失败了,但我没有追求这个思路很长时间。
下面的代码因为无法使用扩展运算符而失败。不过,用于推导元组长度的逻辑可以正常工作。
const myAttempt = <
T extends (readonly [] | readonly unknown[]) & (number extends T["length"] ? readonly [] : unknown),
>(
categories: T,
fnc: (...args: { [K in keyof T]: number }) => number,
) => {
return {
names: categories,
calcFunc: fnc,
};
};
该函数使用已知的、硬编码的一组参数调用,因此使用元组是可以的,但在不久的将来,将会出现需要使用长度为 20-30 的情况,我希望避免硬编码每个不同的重载(而且这样做会使代码更清晰!)。
<details>
<summary>英文:</summary>
I'm writing a function that takes two parameters, A and B.
A is an array of strings with length L.
B is a function that takes L numbers as parameters and returns a number.
const acceptSameLength = (stringArray: string[], fnc: (...inputNums: number[]) => number) => ({
names: stringArray,
calcFunc: fnc
})
I'd like to enforce L being consistent between the two.
acceptSameLength(['foo'], (a: number) => 123) // Should work fine
acceptSameLength(['foo'], (a: number, b: number) => 123) // Should error
acceptSameLength(['foo', 'bar'], (a: number) => 123) // Should error
Because the function takes numbers as the parameters, but the array is of strings, I can't just use
T extends string[], U extends T
which does work for instances where the types are consistent, and my attempts of using a union type with `Exclude` also failed, but I didn't pursue that line of reasoning for a particularly long time.
The following fails as the spread operator can't be used. However, the logic for deriving the length of the tuple works fine.
const myAttempt = <
T extends (readonly [] | readonly unknown[]) & (number extends T["length"] ? readonly [] : unknown),
>(
categories: T,
fnc: (...args: { [K in keyof T]: number }) => number,
) => {
return {
names: categories,
calcFunc: fnc,
};
};
The function is invoked with a known, hard-coded set of parameters, so using tuples is okay, but in the near future there will be instances where this is used with lengths of 20-30 and I'd like to avoid hardcoding each different overload (plus, it makes the code a lot cleaner!).
</details>
# 答案1
**得分**: 0
我的解决方案是通过意识到问题有一种相反的看法而来的:不是试图通过参考数组来限制函数的参数,而是可以通过参考函数的参数来限制数组!
```typescript
function myAttempt<T extends number[]>(
categories?: string[] & { [K in keyof T]: string },
calcFunc?: (...args: T) => number,
) {
return {
category: category,
categories: categories,
calcFunc: calcFunc as (...args: number[]) => number,
};
}
正如您可以在这里所看到的,这满足了我所期望的。
英文:
My solution to this came via realising that there was an inverse way of looking at the problem: instead of trying to restrict the parameters of the function by referencing the array, we could instead restrict the array, by referencing the parameters of the function!
function myAttempt<T extends number[]>(
categories?: string[] & { [K in keyof T]: string },
calcFunc?: (...args: T) => number,
) {
return {
category: category,
categories: categories,
calcFunc: calcFunc as (...args: number[]) => number,
};
}
As you can see here, this fulfils what I was looking for.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论