Typescript: Infer type of nested keyof Properties

∥☆過路亽.° 提交于 2019-12-07 09:23:12

问题


I want to define an Array type that must contain a chain of nested property names of a given type.

Let's say I have a type:

    type Foo = {
        outer: {
            inner: any;
        }
    }

Now I want to define an Array type with 2 elements:

type PropertyList<T, K1 extends keyof T, K2 extends keyof T[K1]> = [K1, K2];

I want to use it like this:

let myList:PropertyList<Foo> = ["outer", "inner"]

So I want the compiler to check if the 2 contained Property names are nested property names of Foo.

But I can't define PropertyList with only 1 generic parameter, I get this error then:

TS2314: Generic type 'PropertyList' requires 3 type argument(s)

Any idea how i can infer the nested keyof types without having to specify them?


回答1:


A first approximation of how to do this is in Tao's answer, the problem is that if you add more properties it does not work as expected:

type Foo = {
    outer: {
        inner: any;
    }
    outer2: {
        inner2: any;
    }
}

type PropertyList<T, K1 extends keyof T = keyof T, K2 extends keyof T[K1] = keyof T[K1]> = [K1, K2];

let myList:PropertyList<Foo> = ["outer", "inner"] // error, since K2 will have to be a property of both outer and outer2

You can use function to help with inferring the corect type based on the actual parameters passed. We need to use a two function approach because we need to have generic parameters for T, K1 and K2, but we only want to specify T and if we specify one we must specify all parameters:

function pathFor<T>() {
    return  function <K1 extends keyof T, K2 extends keyof T[K1]>(outer: K1, inner: K2): [K1,K2]{
        return  [outer, inner];
    }
}
// Usage 
let myList = pathFor<Foo>()("outer", "inner"); // typed as ["outer, "inner"]
let myList2 = pathFor<Foo>()("outer2", "inner"); // error, inner is not part of outer2
let myList3 = pathFor<Foo>()("outer2", "inner2"); // typed as ["outer2, "inner2"]

Edit

You can also expand the function to take paths up to a finite length (4 in the example, but add more as needed):

function keysFor<T>() {
    function keys<K1 extends keyof T, K2 extends keyof T[K1], K3 extends keyof T[K1][K2], K4 extends keyof T[K1][K2][K3]>(outer: K1, inner: K2, innerInner: K3, innerInnerInner: K4): [K1,K2, K3, K4]
    function keys<K1 extends keyof T, K2 extends keyof T[K1], K3 extends keyof T[K1][K2]>(outer: K1, inner: K2, innerInner: K3): [K1,K2, K3]
    function keys<K1 extends keyof T, K2 extends keyof T[K1]>(outer: K1, inner: K2): [K1,K2]
    function keys(): string[]{
        return [...arguments];
    }
    return keys
}



回答2:


Edit

I think I misunderstood your question and don't think that's possible (yet). Maybe inferring the positional types will be possible with type inference for conditional types that will be included in the upcoming version 2.8 of TypeScript.

I will leave the original answer below just in case.


Type parameters can have default values:

type PropertyList<T, K1 extends keyof T = keyof T, K2 extends keyof T[K1] = keyof T[K1]> = [K1, K2];

But maybe in your case it makes more sense to not specify K1 and K2 as type parameters:

type PropertyList<T> = [keyof T, keyof T[keyof T]];


来源:https://stackoverflow.com/questions/49005179/typescript-infer-type-of-nested-keyof-properties

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!