预期有 3 个类型参数,但得到 1 个,但它应该推断出 2 个类型 [英] Expected 3 type arguments but got 1 but it should infer 2 types

查看:33
本文介绍了预期有 3 个类型参数,但得到 1 个,但它应该推断出 2 个类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想知道如何正确推断我的函数的第 2 个和第 3 个模板

假设一个简单的界面

interface ISome {a:字符串;乙?:{c:字符串;};}

关注作品

function pathBuilder(p: K1, p2?: K2) {让 res = String(p);if (p2) { res += "."+ p2;}返回资源;}const pathTest = pathBuilder("b", "c");//--->b.c"和智能感知对参数起作用

但我需要通过指定另一种类型来概括该函数以使其工作(我不想传递对象实例来指定类型)

所以,以下不起作用

function pathBuilder<,K1 扩展了 T 的密钥,K2 extends keyof NonNullable(p: K1, p2?: K2) {让 res = String(p);if (p2) { res += "."+ p2;}返回资源;}const pathTest = pathBuilder("b", "c");//错误:需要 3 个类型参数,但得到 1.ts(2558)

似乎函数的第 2 个和第 3 个模板参数不能从第一个参数推断出来,但应该是因为在第一种情况下,当我直接指定类型 T=ISome 时它起作用了.

我不确定是否有一些语言关键字可以使其工作,但模板应该完全适用:指定未知类型.

编辑

实际上我找到了这种方式,但需要额外的编码,如果可能的话我会避免

function pathBuilder() {返回(p:K1,p2?:K2)=>{让 res = String(p);if (p2) { res += "."+ p2;}返回资源;};}const pathTest = pathBuilder()("b", "c");

解决方案

从 TS3.4 开始,没有 部分类型参数推断.要么让编译器尝试推断所有类型参数,要么指定所有类型参数.(嗯,有 默认类型参数 但这并没有给你你想要的:你想推断你遗漏的类型参数,而不是为它们分配一个默认类型).已经有几项提议解决这个问题,但到目前为止没有人得到完全批准.

因此,目前只有解决方法.我能想到的两个是使用虚拟函数参数或使用currying.>

虚拟参数版本:

function pathBuilderDummy<,K1 扩展了 T 的密钥,K2 extends keyof NonNullable(dummy: T, p: K1, p2?: K2) {让 res = String(p);if (p2) { res += "."+ p2;}返回资源;}const pathDummyTest = pathBuilderDummy(null! as ISome, "b", "c");

这里我们正在做你说你不想做的事情...传入一个 T 类型的参数.但由于它只是一个虚拟参数而不是在运行时使用,所以它只与类型系统认为它是什么有关.您传入的值的实际类型无关紧要.所以你可以传递它 null 并使用 type assertion 选择T.

柯里化函数解决方案:

const pathBuilderCurry =<T>() =><K1 扩展了 T 的密钥,K2扩展了NonNullable的密钥(p:K1,p2?:K2)=>{让 res = String(p);if (p2) { res += "."+ p2;}返回资源;}const pathCurryTest = pathBuilderCurry()("b", "c")

这里返回一个函数,该函数返回另一个函数.第一个函数不接受任何值参数,但它接受您要指定的一个类型参数.然后它返回一个函数,其中指定了 T 但其他类型参数是推断出来的.

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

I wondering how to correctly infer 2th and 3th template of my function

suppose a simple interface

interface ISome {
    a: string;
    b?: {
        c: string;
    };
}

follow works

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

but I need to generalize the function to work by specify another type ( I don't want to pass an object instance to specify the type )

so, following not works

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)

seems that 2th and 3th template argument of the function doesn't infer from the first one but it should because in the case first case when I specified directly a type T=ISome it worked.

I'm not sure if there is some language keyword to make it work but the template should work exactly for that: specify an unknown type.

EDIT

Actually I found this way, but require extra coding I would avoid if possible

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

解决方案

As of TS3.4 there is no partial type parameter inference. Either you let the compiler try to infer all the type parameters, or you specify all the type parameters. (Well, there are default type parameters but that doesn't give you what you want: you want to infer the type parameters you leave out, not assign a default type to them). There have been several proposals to address this, but so far none have met with full approval.

For now, therefore, there are only workarounds. The two that I can think of are to use a dummy function parameter or to use currying.

The dummy parameter version:

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

Here we are doing what you said you didn't want to do... pass in a parameter of type T. But since it's just a dummy parameter and not used at runtime, it only matters what the type system thinks it is. The actual type of the value you pass in doesn't matter. So you can just pass it null and use a type assertion to choose T.

The curried function solution:

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

Here you are returning a function that returns another function. The first function takes no value parameters but it does take the one type parameter you want to specify. It then returns a function where T is specified but the other type parameters are inferred.

Neither solution is perfect, but they are the best we can do for now. Hope that helps; good luck!

这篇关于预期有 3 个类型参数,但得到 1 个,但它应该推断出 2 个类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆