網站搜尋

Typescript,如何將一個物件映射到另一個物件?


我有兩個物件,以及我的自訂地圖函數,用於將一個物件的屬性映射到另一個物件的值,例如:

const obj1 = {
  host: 'clientNameHost',
  pass: 'clientNamePass',
};

const obj2 = {
  clientNamePass: '12345',
  clientNameHost: 'http://localhost:3000',
};

const mapParams = <T, K>(source: T, params: K) => Object.entries(params)
.reduce((acc, [param, sourceProp]) => ({ ...acc, [param]: source[sourceProp] }), {});

mapParams(obj2, obj1) //{host: "http://localhost:3000", pass: "12345"}

我在努力嘗試輸入這個函數時遇到了困難。有沒有什麼方法可以正確地輸入 mapParams 函數?

答案#1

解決方法是縮小泛型類型,以顯示兩個參數中鍵與值之間的關係:

const obj1 = {
    host: 'clientNameHost',
    pass: 'clientNamePass',
  } as const;

  const obj2 = {
    clientNamePass: '12345',
    clientNameHost: 'http://localhost:3000',
  } as const;

  const mapParams = <T extends Record<string, any>, K extends keyof T, P extends Record<T[K], any>>(source: T, params: P) => Object.entries(params)
  .reduce((acc, [param, value]) => ({ ...acc, [param]: source[value as T[K]] }), {});

  console.log(mapParams(obj1, obj2)) //{host: "http://localhost:3000", pass: "12345"}

主要部分:

  • T extends Record<string, any> 的第一種類型是具有字串鍵的 Record
  • K extends keyof T 代表 T 的鍵
  • P extends Record<T[K], any> 關鍵部分 - 我們想要一個物件,其鍵將等於 T 中的值
  • 為了正確推斷縮小後的類型,新增了 as const

上述類型在第一個參數值與第二個參數鍵之間建立了關聯。

附言:抱歉把參數的順序弄反了。

答案#2

當我撰寫函數時,我最關心的是參數和返回類型。

你可以這樣輸入參數:

<InputProps, TargetProps extends Record<keyof TargetProps, keyof InputProps>>(
  source: InputProps,
  params: TargetProps
) => ...

如果第二個物件與第一個不匹配,Typescript 會報錯。

對於返回值,你可以定義如下的工具類型:

type MapFrom<A, B> = Record<keyof A, B[keyof B]>;

透過使用 Object.prototype.entries 型別參數,我們可以避免必須對 sourceProp 進行轉型。

為了保持函數簽名,我們需要在 reduce 中斷言 initialValue 的類型。

我也使用 const 斷言來賦予物件字面量唯讀屬性

這是完整的結果:

type MapFrom<A, B> = Record<keyof A, B[keyof B]>;

const obj1 = {
  host: "clientNameHost",
  pass: "clientNamePass"
} as const;

const obj2 = {
  clientNamePass: "12345",
  clientNameHost: "http://localhost:3000"
} as const;

const mapParams = <InputProps, TargetProps extends Record<keyof TargetProps, keyof InputProps>>(
  source: InputProps,
  params: TargetProps
) =>
  Object.entries<keyof InputProps>(params).reduce(
    (acc, [param, sourceProp]) => ({
      ...acc,
      [param]: source[sourceProp]
    }),
    {} as MapFrom<TargetProps, InputProps>
  );

const obj3 = mapParams(obj2, obj1);

你可以在這裡試試看。