强制类型参数推断不使用任何数组[](Typescript)



我正在编写一个处理类型验证的Typescript库。

我有这个工作代码:

type MyTypePredicate<T> = (value: any) => value is T
function createTypePredicateForArrayT<T>(item: T): MyTypePredicate<T[]> {
return ((value: any) => /* Real code here */ true) as MyTypePredicate<T[]>
}
// This can be use with or without providing the generic T.
const t1 = createTypePredicateForArrayT<number>(42)
const t2 = createTypePredicateForArrayT(42)
// Both t1 and t2 will have the type MyTypePredicate<number[]>

然而,纯粹为了与库中的一组其他函数保持一致,我更愿意让提供的泛型与返回的MyTypePredicate<T>

中的泛型类型相同。可以使用

function createTypePredicateForArrayA<A extends any[]>(item: A[number]): MyTypePredicate<A> {
return ((value: any) => /* Real code here */ true) as MyTypePredicate<A>
}
// which make this working:
const a1 = createTypePredicateForArrayA<number[]>(42)
// ...but the argument inference stops working,
const a2 = createTypePredicateForArrayA(42)
// it gives a2 the type MyTypePredicate<any[]> and not MyTypePredicate<number[]>

是否有一种方法重写第二,仍然使a1和a2工作?

直接的方法是用第一种方式编写它,让编译器推断泛型类型参数。类型参数必须代表你所关心的类型的特定函数的附加约束必然使事情复杂化;很难/不可能让一个类型参数以一种方式使用显式规范,而另一种方式使用类型推断。


让事情按照您的要求工作的一种方法是添加第二个类型参数,以便您仅使用第一个类型参数进行显式规范,第二个类型参数用于类型推断:

function createTypePredicateForArrayA<
A extends any[],
T = A[number]
>(item: T): MyTypePredicate<T[]> {
return ((value: any) => /* Real code here */ true) as MyTypePredicate<T[]>
}

让我们测试一下:

const a1 = createTypePredicateForArrayA<number[]>(42)
// const a1: MyTypePredicate<number[]>
const a2 = createTypePredicateForArrayA(42)
// const a2: MyTypePredicate<number[]>

a1的情况下,您显式指定Anumber[]。由于TypeScript目前不支持部分类型参数推断(如ms/TS#26242所要求的),那么T类型参数也被指定,而不是推断。因此,它最终回落到默认的A[number],这意味着Tnumber,你被迫为item传递一个number参数,你得到number[]

a2的情况下,你让编译器推断AT。没有A的推理站点,所以默认为any[]。但itemT的推理站点,因此默认为number,因此返回类型为number[]


另一种方法是给你的函数两个调用签名;一个用于类型参数推断,另一个用于手动规范。也就是说,使它成为一个重载函数:

function createTypePredicateForArrayA<A extends any[] = never>(
item: A[number]): MyTypePredicate<A>; // manual
function createTypePredicateForArrayA<T>(item: T): MyTypePredicate<T[]>; // infer

让我们测试一下:

const a1 = createTypePredicateForArrayA<number[]>(42)
// const a1: MyTypePredicate<number[]>
const a2 = createTypePredicateForArrayA(42)
// const a2: MyTypePredicate<number[]>

a1的情况下,您手动指定Anumber[],这使得编译器选择第一个重载,其行为如您所期望的。在a2的情况下,不指定类型参数;第一次重载失败,因为没有A的推理站点,因此它回落到默认的never,而42不匹配…因此,它尝试第二次重载,现在您得到了T的直接推理行为,正如您所期望的那样。


好了;两种相对复杂的方法来解决相对复杂的问题,即与TypeScript的泛型函数推断的预期行为作斗争。

Playground链接到代码

最新更新