typescript 应为3个类型参数,但得到1个,但它应推断2个类型

x9ybnkn6  于 2023-03-13  发布在  TypeScript
关注(0)|答案(1)|浏览(217)

我想知道如何正确推断我的函数的第二和第三个模板
假设一个简单接口

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)

看起来函数的第二个和第三个模板参数没有从第一个模板参数中推断出来,但它应该是这样的,因为在第一个案例中,当我直接指定类型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");
brgchamk

brgchamk1#

从TS5.0开始,没有microsoft/TypeScript#10571microsoft/TypeScript#26242所要求的 partial type argument inference,要么让编译器尝试推断所有类型参数,要么指定所有类型参数(当然,有默认的类型参数,但这并不能给予您的需要:您希望 * 推断 * 您遗漏的类型参数,而不是为它们分配 * 默认 * 类型)。在ms/TS#23696ms/TS#22368上已经有几个提案来解决这个问题,但到目前为止还没有一个得到完全批准或合并。
因此,目前只有变通方法,我能想到的两种方法是使用一个伪函数参数或使用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,然后使用类型Assert来选择T
curried函数解:

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,但是推断了其他类型参数。
这两种解决方案都不完美,但它们是我们目前所能做的最好的。祝你好运!

相关问题