awesome-typescript icon indicating copy to clipboard operation
awesome-typescript copied to clipboard

「重学TS 2.0 」TS 练习题第四十五题

Open semlinker opened this issue 2 years ago • 4 comments

实现 JsonifiedObject 工具类型,用于对 object 对象类型进行序列化操作。具体的使用示例如下所示:

type JsonifiedObject<T extends object> = // 你的实现代码

type MyObject = {
  str: "literalstring",
  fn: () => void,
  date: Date,
  customClass: MyClass,
  obj: {
    prop: "property",
    clz: MyClass,
    nested: { attr: Date }
  },
}

declare class MyClass {
  toJSON(): "MyClass";
}

/**
 * type JsonifiedMyObject = {
 *  str: "literalstring";
 *  fn: never;
 *  date: string;
 *  customClass: "MyClass";
 *  obj: JsonifiedObject<{
 *    prop: "property";
 *    clz: MyClass;
 *    nested: {
 *      attr: Date;
 *    };
 *   }>;
 * }
*/
type JsonifiedMyObject = Jsonified<MyObject>;
declare let ex: JsonifiedMyObject;
const z1: "MyClass" = ex.customClass;
const z2: string = ex.obj.nested.attr;

请在下面评论你的答案

semlinker avatar Oct 01 '21 10:10 semlinker

declare class MyClass {
  toJSON(): "MyClass";
}
type sss = never extends void ? true : false;
type Jsonified<T extends object> = {
  [K in keyof T]: T[K] extends { toJSON(): infer Return }
    ? ReturnType<T[K]["toJSON"]>
    : T[K] extends (...arg: any[]) => any
    ? never
    : T[K] extends object
    ? Jsonified<T[K]>
    : T[K];
};

type MyObject = {
  str: "literalstring";
  fn: () => void;
  date: Date;
  customClass: MyClass;
  obj: {
    prop: "property";
    clz: MyClass;
    nested: { attr: Date };
  };
};

/**
 * type JsonifiedMyObject = {
 *  str: "literalstring";
 *  fn: never;
 *  date: string;
 *  customClass: "MyClass";
 *  obj: JsonifiedObject<{
 *    prop: "property";
 *    clz: MyClass;
 *    nested: {
 *      attr: Date;
 *    };
 *   }>;
 * }
 */
type JsonifiedMyObject = Jsonified<MyObject>;
declare let ex: JsonifiedMyObject;
const z1: "MyClass" = ex.customClass;
const z2: string = ex.obj.nested.attr;

sunboyZgz avatar Oct 02 '21 05:10 sunboyZgz

type Jsonified<T extends object> = {
  [k in keyof T]: T[k] extends object
    ? "toJSON" extends keyof T[k]
      ? T[k]["toJSON"] extends (...args: any[]) => infer R
        ? R
        : never
      : T[k] extends (...args: any[]) => any
        ? never
        : Jsonified<T[k]>
    : T[k];
};

type MyObject = {
  str: "literalstring";
  fn: () => void;
  date: Date;
  customClass: MyClass;
  obj: {
    prop: "property";
    clz: MyClass;
    nested: { attr: Date };
  };
}

declare class MyClass {
  toJSON(): "MyClass";
}

/**
 * type JsonifiedMyObject = {
 *  str: "literalstring";
 *  fn: never;
 *  date: string;
 *  customClass: "MyClass";
 *  obj: JsonifiedObject<{
 *    prop: "property";
 *    clz: MyClass;
 *    nested: {
 *      attr: Date;
 *    };
 *   }>;
 * }
*/
type JsonifiedMyObject = Jsonified<MyObject>;
declare let ex: JsonifiedMyObject;
const z1: "MyClass" = ex.customClass;
const z2: string = ex.obj.nested.attr;

思路: 比较费事儿一些,要枚举所有可能遇到的情况,测试用例对于function类型没有详细说明,我按照never统一输出的。如果需要考虑函数的返回值的话,多一层 extends 判断。

zhaoxiongfei avatar Oct 02 '21 11:10 zhaoxiongfei

type Jsonified<T extends object> = { [K in keyof T]: changeType<T[K]> }

type changeType<T> = [T] extends [string] ? T : T extends (...args: any) => infer R ? R & 1 : T extends Date ? string : T extends { toJSON(): infer C } ? C : T extends object ? { [K in keyof T]: T[K] extends { toJSON(): infer C } ? C : changeType<T[K]> } : false

jackwangwj avatar Dec 17 '21 03:12 jackwangwj

type Jsonified<T> = T extends Date
  ? string
  : T extends MyClass
  ? "MyClass"
  : T extends () => void
  ? never
  : T extends object
  ? {
      [K in keyof T]: Jsonified<T[K]>;
    }
  : T;

zjxxxxxxxxx avatar Mar 24 '22 09:03 zjxxxxxxxxx