NodeJS 基于对象键的递归函数类型

egmofgnx  于 2023-01-25  发布在  Node.js
关注(0)|答案(1)|浏览(114)

我试图创建一个递归函数类型,它将对象类型中最外层的键作为参数,并返回一个新函数,然后递归地从给定的键中获取对象的下一个最外层的键,直到没有更多的键可用。
如果键不扩展/${string},则参数必须是包含键和属性的对象类型:

type Argument<T> = T extends `/${string}` ? T : { [key in T]: string | number }
    • 每个. level只有一个不会扩展/${string}的密钥。**

示例

const values = {
    '/aaa': {
        '/lorem': {
            foo: 'hello world',
            '/boo': 12345,
        },
    },

    bbb: {
        '/ipsum': {
            '/dolor': 'lorem ipsum',
            amet: 567890,
        },
    },

    '/ccc': ...
};

foo<typeof values>('/aaa')('/lorem')({ foo: '...' }); // should return type of string
foo<typeof values>('/aaa')('/lorem')('/boo'); // should return type of number
foo<typeof values>('/aaa')('/ipsum'); // should fail

foo<typeof values>({ bbb: '...' })('/ipsum')({ amet: '...' }); // should return type of number
foo<typeof values>({ bbb: '...' })('/ipsum')('/dolor'); // should return type of string
foo<typeof values>({ bbb: '...' })('/lorem'); // should fail

我的当前代码

我有一个类型,几乎做的工作。,但不与非/${string}扩展:(

type Foo<T extends object> = <K extends keyof T>(args: K) => T[K] extends object ? Foo<T[K]> : T[K];

const values = {
    '/aaa': {
        '/lorem': {
            foo: 'hello world',
            '/boo': 12345,
        },
    },

    bbb: {
        '/ipsum': {
            '/dolor': 'lorem ipsum',
            amet: 567890,
        },
    },
};

const foo = {} as Foo<typeof values>

foo('/aaa')('/lorem')('/boo') // works :D
foo('/aaa')('/lorem')({ foo: '...' }) // fails :(

我尝试在函数中处理args类型-但它无法返回下一个键:(

type Foo<T extends object> = <K extends keyof T>(
    args: K extends `/${string}` ? K : { [key in K]: string | number }
) => T[K] extends object ? Foo<T[K]> : T[K];

const values = {
    '/aaa': {
        '/lorem': {
            foo: 'hello world',
            '/boo': 12345,
        },
    },

    bbb: {
        '/ipsum': {
            '/dolor': 'lorem ipsum',
            amet: 567890,
        },
    },
};

const foo = {} as Foo<typeof values>

foo('/aaa')('/lorem')('/boo') // works :D
const a = foo({ bbb: '...' })('/ipsum') // fails :(

我甚至不知道我想尝试的是不是可能--但是如果你有任何建议,你会拯救我的一天:)
谢谢你的时间。

kmbjn2e3

kmbjn2e31#

嗯,问题似乎出在{[key in K]: string | number}上,很明显,typescript不知道T[K]在那之后是否是一个对象。如果你把它改为{[key: string]: K},它就能工作(但{[key: string|number]: K}不行)。这似乎是Typescript的问题(我使用的是4.7.4版本)。
我能做的最好的事情是使用第二个参数而不是对象:

type Foo<T extends object> = <K extends keyof T>(
  arg: K,
  val?: K extends `/${string}` ? undefined : string|number
) => T[K] extends object ? Foo<T[K]> : T[K];

const foo = {} as Foo<typeof values>

const boo = foo('/aaa')('/lorem')('/boo') // TS: boo is number
const ipsum = foo('bbb', 12)('/ipsum') // TS: ipsum is Foo<{'/dolor': string, amet: number}>

相关问题