我想根据同级属性缩小函数参数的类型。我知道存在类型会有所帮助,但它们不可用,所以我正在使用帮助程序函数。我目前在评论中感谢jcalz。
// helper function
function helper<T extends readonly unknown[]>(o: {
choices: T;
func: (opts: T[number]) => T[number];
}) {
return o;
}
helper({
choices: [
"one",
"two",
"three",
], // as const
func(o) { // should be "one" | "two" | "three", but is string
return o;
}
})
我希望func
中的o
是"one" | "two" | "three"
型,所以我的问题是打字稿不会将选择推断为元组,而是推断为string[]
。我无法将as const
添加到choices
,因为该函数将在 javascript 中调用。
我知道我可以将选择和函数拆分为帮助函数的单独参数,但这是不可能的,因为对象中会有更多属性。这些属性的目的是描述func
,它需要什么作为输入和返回什么。
最终目标是让帮助程序函数用打字稿编写,但在 javascript 中用作类型注释。因此,在调用函数或使用标有注释的函数as const
时,我无法提供任何类型。
您希望helper()
数组文本值["one", "two", "three"]
视为具有类型["one", "two", "three"]
; 也就是说,作为字符串文本类型的元组,而不仅仅是string[]
。 从函数的调用方端很容易做到这一点;调用方可以使用const
断言,例如["one", "two", "three"] as const
。 但是,如果调用方不想(或不能)使用const
断言,最好让函数本身来执行此操作。
不久前我打开了Microsoft/TypeScript#30680,要求一些简单的方法来做到这一点。 也许它看起来像:
// INVALID TYPESCRIPT SYNTAX, DO NOT TRY THIS
// vvvvv
function helper<T extends const readonly unknown[]>(o: {
choices: T;
func: (opts: T[number]) => T[number];
}) {
return o;
}
其中const
可以以某种方式应用于泛型类型参数。 不幸的是,这目前不是该语言的一部分。
幸运的是,有一些技巧可以让编译器的行为有些相似。
如果你想给编译器一个提示,它应该推断文字类型,你可以看看microsoft/TypeScript#10676。 如果类型参数被约束为包含string
的类型,则编译器将倾向于推断字符串文本类型。number
也是如此. 因此,虽然一个不受约束的T
(如T extends unknown
)在看到"one"
时会推断出string
,而一个受约束的T extends string
会推断出"one"
。 如果你真的不想约束类型参数,那么你需要提出尽可能广泛的约束,明确包含string
和/或number
。 您不能使用unknown
类型,因为T extends string | unknown
会立即折叠为T extends unknown
。 但是,您可以执行以下操作
type Narrowable = string | number | bigint | boolean |
symbol | object | undefined | void | null | {};
然后写T extends Narrowable
而不是T extends unknown
. 或者在您的情况下,T extends readonly Narrowable[]
而不是T extends readonly unknown[]
.
如果您希望编译器推断元组而不是数组类型,还有其他技巧。 TypeScript 4.0 引入了可变元组类型。 如果U
是一个类似数组的类型参数,那么编译器将倾向于看到(arr: U)
并推断出U
的无序数组类型。 但是如果你写(arr: [...U])
或'(arr:只读[...U]),它将倾向于推断元组类型。
结合这些可以得到:
function helper<T extends readonly Narrowable[]>(o: {
choices: readonly [...T];
func: (opts: T[number]) => T[number];
}) {
return o;
}
您可以验证它是否有效:
helper({
choices: [
"one",
"two",
"three",
], // as const
func(o) { // "one" | "two" | "three"
return o;
}
})
看起来不错。
操场链接到代码