打字稿中的条件函数参数

conditional function arguments in 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.

理论上,上述函数不应该需要第二个参数,但确实如此!

编辑:我知道“?”对于可选参数类型,但我想让它有条件地利用编译器错误和建议。

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

// ...
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

最后,要使参数可选,请使用 ? 运算符。

Playground

我认为你混淆了两种不同的东西。

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

意味着该函数需要 2 个参数,其中第二个参数可能具有未定义的类型,意思是:

_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 是“该字段不存在”

您始终可以使用 signature overloading 来阻止用户直接使用实现签名:

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'

正如@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