Constraints on interface members in typescript generics

后端 未结 3 1233
Happy的楠姐
Happy的楠姐 2021-01-14 00:17

I have a method, that should accepts any object, as long as all its fields are strings or numbers

I made this, which works great with duck typing

sta         


        
相关标签:
3条回答
  • 2021-01-14 00:52

    T's constraint can refer to T (with some restrictions), so you can use a mapped type to generate a constraint that has the same fields as T:

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

    Beware that trickery with circular type parameter constraints can sometimes cause problems, though this scenario will likely be OK.

    0 讨论(0)
  • 2021-01-14 01:05

    Here is final version, thanks Matt for hint

    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);
    }
    
    0 讨论(0)
  • 2021-01-14 01:14

    If you want interpolateParams("/api/:method/:value", {method: "abc", value: 10}); to typecheck you can't. This is because you cannot infer anything about "/api/:method/:value" to give you a method,value signature.

    Workaround

    Write functions that take method and value and use it to power both the config and the use.

    E.g. this is the strategy I use with 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)
    
    0 讨论(0)
提交回复
热议问题