簡體   English   中英

Typescript 來自 object 的類型推斷

[英]Typescript type inference from object

我正在嘗試讓 TypeScript 正確推斷以下代碼示例中的輸入:

type User = {
  name: string
}
type Product = {
  price: number
}

const userConfig = {
  action: () => [{name: 'John'}] as User[],
}
const productConfig = {
  action: () => [{price: 123}] as Product[],
}

const entityConfig = {
  userConfig,
  productConfig,
}

export const makeApp = <K extends string, R>({
  config,
}: {
  config: Record<K, {action: () => R}>
}) => {
  const actions: Record<K, {action: () => R}> = Object.keys(config).reduce(
    (acc, curr) => {
      const c: {action: () => R} = config[curr as K]
      return {
        ...acc,
        [curr]: c.action(),
      }
    },
    {} as any,
  )

  return {actions}
}

const app = makeApp({config: entityConfig})

const users = app.actions.userConfig.action() // Correctly typed as User[]
const products = app.actions.productConfig.action() // Incorrectly typed as User[] instead of Product[]

我在const app = makeApp({config: entityConfig})行看到以下 TypeScript 錯誤

Type '{ userConfig: { action: () => User[]; }; productConfig: { action: () => Product[]; }; }' is not assignable to type 'Record<"userConfig" | "productConfig", { action: () => User[]; }>'.
  The types returned by 'productConfig.action()' are incompatible between these types.
    Type 'Product[]' is not assignable to type 'User[]'.
      Property 'name' is missing in type 'Product' but required in type 'User'.ts(2322)

如何將app.actions.userConfig.action()app.actions.productConfig.action()調用的返回類型分別正確推斷為User[]Product[]

您應該推斷傳入的密鑰 object 並通過{ [key in keyof T ]: T[key] }}構造一個新接口:

const makeApp = <T, K extends keyof T>({ config }: { config: T }): { actions: { [key in keyof T ]: T[key] } } => {
  const actions: { [key in keyof T ]: T[key] } = Object.keys(config).reduce(
    (acc, curr) => {
      const c = config[curr as K] as unknown as { action: () => void };
      return {
        ...acc,
        [curr]: { action: c.action },
      }
    },
    {} as any,
  )
  return { actions };
}

因為 reducer 不知道傳入的 object 屬性包含action鍵,所以可以使用config[curr as K] as unknown as { action: () => void }; .

游樂場鏈接

我不確定你問的問題是否正確,或者我誤解了它。 您已要求從{ config: entityConfig }形式的 object 開始,並為某些 function makeApp 執行const app = makeApp({config: entityConfig}) 然后您希望const products = app.actions.productConfig.action()工作並正確輸入。

然而,這是相當微不足道的,因為 entityConfig 已經是正確的形式,所以我們需要做的就是交換我們的密鑰。 它不需要減少 function。下面的 function 有效:

const makeApp = <T>({ config }: { config: T }): { actions: T } 
    => ({ actions: config })

完整代碼:

type User = {
  name: string
}
type Product = {
  price: number
}

const userConfig = {
  action: () => [{name: 'John'}] as User[],
}
const productConfig = {
  action: () => [{price: 123}] as Product[],
}

const entityConfig = {
  userConfig,
  productConfig,
}

const makeApp = <T>({ config }: { config: T }): { actions: T } 
    => ({ actions: config })

const app = makeApp({config: entityConfig})

const users = app.actions.userConfig.action() // Correctly typed as User[]
const products = app.actions.productConfig.action()  // Correctly typed as Product[]

console.log(users[0].name) // John
console.log(products[0].price) // 123

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM