预期3个类型参数但得到1但它应该推断出2种类型

问题描述 投票:2回答:1

我想知道如何正确推断我的函数的第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");
typescript type-inference extends keyof
1个回答
3
投票

从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但推断了其他类型参数。

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

© www.soinside.com 2019 - 2024. All rights reserved.