打字稿递归包括,递归选择

时间:2020-09-04 20:24:00

标签: typescript typescript-typings typescript-generics

我有很多接口类型,这些接口类型扩展了其他一些“未知”位和嵌套类型,并且我想递归地提取一些我关心的属性。这就是我的想法

//pseudocode
type RecursiveInclude< T,  keys> = // implementation here

type A = {
  keyToKeep: string
  keyToOmit: string
  nested: {
    keyToKeep: string
    keyToOmit: string
    foo:string,
    nested2:{
       keyToKeep: string
       keyToOmit: string
    }
    
  }
  nestedOptional?: {
    keyToKeep: string
    keyToOmit: string
  }
}

// type Result = RecursiveInclude<A, 'keyToKeep' |  'foo' | 'anyotherproperty'>

type Expected = {
  keyToKeep: string
  nested: {
    keyToKeep: string
    foo:string,
    nested2:{
       keyToKeep: string
    }
  }
  nestedOptional?: {
    keyToKeep: string
  }
} 

//任何想法或近似值都欢迎,所以不要害羞

https://stackoverflow.com/users/125734/titian-cernicova-dragomir

给我一​​些启发,这是他到目前为止所创造的,

https://www.typescriptlang.org/play?#code/LAKALgngDgpgBABQJYGMDWARJBnMAnJAIwFcwkA3GAHgBUAaOAaThgA8wYA7AE20TwD2sPJEYwIAPjgBeODRbsuvOAENOEOAH44ACnlsOPPgMIArGCjBa4ASW5Vk6AEoXiebBRgAbCLQaMJKQAuOQBKOBDOGEo8AG5QSFhbexopWQBvAF84ADI4dLgAbQQ4JE44NHEBADM5AF0IuWK67IB6VrgAYQFsAFsYMhQ4Ymx4AU4fOF6VSr4wAAt4MAEBLzIoPjYoFW44BaXoeBQ1OEJ4PBhegUpdhMPEVDQXFDcPSh9aBUNlNQh-L6UfAQgmEonEaQe6CooDgsPyRRKZQqVVqNDqIUcmBw+CIpE8tGa-ikmToMLhAFF2HgVJYqIwGJUIDU5BJQKyQKA7kkAIIyfJkxk0ARiGBQEK4AicADmAvEQoA8r0kGBxTjpWSorgYNwQukybDBcKYKLVZKZSA4ciIAqlSq4BKyubLdUVkEHeqLXBMhqYFruPKoGRxiovJpdfqrUKRWL7WqnXDDYrlabHWTvSB01z4C5sMQ1nzMc9Xp4PtyGAByQ3R8twAA+cHLLoENfr5d+An2eCgIJgeAg5fZWbglNglm1fL1nqrxpj7vjmo4Ov5noNcqNJtjZojTbdcbTPr9AaDnBDYeXlunG7n+4zcFAQA

唯一需要注意的是,您需要了解所有键,以便可以遍历所有属性 表示您需要提供该类型上的所有键

'keyToKeep' |  'nested' | 'nested2' | 'nestedOptional' | 'foo' | 'anyotherproperty'

更近了,但是最初的问题我无法访问该信息->表示此键

|  'nested' | 'nested2' | 'nestedOptional' |

2 个答案:

答案 0 :(得分:1)

这是一种继续进行的方法:

type OmitNever<T> = Omit<T,
  { [K in keyof T]-?: T[K] extends never ? K : never }[keyof T]
>;
type RecursivePick<T extends object, K extends PropertyKey> = OmitNever<{
  [P in keyof T]: P extends K ? T[P] : (
    T[P] extends infer O ? O extends object ? RecursivePick<O, K> : never : never
  )
}> extends infer O ? { [P in keyof O]: O[P] } : never

您可以验证它是否适用于您的类型:

type Result = RecursivePick<A, 'keyToKeep' | 'foo' | 'anyotherproperty'>
/* type Result = {
    keyToKeep: string;
    nested: {
        keyToKeep: string;
        foo: string;
        nested2: {
            keyToKeep: string;
        };
    };
    nestedOptional?: {
        keyToKeep: string;
    } | undefined;
} */

这里的方法是遍历您的对象并保留每个属性(如果其键在K中)。如果键是K中的 not ,请检查该属性是否为对象。如果不是,则要完全忽略该属性。如果是这样,那么您想使用RecursivePick递归到属性中。

请注意,在使用映射类型进行迭代时,无法省略属性。因此,我将属性设置为never,然后在结果对象上运行OmitNever<T>。这样做的副作用是,将消除类型为never的任何嵌套属性:

type Oops = RecursivePick<{a: never}, "a"> // {}

如果这很重要,我们可以使用其他一些标记来更改RecursivePick的定义以保留never属性,但我对此表示怀疑。

Playground link

答案 1 :(得分:0)

TypeScript仅在这里帮助您进行编译时错误检查。它已编译为JavaScript,并且在运行时不执行任何操作。就像矩阵中的汤匙一样,类型和接口不存在。

创建类型以选择对象中的属性只能在编写代码时帮助您选择那些对象。不会在最终代码中将它们从对象中删除。

如果在编译时无法访问信息,则必须求助于属性,并确保它们通过JavaScript手段存在。您可以创建类型保护,以帮助您做到这一点,同时仍保留一些您知道的类型。

https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-guards-and-differentiating-types