预期有3个类型参数,但有1个参数,但它应该推断2个类型

时间:2019-04-18 23:18:51

标签: typescript type-inference extends keyof

我想知道如何正确推断函数的第二和第三模板

假设一个简单的界面

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");

1 个答案:

答案 0 :(得分:3)

从TS3.4开始,没有partial type parameter inference。您要么让编译器尝试推断所有类型参数,要么指定所有类型参数。 (嗯,有default type parameters,但这并不能满足您的需求:您想推断所遗漏的类型参数,而不分配 default 输入他们)。已有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但推断出其他类型参数的函数。

这两种解决方案都不完美,但是它们是我们目前能做到的最好的解决方案。希望能有所帮助;祝你好运!