递归类型不适用于可选属性

问题描述 投票:0回答:1

我有一个类型定义,允许我通过表示对象或嵌套数组的键的字符串/索引数组来导航对象:

export type PredicateFunction<ArrayType> = (array: ArrayType, index?: number) => boolean;
export type IndexOrPredicateFunction<Type> = number | PredicateFunction<Type>;
export type StatePathKey = IndexOrPredicateFunction<any> | string;

export type StatePath<Obj, Path extends (string | IndexOrPredicateFunction<any>)[] = []> =
    object extends Obj
        ? Path
        : Obj extends object
            ? (Path |
                    // Check if object is array
                    (Obj extends readonly any[] ?
                        // ...when array only allow index or PredicateFunction
                        StatePath<Obj[number], [...Path, IndexOrPredicateFunction<Obj[number]>]>
                        // ...when object generate type of all possible keys
                        : { [Key in string & keyof Obj]: StatePath<Object[Key], [...Path, Key]> }[string & keyof Obj]))
            : Path;

这适用于例如这个界面:

interface State1  {
    test: {
        nestedTest: boolean
    }
}

像这样:

const t1: StatePath<State1> = ['test', 'nestedTest']; 

但是一旦我有了可选属性,它就会崩溃:

interface State2  {
    test: {
        nestedTest?: boolean
    }
}

知道如何解决这个问题吗?我已经尝试在该类型上使用

-?
但没有成功。

在此处查找用于复制的沙箱:Typescript Playground

typescript recursion
1个回答
0
投票

解决此特定问题的最简单方法是将支票从

object extends Obj
更改为
object extends Required<Obj>
。如果
Obj
结果是一个 弱类型,这意味着它是一个对象类型,其中 all 的属性都是 可选,那么 TypeScript 将看到空对象类型
{}
object
类型可分配给它。 例如,
object extends {a?: string, b?: number}
true。但随后该类型会在您不希望它出现的地方退出。

有多种方法可以继续,但是通过使用

Required<T>
实用程序类型,您可以将
object
与需要可选属性的类型版本进行比较。虽然
object extends {a?: string, b?: number}
为 true,但
object extends Required<{a?: string, b?: number}>
(又名
object extends {a: string, b: number}
)为 false。 因此,现在该类型将不会退出,除非
Obj
确实为空,或
object
,或
unknown
等。

Playground 代码链接

© www.soinside.com 2019 - 2024. All rights reserved.