繁体   English   中英

打字稿通用对象图

[英]Typescript Generic Object Map

需要一些帮助来完成一个通用的object.map函数,我当前的实现在编译器上都非常慢到它破坏的程度(但是有效)并且目前我想不出推断ReturnType的好方法

type DeepKeyOf<T extends object> = {
    [K in keyof T]: T[K] extends object ? DeepKeyOf<T[K]> | { key: K; value: T[K] } : { key: K; value: T[K] }
}[keyof T];

function objectMap<T extends object, B>(obj: T, mapper: (value: DeepKeyOf<T>) => any): T {
    return Object.keys(obj).reduce((newObj, key) => {
        return { ...newObj, [key]: mapper(obj[key]) };
    }, {}) as any;
}

这将返回一个有区别的{key,value}对象文字对的并集,这样你就可以用密钥===“foo”检查密钥,并获得该密钥的类型保护值,这就是我想要的。

2个问题。

  1. 即使在非常小的接口上,DeepKeyOf也经常停止编译器工作(无法执行)
  2. 无法推断ReturnType(与最后的“T”相关)和mapper函数上的“any”

如果有人说他们可以用更高级别的类型来解决这个问题,那么给我一些伪代码,最好用HKT的scala语法,我可以写出一个应该完成它的实现。

编辑:

type DeepKeyOf<T extends object> = {
    [K in keyof T]: T[K] extends object ? DeepKeyOf<T[K]> | { key: K; value: T[K] } : { key: K; value: T[K] }
}[keyof T];
interface IPerson {
    name: "susan";
    children: {
        billy: string;
        sally: Date;
    };
}

将给予{key: "name", value: "susan"} | {key: "children", value: {billy: string, sally: Date} | {key: "billy", value: string} | {key: "sally", value: Date} {key: "name", value: "susan"} | {key: "children", value: {billy: string, sally: Date} | {key: "billy", value: string} | {key: "sally", value: Date}

编辑:这是iv'e得到的距离是多远,但只有当你删除“if key ==”dob“所有键被单独处理时才有效。”ReturnType出来的不是我想要的因为Dob是一个其他类型的联盟。

//我也只是将它变成浅层的object.map,以便在编译器和我自己上更容易。

export type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void)
    ? I
    : never;

type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
type ShallowKeyOf<T extends object> = { [K in keyof T]: { key: K; value: T[K] } }[keyof T];

interface IPerson {
    name: "susan";
    children: {
        billy: number;
        sally: Date;
    };
    dob: Date;
}

type GenerateRecord<Tuple extends [any, any]> = Tuple extends infer SELF
    ? SELF extends [infer K, infer V] ? (K extends string ? Record<K, V> : never) : never
    : never;

type ChangeValue<T extends object, Tuple extends [keyof T, any]> = Omit<T, Tuple[0]> extends infer SELF
    ? SELF & UnionToIntersection<GenerateRecord<Tuple>>
    : never;


function objectMap<T extends object, TupleReturn extends [keyof T & string, any]>(
    obj: T,
    mapper: (value: ShallowKeyOf<T>) => TupleReturn
): ChangeValue<T, TupleReturn> {
    return Object.keys(obj).reduce((newObj, key) => {
        return { ...newObj, [key]: mapper((obj as any)[key]) };
    }, {}) as any;
}

const test = objectMap(("" as any) as IPerson, ({ key, value }) => {
    if (key === "name") {
        return [key, new Date()];
    }
    if (key === "dob") {
        return [key, "NOW STRING"]
    }
    return [key, new Date()];
});

这是我通过拆分对象ey键和非对象ey键来采取的方法。 我认为它是一个更广泛的类型空间来extend object ,它通常给我带来了困难,因为它包含像Dates这样的东西。

type NonObject = Date | RegExp | string | number;

type ObjectValuedKeys<T> = {
  [K in keyof T]: T[K] extends NonObject ?
  never :
  { o: K }
}[keyof T]['o'];

type NonObjectValuedKeys<T> = {
  [K in keyof T]: T[K] extends NonObject ?
  { o: K } :
  never
}[keyof T]['o'];

type NonObjectKeyValues<T> = {
  [K in NonObjectValuedKeys<T>]: {
    key: K;
    value: T[K];
  }
}[NonObjectValuedKeys<T>];

type ObjectKeyValues<T> = {
  [K in ObjectValuedKeys<T>]: MapKeys<T[K]>
}[ObjectValuedKeys<T>];

type MapKeys<T> =
  {
    v: NonObjectKeyValues<T>,
    k: ObjectKeyValues<T>;
  }['v' | 'k'];

interface IPerson {
  name: 'susan';
  children: {
    billy: string;
    sally: Date;
    deeper: {
      norma: RegExp,
    }
  };
}

MapKeys<IPerson>给了我

{
    key: "name";
    value: "susan";
} | {
    key: "billy";
    value: string;
} | {
    key: "sally";
    value: Date;
} | {
    key: "norma";
    value: RegExp;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM