【问题标题】:Constraints on interface members in typescript generics打字稿泛型中接口成员的约束
【发布时间】:2018-10-09 22:34:59
【问题描述】:

我有一个方法,它应该接受任何对象,只要它的所有字段都是字符串或数字

我做了这个,非常适合鸭子打字

static interpolateParams(
    route: string, 
    params: {[key: string] : string | number}) : string {

    const parts = route
        .split("/")
        .map(part => {
            const match = part.match(/:([a-zA-Z09]*)\??/);
            if (match) {
                if (!params[match[1]]) {
                    console.error("route argument was not provided", route, match[1]);
                    return part;
                }

                return params[match[1]];
            }
            else {
                return part;
            }
        })

    return "/" + parts.slice(1).join("/");
}

然后打电话

interpolateParams("/api/:method/:value", {method: "abc", value: 10});

现在我想让interpolateParams 接受路由params 的任何接口。

interpolateParams<IABCParams>("/api/:method/:value", {method: "abc", value: 10});

问题是它仍然应该匹配所有字符串或数字字段的约束

有没有办法将给定接口的所有字段的通用约束指定为某种类型?

我试过了

static interpolateParams<T extends {[key: string] : string | number}>(
    route: string, 
    params: T) : string {

显然得到了这个

类型 'IABCParams' 不满足约束 '{ [key: string]: string |数字; }'。

类型“IABCParams”中缺少索引签名。

谢谢

【问题讨论】:

  • 我的猜测:不,没有。很想错。
  • @AndrewShepherd 这似乎是可能的,检查马特的评论和我的最终版本,谢谢

标签: typescript generics interface constraints


【解决方案1】:

T的约束可以引用T(有一些限制),所以可以使用映射类型来生成与T具有相同字段的约束:

function interpolateParams<T extends {[P in keyof T] : string | number}>(
    route: string, 
    params: T) : string { /*...*/ }

请注意,使用循环类型参数约束的诡计有时会导致问题,尽管这种情况可能没问题。

【讨论】:

  • 好吧,那效果不太好,但是有了额外的层,它似乎欺骗了解释器:) 正如你所描述的,if (!params[match[1]]) 行中的错误 - 没有索引器。所以我按照你的建议添加了另一个版本的 interpolateRoute,并在里面调用了 bare interpolate(代码见下)
【解决方案2】:

这是最终版本,感谢马特的提示

static interpolateParams(
    route: string, 
    params: {[key: string] : string | number}) : string {

    const parts = route
        .split("/")
        .map(part => {
            const match = part.match(/:([a-zA-Z09]*)\??/);
            if (match) {
                if (!params[match[1]]) {
                    if (part.endsWith("?")) {
                        return null;
                    }

                    console.error("route argument was not provided", route, match[1]);
                    return part;
                }

                return params[match[1]];
            }
            else {
                return part;
            }
        }).filter(p => p && p != "");

    return "/" + parts.join("/");
}

static formatRoute<T extends {[P in keyof T] : string | number}>(
    route: string,
    params: T
) : string {
    return interpolateParams(route, params);
}

【讨论】:

    【解决方案3】:

    如果你想让interpolateParams("/api/:method/:value", {method: "abc", value: 10}); 进行类型检查,你不能。这是因为您无法推断出任何关于 "/api/:method/:value" 的信息来给您一个 method,value 签名。

    解决方法

    编写采用methodvalue 的函数,并使用它来为配置和使用两者提供动力。

    例如这是我对takeme 使用的策略。

    // Define 
    export const links = {
      profile: (id: string) => `/profile/${id}`
    }
    
    // Use in Configure 
    links.profile(':profileId')
    
    // Use in Navigate / a tags
    links.profile(user.id)
    

    【讨论】:

      猜你喜欢
      • 2018-02-09
      • 2019-09-23
      • 2021-04-20
      • 1970-01-01
      • 2020-08-03
      • 2022-11-23
      • 2017-09-26
      • 1970-01-01
      • 2020-12-30
      相关资源
      最近更新 更多