从同级属性推断/缩小函数参数范围 [英] Infer/narrow function argument from sibling property
问题描述
我想根据同级属性缩小函数参数的类型。我知道存在类型会有帮助,但它们不可用,所以我使用了一个助手函数。多亏了评论中的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
,因为该函数将在Java脚本中调用。
我知道我可以将选项和函数拆分到helper函数的单独参数中,但这是不可能的,因为对象中将有更多属性。属性的目的是描述func
、它需要什么作为输入以及它返回什么。
最终目标是让helper函数用类型脚本编写,但在Java脚本中用作类型批注。因此,我不能在调用函数时提供任何类型,也不能在用注释标记的地方使用as const
。
推荐答案
您希望helper()
将数组文字值["one", "two", "three"]
视为类型["one", "two", "three"]
;即,将其视为字符串literal types的tuple,而不仅仅是string[]
。从函数的调用方端可以很容易地做到这一点;调用方可以使用类似["one", "two", "three"] as const
的const
assertion。但是,如果调用方不想(或不能)使用const
断言,让函数本身来做这件事就更好了。
// 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。如果类型参数constrained为包含string
的类型,则编译器将倾向于推断字符串文字类型。number
也是如此。因此,当不受约束的T
(如T extends unknown
)看到"one"
时将推断string
,而受约束的T extends string
将推断"one"
。如果您真的不想约束类型参数,那么您需要想出最宽的约束,显式地包含string
和/或number
。您不能使用the unknown
type,因为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[]
。
如果希望编译器推断元组而不是数组类型,还有其他诀窍。TypeScrip 4.0引入variadic tuple types。如果U
是类似数组的类型参数,则编译器将倾向于看到(arr: U)
并推断U
的无序数组类型。但如果您编写(arr: [...U])
或`(arr:Readonly[...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;
}
})
看起来不错。
这篇关于从同级属性推断/缩小函数参数范围的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!