我想知道如何正确推断我的函数的第2和第3个模板
假设一个简单的界面
interface ISome {
a: string;
b?: {
c: string;
};
}
按照工作
function pathBuilder<
K1 extends keyof ISome,
K2 extends keyof NonNullable<ISome[K1]>>(p: K1, p2?: K2) {
let res = String(p);
if (p2) { res += "." + p2; }
return res;
}
const pathTest = pathBuilder("b", "c"); // ---> "b.c" and intellisense works on parameters
但是我需要通过指定另一种类型来概括函数来工作(我不想传递一个对象实例来指定类型)
所以,以下不起作用
function pathBuilder<
T,
K1 extends keyof T,
K2 extends keyof NonNullable<T[K1]>>(p: K1, p2?: K2) {
let res = String(p);
if (p2) { res += "." + p2; }
return res;
}
const pathTest = pathBuilder<ISome>("b", "c"); // ERROR: Expected 3 type arguments, but got 1.ts(2558)
似乎函数的第2和第3个模板参数没有从第一个推断,但它应该因为在第一种情况下,当我直接指定类型T = ISome时它起作用。
我不确定是否有一些语言关键字使其工作但模板应该完全适用于此:指定一个未知类型。
编辑
实际上我发现了这种方式,但是如果可能的话我需要额外的编码
function pathBuilder<T>() {
return <
K1 extends keyof T,
K2 extends keyof NonNullable<T[K1]>>(p: K1, p2?: K2) => {
let res = String(p);
if (p2) { res += "." + p2; }
return res;
};
}
const pathTest = pathBuilder<ISome>()("b", "c");
从TS3.4开始,没有partial type parameter inference。您可以让编译器尝试推断所有类型参数,也可以指定所有类型参数。 (好吧,有default type parameters,但它没有给你你想要的东西:你想推断你遗漏的类型参数,而不是为它们分配默认类型)。有several提议给address这个,但到目前为止none have met with full approval。
因此,目前只有变通方法。我能想到的两个是使用虚函数参数或使用currying。
虚拟参数版本:
function pathBuilderDummy<
T,
K1 extends keyof T,
K2 extends keyof NonNullable<T[K1]>>(dummy: T, p: K1, p2?: K2) {
let res = String(p);
if (p2) { res += "." + p2; }
return res;
}
const pathDummyTest = pathBuilderDummy(null! as ISome, "b", "c");
在这里,我们正在做你说你不想做的事情......传递T
类型的参数。但是因为它只是一个虚拟参数而没有在运行时使用,所以只关注类型系统认为它是什么。传入的值的实际类型无关紧要。所以你可以通过它null
并使用type assertion选择T
。
咖喱功能解决方案:
const pathBuilderCurry =
<T>() => <
K1 extends keyof T,
K2 extends keyof NonNullable<T[K1]>>(p: K1, p2?: K2) => {
let res = String(p);
if (p2) { res += "." + p2; }
return res;
}
const pathCurryTest = pathBuilderCurry<ISome>()("b", "c")
在这里,您将返回一个返回另一个函数的函数。第一个函数不带任何值参数,但它确实采用您要指定的一个类型参数。然后返回一个函数,其中指定了T
但推断了其他类型参数。
这两种解决方案都不是完美的,但它们是我们现在能做的最好的解决方案。希望有所帮助;祝好运!