【问题标题】:Nested keys generics嵌套键泛型
【发布时间】:2023-02-09 19:27:20
【问题描述】:

我想创建一个具有通用类型和 2 个属性的类型,这些属性将拆分指定类型的嵌套路径。

export type RecursiveKeyOf<TObj extends object> = {
    [TKey in keyof TObj & (string | number)]: TObj[TKey] extends any[]
    ? `${TKey}`
    : TObj[TKey] extends object
    ? `${TKey}` | `${TKey}.${RecursiveKeyOf<TObj[TKey]>}`
    : `${TKey}`;
}[keyof TObj & (string | number)];

type SubRecursiveKeys<RK extends string, PFX extends RK> = RK extends `${PFX}.${infer SubKey}` ? SubKey : never;

const a = {
    level1: {
        level2: {
            level3: {
                level4: {
                    level5: 'test'
                }
            }
        }

    }
}


type SubKeys = SubRecursiveKeys<RecursiveKeyOf<typeof a>, 'level1.level2'> // ok

// error here - does not satisfy the constraint 
type ShortHandKeyMapper<F extends object, Base extends RecursiveKeyOf<F> = RecursiveKeyOf<F>> = {
    control: Base                              // error here - does not satisfy the constraint 
    value: SubRecursiveKeys<RecursiveKeyOf<F>, Base>
}

const denyMapper: ShortHandKeyMapper<typeof a> = {
    control: 'level1.level2.level3',
    value: 'level2' // shouldnt allow this
}

const okMapper: ShortHandKeyMapper<typeof a> = {
    control: 'level1.level2.level3',
    value: 'level4' // should allow this or 'level4.level5'
}

ShortHandKeyMapper 是将 control 作为嵌套路径的前缀部分和 value 作为其余部分的类型。

我收到 does not satisfy the constraint,这毫无意义。

Playground

【问题讨论】:

    标签: typescript generics typescript-generics


    【解决方案1】:

    问题在RecursiveKeyOf,写错了。在这里使用 (string | number) TKey in keyof TObj &amp; (string | number) 是不正确的。

    请考虑这个例子:

    type RecursiveKeyOf<T, Cache extends string = ''> =
        T extends PropertyKey ? Cache : {
            [P in keyof T]:
            P extends string
            ? Cache extends ''
            ? RecursiveKeyOf<T[P], `${P}`>
            : Cache | RecursiveKeyOf<T[P], `${Cache}.${P}`>
            : never
        }[keyof T]
    
    type SubRecursiveKeys<RK extends string, PFX extends RK> = RK extends `${PFX}.${infer SubKey}` ? SubKey : never;
    
    const a = {
        level1: {
            level2: {
                level3: {
                    level4: {
                        level5: 'test'
                    }
                }
            }
    
        }
    }
    
    const infered = <Obj extends object,>(obj: Obj) =>
        <Control extends RecursiveKeyOf<Obj>>(
            control: Control,
            value: SubRecursiveKeys<RecursiveKeyOf<Obj>, Control>
        ) => ({
            control,
            value
        })
    
    const curry = infered(a)
    
    const _ = curry('level1.level2.level3', 'level4') // ok
    const __ = curry('level1.level2.level3', 'level4.level5') // ok
    const ___ = curry('level1.level2.level3', 'level5') // expected error
    

    Playground

    请查看相关答案:hereherehere

    另外,你可以查看我的article

    为了保证您的反对有效,您需要推断control,然后 TS 才能计算出value

    如果您不需要/不想使用函数进行推理,则可以生成对象的所有允许状态。看到这个:

    type RecursiveKeyOf<T, Cache extends string = ''> =
        T extends PropertyKey ? Cache : {
            [P in keyof T]:
            P extends string
            ? Cache extends ''
            ? RecursiveKeyOf<T[P], `${P}`>
            : Cache | RecursiveKeyOf<T[P], `${Cache}.${P}`>
            : never
        }[keyof T]
    
    type SubRecursiveKeys<RK extends string, PFX extends RK> = RK extends `${PFX}.${infer SubKey}` ? SubKey : never;
    
    const a = {
        level1: {
            level2: {
                level3: {
                    level4: {
                        level5: 'test'
                    }
                }
            }
    
        }
    }
    
    type Values<T> = T[keyof T]
    
    type ValidState<Obj> = Values<{
        [Prop in RecursiveKeyOf<Obj>]: {
            control: Prop,
            value: SubRecursiveKeys<RecursiveKeyOf<Obj>, Prop>
        }
    }>
    
    type Result = ValidState<typeof a>
    
    // expected error
    const denyMapper: Result = {
        control: 'level1.level2.level3',
        value: 'level2' // shouldnt allow this
    }
    
    // ok
    const okMapper: Result = {
        control: 'level1.level2.level3',
        value: 'level4' // should allow this or 'level4.level5'
    }
    

    Playground

    Result 类型只是所有允许状态的联合

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-03
      • 1970-01-01
      • 1970-01-01
      • 2023-03-12
      • 1970-01-01
      • 2019-03-10
      相关资源
      最近更新 更多