动态编程的类型

时间:2019-01-31 18:43:05

标签: typescript dynamic-programming typescript-typings

type someObj = {
    a: number,
    b: Array<string>,
    c: { d: string },
    e: {
        f: string,
        g: Array<{
            h: string,
            i: Array<number>
        }>
    },
    j: {
        k: string,
        l: Array<string>
    },
    m: Array<Array<string>>
};

type someGeneric<T /* can be primitive, array or object */> = ???;
// Expected Result


type someObjGenericResult = {
    b: (item: string) => void,
    e: {
        g: (item: { h: string, i: Array<number> }) => {
            i: (item: number) => void
        }
    },
    j: { l: (item: string) => void },
    m: (item: Array<string>) => (item: string) => void 
};

所以我一直在寻找一种泛型,该泛型将接受任何值并在对象中可能存在的每个数组上执行一个函数(无论其嵌套如何) 并具有以下属性

=>排除所有原语(例如someObj.a)

=>排除不具有以数组为类型(例如someObj.c)的道具的对象

=>但包含具有数组作为对象的对象(例如someObj.j)。

=>用户正在寻找具有嵌套数组(例如someObj.m)的数组。

1 个答案:

答案 0 :(得分:1)

由于问题更改而更新

如果您只是在寻找打字方法(而不是将someObj的实例转换为someObjGenericResult的实例的运行时代码),请思考以下将起作用:

// HasNestedArrays<T> outputs unknown (meaning true) or never (meaning false)
// depending on whether T is an array or an object with at least one
// property that HasNestedArrays itself.  This is a recursive definition.
// HasNestedArrays<{a: {b: {c: string[]}}}> should return unknown,
// HasNestedArrays<{a: {b: {c: string}}}> should return never.
type HasNestedArrays<T> = T extends Array<any> ? unknown :
  T extends object ? { [K in keyof T]: HasNestedArrays<T[K]> }[keyof T] : never;

// PickHasNestedArrays<T> takes an object type T and removes any properties
// that do not pass the HasNestedArrays test. 
// PickHasNestedArrays<{a: string, b: string[], c: {d: string[]}}> should return
// {b: string[], c: {d: string[]}}.
type PickHasNestedArrays<T extends object> = Pick<T,
  { [K in keyof T]: unknown extends HasNestedArrays<T[K]> ? K : never }[keyof T]
>

// SomeGeneric<T> is the type you want: 
// If T is an array like U[], return a function (item: U) => SomeGeneric<U>.
// If T is an object, strip all the non-array-containing properties out
// (via PickHasNestedArrays<T>) and assign the stripped object to V.
// If V is an empty object, then return void. 
// Otherwise, return a new object whose properties have the same keys K from V
// but whose values are SomeGeneric<V[K]>.
type SomeGeneric<T> = T extends Array<infer U> ?
  (item: U) => SomeGeneric<U> :
  T extends object ? (
    PickHasNestedArrays<T> extends infer V ? (
      {} extends V ? void : { [K in keyof V]: SomeGeneric<V[K]> }
    ) : never
  ) : void;

type someObjGenericResult = SomeGeneric<someObj>;

请注意,在TypeScript中,unknown是所谓的top type,其中包括所有值……基本上是类型的“ true”。 never是所谓的bottom type,其中不包含任何值……基本上是类型的“假”。当我执行类似布尔类型的操作时,我倾向于为unknown / never输出true / false,因为它有时会使后续操作更容易。

HasNestedArrays<T>PickHasNestedArrays<T>类型用于清除不需要的T的所有属性。 SomeGeneric<T>是您要求的类型函数。我已经在上面的注释中解释了它们的作用,但是它们的工作原理需要理解conditional typesmapped typeslookup types 。如果您了解所有符号,并且在某些情况下需要更多有关发生的事情的详细信息,我可以详细说明。

我不确定SomeGeneric<T>是否确实是您真正想要的东西,因为存在SomeGeneric<number>SomeGeneric<{a: number}>等边缘情况。可以肯定的是,上述定义在大多数情况下都会出void

无论如何,我希望这会有所帮助。祝你好运!