[英]conditional types parameters in typescript
I'm attempting to create a function where the columns
parameter depends on what type
has been selected.我正在尝试创建一个 function ,其中
columns
参数取决于选择的type
。
function useNavigation(type: "horizontal" | "grid" | "vertical", columns?: number)
How to change that type declaration to set that columns parameter is required
when parameter type
is grid
当参数
type
为grid
时,如何更改该类型声明以设置该列参数是required
的
You can use function overloads to achieve this.您可以使用 function 重载来实现此目的。 In my example below, the first two declarations are specifying different arguments based on your requirements whereas the last declaration (and consequently the implementation) receives all possible arguments, which is why
type
is still 'horizontal' | 'vertical' | 'grid'
在下面的示例中,前两个声明根据您的要求指定不同的 arguments,而最后一个声明(以及因此实现)接收所有可能的 arguments,这就是为什么
type
仍然是'horizontal' | 'vertical' | 'grid'
'horizontal' | 'vertical' | 'grid'
'horizontal' | 'vertical' | 'grid'
. 'horizontal' | 'vertical' | 'grid'
。
The caller will get proper types and auto-completion, you'll simply have to do the checks inside the function to determine your logic based on the arguments.调用者将获得正确的类型和自动完成功能,您只需在 function 中进行检查,即可根据 arguments 确定您的逻辑。
function useNavigation(type: 'horizontal' | 'vertical');
function useNavigation(type: 'grid', columns: number);
function useNavigation(
type: 'horizontal' | 'vertical' | 'grid',
columns?: number,
) {
// Your function body...
}
Here's a TS playground demonstrating this.这是一个展示这一点的 TS 操场。
Traditionally (pre TS 2.8 or TS 3.0) you could only use overloads to achieve this, but now I'd be inclined to use a tuple-typed rest parameter ;传统上(TS 2.8 或 TS 3.0 之前)你只能使用重载来实现这一点,但现在我倾向于使用元组类型的 rest 参数; specifically a union of such tuple types:
特别是这种元组类型的联合:
function useNavigation(...args:
[type: "horizontal" | "vertical", columns?: number] |
[type: "grid", columns: number]
) { }
That means the arguments to useNavigation()
must either be: a pair where the first element is "horizontal"
or "vertical"
and the second element is an optional number
value;这意味着使用
useNavigation()
的 arguments 必须是:第一个元素是"horizontal"
或"vertical"
并且第二个元素是可选number
的一对; or a pair where the first element is "grid"
and the second element is a required number
value.或第一个元素是
"grid"
而第二个元素是必需的number
的一对。
You can test that it works as desired:您可以测试它是否按需要工作:
useNavigation("horizontal") // okay
useNavigation("vertical", 20); // okay
useNavigation("grid", 10); //okay
useNavigation("grid"); // error!
// ---------> ~~~~~~
// Source has 1 element(s) but target requires 2
Note that the type signature for useNavigation()
is also taking advantage of labeled tuple elements , to show that we intend the caller to think of the first argument as having a name of type
and the second argument as having a name of columns
.请注意,
useNavigation()
的类型签名也利用了标记的元组元素,以表明我们希望调用者将第一个参数视为具有type
名称,而将第二个参数视为具有columns
名称。 Such labels are only relevant when it comes to type hints in IntelliSense and do not affect the types or runtime behavior.此类标签仅在涉及 IntelliSense 中的类型提示时才相关,并且不会影响类型或运行时行为。 In particular, there is no variable named
type
or columns
here;特别是,这里没有名为
type
或columns
的变量; the function implementation sees only an args
array: function 实现只看到一个
args
数组:
function useNavigation(...args:
[type: "horizontal" | "vertical", columns?: number] |
[type: "grid", columns: number]
) {
if (args[0] === "grid") {
args[1].toFixed(); // okay
}
}
You could, if you want, destructure args
into type
and columns
variables as in如果需要,您可以将
args
解构为type
和columns
变量,如
const [type, columns] = args;
but there is an advantage to leaving it as args
;但是将其保留为
args
有一个优势; namely, that the compiler sees args
as a discriminated union type.即,编译器将
args
视为可区分的联合类型。 Note that in the above implementation, the compiler sees that if args[0] === "grid"
, then args[1]
is definitely a number
and not possibly undefined
.请注意,在上面的实现中,编译器看到如果
args[0] === "grid"
,那么args[1]
肯定是一个number
,而不可能是undefined
。 Compare to the behavior when you separate args
out into two no-longer-seen-as-correlated variables:与将
args
分成两个不再被视为相关变量时的行为进行比较:
if (type === "grid") {
columns.toFixed(); // oops, possibly undefined
}
The compiler only sees columns
as type number | undefined
编译器仅将
columns
视为类型number | undefined
number | undefined
regardless of whether or not you check type === "grid"
first.无论您是否先检查
type === "grid"
都是number | undefined
的。 This might not be a big deal to you, but discriminated unions are useful enough that I wanted to point it out.这对您来说可能没什么大不了的,但受歧视的工会非常有用,我想指出这一点。
My favorite pattern to achieve some additional parameters to be required for some parameter(s) is to wrap it in an object and type it as:我最喜欢的实现某些参数所需的一些附加参数的模式是将其包装在 object 中并将其键入为:
function useNavigation(options: { type: "horizontal" | "vertical"; columns?: number } | { type: "grid", columns: number })
and use it as并将其用作
useNavigation({ type: "horizontal" })
useNavigation({ type: "grid", columns: 12 })
It may feel a little bit repetitive but provides good typesafely, also when types are narrowed down after property check它可能感觉有点重复,但提供了良好的类型安全性,在属性检查后缩小类型时也是如此
if (options.type === "grid") {
// here typescript knows that `columns` exists
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.