如何使未定义的参数不需要?

How can I make an undefined parameter not required?

Playground

我试图让未定义的值成为可选的,而不是可选的值。我的完美示例是:

type TypeObj = {
  a: undefined;
  b: number;
  c: string;
}

call("a"); call("b", 1); call("c", "3"); // should work
call("b", "a"); call("c", 3) // shouldn't work
call("b"); call("c") // also shouldn't work

但我目前的方法(第二个参数是可选的)允许 call 在没有第二个参数的情况下使用 b 和 c,这是我不想要的。

function call<K extends keyof TypeObj = keyof TypeObj>(key: K, param?: TypeObj[K]) {

}

call("a"); call("b", 1); call("c", "3"); // works (good)
call("b", "a"); call("c", 3) // doesn't work (good)
call("b"); call("c") // works (don't want that)

这种事情基本上需要 call() 有多个调用签名。 传统上,这需要您将 call() 设为 overloaded function, but you can also do it by having the function accept a tuple-typed rest parameter.

这是一种方法:

type OptionalIfUndefined<T> =
  undefined extends T ? [param?: T] : [param: T];

function call<K extends keyof TypeObj>(
  key: K, ...[param]: OptionalIfUndefined<TypeObj[K]>
) {
  const _param = param as TypeObj[K]; // <-- might need this
}

OptionalIfUndefined<T>类型是conditional type that checks if undefined is assignable to T. If so, it evaluates to a single-element tuple with an optional element of type T;否则,它计算为 single-element 元组,其中 required 元素类型为 T.

然后 call() 被赋予一个类型为 OptionalIfUndefined<TypeObj[K]> 的剩余参数。我们使用 destructuring assignment 以便 param 是一个保存单个元素的变量名,因为这就是我们真正想要的(保存它的实际数组对我们没有用)。

在您的示例中,您没有在 call() 的实现中执行任何操作,但是如果您期望 param 被称为 TypeObj[K] 类型,您会感到失望。通用条件类型对编译器来说本质​​上是不透明的,因此它不知道 typeof param 会比 TypeObj[K] | undefined 更窄。如果您需要这样的功能,您需要 assertconst _param = param as TypeObj[K] 一样,然后使用 _param 而不是 param (或者如果需要交换名称)。

好吧,让我们确保它有效:

call("a") // okay
call("a", undefined) // okay
call("b", 1); // okay
call("b") // error
call("c", "a"); // okay
call("c") // error

看起来不错!

Playground link to code