typescript中的条件函数参数



是否有可能根据第一个参数类型有一个分隔的必需参数类型:

type ConditionalRequiredArg<T, P> = T extends string ? P | undefined : P;
function func<T, P>(_arg1: string | number, _arg2: ConditionalRequiredArg<T, P>) {}
func('foo'); // Error: Expected 2 arguments, but got 1.

理论上,上面的函数不应该需要第二个参数,但它确实需要!

编辑:I am aware of '?'用于可选参数类型,但我希望将其设置为有条件的,以利用编译器错误和建议。

您总是可以使用签名重载来阻止用户直接使用实现签名:

function func<P>(_arg1: string, _arg2?: P)
function func<P>(_arg1: number, _arg2: P)
function func<P>(_arg1: string | number, _arg2?: P) { }

此代码强制用户选择使用第一个签名或第二个签名,而不是实现的第三个签名。

func('foo', 1); // success
func('foo'); // success
func(1, 1); // success
func(1); // Error: Argument of type 'number' is not assignable to parameter of type 'string'

我想你把两件事搞混了。

func<T, P>(_arg1: string | number, _arg2: ConditionalRequiredArg<T, P>)

表示该函数需要2个参数,其中第二个参数的类型可能是undefined,即

_arg2 = undefined,其中_arg2字段已经声明。

但是您希望不声明该字段。

_arg2 = undefined不等于说:

func<T, P>(_arg1: string | number, undefined)

对于这个用例,您可以使用?如下:

func<T, P>(_arg1: string | number, _arg2?: ConditionalRequiredArg<T, P>)

编辑:

类型和参数是两码事。类型定义值的类型,而参数是一个字段。

虽然这不是一个好的做法,但您也可以使用undefined手动初始化字段,如下所示:

func<T, P>(_arg1: string | number, _arg2: string = undefined)

但这最终与_arg2?相同

类型undefined就像说"该字段不包含任何内容"或者更好地说,"字段包含未定义的",而字段undefined是"字段不存在">

定义函数的第二个参数的第一种解决方案是使用?操作符将其标记为可选的,并具有以下内容:

// ...
function func<T, P>(_arg1: string | number, _arg2?: ConditionalRequiredArg<T, P>) {}
// ...

然而,如果你想做更多的改变,看看下面的:

/*
* You could remove the conditional type to get a better inference on the generics
* that are used and simply check the typeof _arg1 and if it is string as you had
* defined in the ConfitionalRequiredArg it will use a type in the generic arg
* you would then have to provide
*/
function func<P>(_arg1: string | number, _arg2?: typeof _arg1 extends string ? P | undefined : P) {}
func<string>('foo', 4); // ts error 4 is not assignable to type string | undefined

最后,要使实参成为可选的,可以使用?操作符。

游乐场

正如@daylily所说,如果您能够使用签名重载语法,那么这绝对是要走的路。然而,如果你需要推断仿制药然后我通常会有条件地生成一个元组:

function assertPerson<
Age extends number
>(
...args: 0 extends Age ? [age: Age] : [age: Age, name: string]
) {
const [age, maybeName] = args; // [number, string | undefined]
}
assertPerson(2, "hey");
assertPerson(0)

TypeScript Playground Link

最新更新