简体   繁体   English

打字稿通用对象图

[英]Typescript Generic Object Map

Need some help finishing off a generic object.map function, my current implementation is both extremely slow on the compiler to the point where it breaks (but works) and currently i can't think of a good way to infer the ReturnType 需要一些帮助来完成一个通用的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;
}

This will give back a discriminated union of {key, value} object literal pairs such that you can check the key with key === "foo" and get the typeguarded value of that key back which is what i want. 这将返回一个有区别的{key,value}对象文字对的并集,这样你就可以用密钥===“foo”检查密钥,并获得该密钥的类型保护值,这就是我想要的。

2 problems. 2个问题。

  1. DeepKeyOf often stops compiler working (unperformant) even on very small interfaces 即使在非常小的接口上,DeepKeyOf也经常停止编译器工作(无法执行)
  2. Can't infer ReturnType (related to "T" on very end) and 'any' on mapper function 无法推断ReturnType(与最后的“T”相关)和mapper函数上的“any”

If someone says they can solve the problem with higher-kinded types give me the pseudo code perferably with scala syntax for HKT"s and i can write out a implementation that should finish it off. 如果有人说他们可以用更高级别的类型来解决这个问题,那么给我一些伪代码,最好用HKT的scala语法,我可以写出一个应该完成它的实现。

EDIT: 编辑:

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;
    };
}

will give back {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} {key: "name", value: "susan"} | {key: "children", value: {billy: string, sally: Date} | {key: "billy", value: string} | {key: "sally", value: Date}

EDIT: This is how far iv'e gotten which is close, but it only works when all keys are handled individually if you remove the "if key == "dob"" the ReturnType comes out as not what i want because Dob is a union of the other types. 编辑:这是iv'e得到的距离是多远,但只有当你删除“if key ==”dob“所有键被单独处理时才有效。”ReturnType出来的不是我想要的因为Dob是一个其他类型的联盟。

// also i went with just making it shallow object.map to make it easier on the compiler and myself. //我也只是将它变成浅层的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()];
});

Here is an approach I take by breaking apart the object-ey keys and non-object-ey keys. 这是我通过拆分对象ey键和非对象ey键来采取的方法。 I think its a much wider type space to extend object , and it typically gives me a hard time because it includes things like Dates. 我认为它是一个更广泛的类型空间来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> gives me 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