typescript Map嵌套对象的值,并保留泛型类型信息

xmakbtuz  于 2023-05-19  发布在  TypeScript
关注(0)|答案(1)|浏览(123)

我想Map第一个obj的所有对象值,并保留Wrapped对象的泛型类型。
我尝试过使用Map类型,但它在两个方面失败了:
1.我无法在MapToWrappedType中表示嵌套对象
1.无法确定如何表示类型,因此Map的类型不是Wrapper<ValueObject<...>>而是Wrapper<...>
出发地:

{
 a: ValueObject<number>,
 b: ValueObject<number>,
 c: {
   d: ValueObject<string>,
   e: ValueObject<number>
 }
}

收件人:

{
 a: Wrapper<number>,
 b: Wrapper<number>,
 c: {
   d: Wrapper<string>,
   e: Wrapper<number>
 }
}
class ValueObject<T> {
  public value: T;
  constructor(value: T) {
    this.value = value;
  }
}

class Wrapper<T> {
  public value: T;
  constructor(vo: ValueObject<T>) {
    this.value = vo.value;
  }
}

const obj = {
  a: new ValueObject<number>(1),
  b: new ValueObject<number>(2),
  c: {
    d: new ValueObject<string>("3"),
    e: new ValueObject<number>(4)
  }
} as const;

// 2 Problems:
// - how to express a nested object?
// - how to change the type to Wrapper<...> instead of Wrapper<Valueobject<...>>?
type MapToWrapped<T> = {
  [K in keyof T]: Wrapper<T[K]> 
}

function toWrappedObj<T>(obj: T): MapToWrapped<T> {
  const result: any = {};
  for (const key in obj) {
    const item = obj[key];
    if (item instanceof ValueObject) {
      result[key] = new Wrapper<any>(item.value);
    } else {
      result[key] = toWrappedObj(item);
    }
  }
  return result;
}

const wrappedValuesObj = toWrappedObj(obj);
const x = wrappedValuesObj.a // should be Wrapper<number>

CodeSandbox

xpcnnkqh

xpcnnkqh1#

我们需要使用mapped types和inferor关键字来删除ValueObject,并将其替换为Wrapper
来自docs的infer示例:

type Flatten<Type> = Type extends Array<infer Item> ? Item : Type;

type Result = Flatten<Array<number>> // number

因此本例检索数组项的类型;我们可以使用相同的方法,但使用ValueObject

Type extends ValueObject<infer Item> ? Item : Type;

然后我们可以将Item Package 在Wrapper中以获得预期的结果。
对于更深的层次,如果mapped属性不是ValueObject,我们将递归调用我们的mapper类型,假设不能嵌套ValueObject。此外,为了使结果类型更具可读性,我们将使用Prettify实用程序类型:

type Prettify<T> = T extends infer Result
  ? {
      [K in keyof Result]: Result[K];
    }
  : never;
type MapToWrapped<T> = Prettify<{
  [K in keyof T]: T[K] extends ValueObject<infer Value>
    ? Wrapper<Value>
    : MapToWrapped<T[K]>;
}>;

function toWrappedObj<T>(obj: T): MapToWrapped<T> {...}

使用方法:

const obj = {
  a: new ValueObject<number>(1),
  b: new ValueObject<number>(2),
  c: {
    d: new ValueObject<string>('3'),
    e: new ValueObject<number>(4),
  },
} as const;

// const wrappedValuesObj: {
//   readonly a: Wrapper<number>;
//   readonly b: Wrapper<number>;
//   readonly c: {
//       readonly d: Wrapper<string>;
//       readonly e: Wrapper<number>;
//   };
// }
const wrappedValuesObj = toWrappedObj(obj);
const x = wrappedValuesObj.a; // Wrapper<number>

Playground

相关问题