網站搜尋

typescript - 在迴圈中評估模板化的配置


我有一個系統配置(由我提供),它描述了用戶配置(由用戶提供)的能力。

然後我想傳遞使用者設定的每一部分,並根據系統設置進行評估。

為了說明我的意思,這裡有一些範例程式碼。它應該輸出兩行:

  • 123
  • 「隨便」
const systemConfig = {
  a: (arg: number) => arg,
  b: (arg: string) => arg,
}

type InferConfig<T extends {}> = { [K in keyof T]: T[K] extends (arg: infer U) => any ? U : never }

const userConfig: InferConfig<typeof systemConfig> = {
  a: 123,
  b: 'whateva',
};

// (everything above working as expected)

for (const key in userConfig) {
  if (key in systemConfig) {
    console.log(systemConfig[key](userConfig[key])); // correct output but highlights errors

    console.log(systemConfig[key as keyof typeof systemConfig](userConfig[key as keyof typeof userConfig])); // luckily doesn't work because I want to avoid "as" if possible
  }
}

操場

另一個要求是循環(程式碼中不起作用的部分)不得包含形如 “if (key === 'a') ...” 的邏輯(即不依賴 a)

答案#1

你的程式碼沒有包含對象類型 systemConfig 的索引簽名。這意味著任何使用 string 來索引 systemConfig 的嘗試都將被阻止。例如,我們可以這樣定義帶有索引簽名的對象:

const systemConfig: {[key: string]: (arg: any) => string | number} = {
  a: (arg: number) => arg,
  b: (arg: string) => arg,
}

在上述程式碼中,[key: string] 意味著可以使用 string 鍵來為 systemConfig 的屬性賦值。而 (arg: any) => string | number 意味著該值應該是一個函數,該函數接收一個 any 類型的參數並返回 string 或 number(你可以修改這個函數)

完成後,你可以移除這行多餘的程式碼並檢查它(遊樂場):

console.log(systemConfig[key as keyof typeof systemConfig](userConfig[key as keyof typeof userConfig]));

供您參考:TypeScript 的索引簽名實際上意味著什麼?

答案#2

通常我們先定義配置類型,然後再擴展轉換器。

但如果你真的想要它,請看下面的程式碼。

遊樂場

type ConfigTransformer = {
  [K in string]: (arg: any) => any
}

type ConfigInput<T extends ConfigTransformer> = {
  [K in keyof T]: T[K] extends (arg: infer U) => any ? U : never
}

type ConfigOutput<T extends ConfigTransformer> = {
  [K in keyof T]: T[K] extends (arg: any) => infer U ? U : never
}

const systemConfig = {
  a: (arg: number) => arg,
  b: (arg: string) => arg,
}

type SystemInput = ConfigInput<typeof systemConfig>
type SystemOutput = ConfigOutput<typeof systemConfig>

const userConfig: SystemInput = {
  a: 123,
  b: 'whateva',
};

function transform<T extends ConfigTransformer>(transformer: T, input: ConfigInput<T>){
  // you must do this `as` because typescript not infer the correct key type
  const keys = Object.keys(transformer) as (keyof T)[];
  for(const key of keys){
    console.log(transformer[key](input[key]))
  }
}

transform(systemConfig, userConfig);