假设我有一个任意深度的TypScript界面,它描述应用程序的状态,如下所示:

interface MyState {
    foo: boolean;
    bar: {
        baz: string;
        bur: {
            pir: string;
            par: { pew: boolean }[]
        }
    }
}

我现在想写一个类型,允许我约束一个字符串数组,以描述指向遵守该接口的对象中任意点的有效路径.例如,所有这些都应该是正确的:

myPath: PathType<MyState> = ["foo"];
myPath = ["bar", "baz"];
myPath = ["bar", "bur"];
myPath = ["bar", "bur", "par"];
Array handling would be cool like:
myPath = ["bar", "bur", "par", 1, "pew"];

无效:

myPath = ["baz"];
myPath = ["bar", "pir"];
myPath = ["any", "random", "crap"];

我try 过这样的方法:

type PathType<State, Key extends keyof State = keyof State> =
    State extends object ?
        [Key, ...PathType<State[Key], keyof State[Key]>] : never;

但它们似乎都在回归时失败,只有数组的第一个条目被正确输入,然后它接受所有内容.

推荐答案

请注意,这种深度迭代类型往往会有奇怪的边缘情况,需要进行大量的重构来处理.这里显示的代码适用于问题中的示例,但future 具有表面上类似要求的读者可能无法直接使用它.所以要小心.


给定该示例,我倾向于将PathTuple<T>设为recursive conditional type,其中第二个generic类型参数A所起的作用是到当前对象的路径.

type PathTuple<T, A extends PropertyKey[] = []> = T extends object ? (
    A | { [I in keyof T]: PathTuple<T[I], [...A, I]>
    }[keyof T & (T extends readonly any[] ? number : unknown)]
) : A;

基本情况是T不是对象,在这种情况下,我们只是返回累积的路径.否则,我们返回A中的union和大部分形式{[I in keyof T]: PathTuple<T[I], [...A, I]>}[keyof T]distributive object type(如ms/TS#47PathTuple中所创造的). 它会循环到T的属性中,并且对于每个键为I的属性,将I添加到该属性的PathTuple的结果中. 请注意,当你写{[I in K]: F<I>}[K]时,你最终会得到K中的每I得到F<I>的联合. 唯一的问题是,如果T是数组类型,那么mapped type将是数组,但keyof T将包括我们不想包括的所有键,例如lengthpush;我们只希望数组的数字键T. 因此,如果T不像数组,则类型keyof T & (T extends readonly any[] ? number : unknown)将是keyof T,但如果是,则类型keyof T & (T extends readonly any[] ? number : unknown)将是number.


让我们来测试一下:

let myPath: PathTuple<MyState>;
/* let myPath: [] | ["foo"] | ["bar"] | ["bar", "baz"] | ["bar", "bur"] | 
   ["bar", "bur", "pir"] | ["bar", "bur", "par"] | ["bar", "bur", "par", number] | 
   ["bar", "bur", "par", number, "pew"] 
*/
myPath = ["foo"];
myPath = ["bar", "baz"];
myPath = ["bar", "bur"];
myPath = ["bar", "bur", "par"];
myPath = ["bar", "bur", "par", 1, "pew"];
myPath = ["baz"]; // error
myPath = ["bar", "pir"]; // error
myPath = ["any", "random", "crap"]; // error

看起来不错

Playground link to code

Typescript相关问答推荐

如何获取嵌套对象属性的正确类型?

已定义但从未使用通用推断的错误

如何声明循环数组类型?

为什么文字必须硬编码才能在TypScript中工作?

ReturnType此索引方法与点表示法之间的差异

如何访问Content UI的DatePicker/中的sx props of year picker?'<>

使用ngrok解析Angular 时出现HTTP故障

如何将类型从变量参数转换为返回中的不同形状?

创建一个根据布尔参数返回类型的异步函数

具有泛型类方法的类方法修饰符

使用`renderToPipeableStream`时如何正确恢复服务器端Apollo缓存?

如何使用函数填充对象?

如何在编译时为不带常量的参数引发错误

如何将泛型对象作为返回类型添加到类型脚本中

如何在Type脚本中动态扩展函数的参数类型?

在TypeScript中扩展重载方法时出现问题

将超类型断言为类型脚本中的泛型参数

如何解决&Quot;类型不可分配给TypeScrip IF语句中的类型&Quot;?

如何使用TypeScrip在EXPO路由链路组件中使用动态路由?

三重融合视角与正交阵