【问题标题】:Typescript nested generics打字稿嵌套泛型
【发布时间】:2021-12-20 16:28:12
【问题描述】:

如何嵌套使用泛型的相同类型?

interface programConfig<T extends Record<string, any>> {
  // other types removed; not relevant to the question
  commands?: { [key: string]: programConfig<???> }; // how do I type this?
}

更完整的ts playground 示例显示了我正在尝试完成的工作

【问题讨论】:

  • 您希望这种类型是什么?如果您希望它完全相同,请传入T
  • @Chase - 我希望它成为自己的记录。该记录当前是根据调用代码推断出来的。如果我传入T,那么所有嵌套实例都使用与根实例相同的记录,这不是我想要的。例子应该更清楚
  • 我觉得这是不可能的。您需要 commands 是通用的,但在 TypeScript 中,只有类型别名、接口、类、构造函数、函数和方法可以是通用的,但属性、对象和值不能。你基本上需要commands?&lt;U&gt;: { [key: string]: programConfig&lt;U&gt; };github.com/microsoft/TypeScript/issues/17574

标签: typescript generics typescript-generics


【解决方案1】:

您可以指定第二个泛型来包含 programConfig 的子元素,在此示例中,我限制内部元素不允许第三级嵌套,因为支持任意嵌套会很烦人,希望没有必要

playground


interface BaseProgramConfig<T extends Record<string, unknown> >{
  options?: {
    [K in keyof T]: {
      validator?: () => T[K]
    }
  },
  handler?: (data: T) => void
}
interface programConfigWithCommands<T extends Record<string, unknown>, Sub extends Record<string, Record<string, unknown>>> extends BaseProgramConfig<T> {
  commands?: {[K in keyof Sub]: BaseProgramConfig<Sub[K]>}
}

class Program<T extends Record<string, unknown>, Comms extends Record<string, Record<string, unknown>>> {
  constructor(config: programConfigWithCommands<T,Comms>) { }
}

const foo = new Program({
  options: {
    'fruit': { validator: () => 'asdf' },
    'animal': { validator: Number },
  },
  handler: ({ fruit, animal, thing }) => { // fruit and animal are properly typed based on options above
    console.log(fruit, animal)
  },
  commands: {
    foo: {
      options: {
        'tree': { validator: () => 'asdf' },
        'person': {},
      },
      handler: ({ tree, person, thing }) => { // tree is typed as string, person is typed as unknown
        console.log(tree, person)
      },
    }
  }
});

【讨论】:

  • 我在玩这样的东西,但无法让它工作。这也不是,因为最后一个 handler 中的 args 仍然输入为 any。同样不幸的是,它确实需要任意级别的嵌套(或者实际上至少需要一个嵌套级别)。这是我试图不改变的遗留代码(只是添加类型),但我倾向于放弃嵌套配置以支持{commands: new Program({...})},这将是一个相当小的代码模块。
  • @Nobody 我更新了它,所以它可以在一层嵌套中正常工作,我不确定你如何用 3 层来做到这一点,因为我不确定这些子元素会在哪里被存储。
  • @TadhgMcDonald-Jensen 感谢您的编辑。我试过F-bounded quantification 但它不起作用
【解决方案2】:

您只需要再次致电new Program,如下所示:

type programConfig<T extends Record<string, any> = Record<string, any>> = {
  options?: {
    [K in keyof T]: {
      validator?: () => T[K]
    }
  },
  handler?: (data: T) => void,
  commands?: { [key: string]: Program<Record<string,unknown>> }; // here use sub-programs that have nothing to do with T
}

class Program<T extends Record<string, any> = Record<string, any>> {
  constructor(config: programConfig<T>) { }
}

const foo = new Program({
  options: {
    'fruit': { validator: () => 'asdf' },
    'animal': { validator: Number },
  },
  handler: ({ fruit, animal, thing }) => { // fruit and animal are properly typed based on options above
    console.log(fruit, animal)
  },
  commands: {
    foo: new Program({
      options: {
        'tree': { validator: () => 'asdf' },
        'person': {},
      },
      handler: ({ tree, person, thing }) => { // tree and person are typed in the same way and Program of any type is accepted in commands
        console.log(tree, person)
      },
    })
  }
});

Playground

【讨论】:

  • 是的,这是我的后备计划 :) 但它是在我们的代码库中使用的遗留代码,所以我试图避免它
猜你喜欢
  • 1970-01-01
  • 2021-09-10
  • 2021-12-06
  • 2020-12-17
  • 2019-07-30
  • 2015-11-27
  • 2016-10-19
  • 1970-01-01
  • 2021-11-26
相关资源
最近更新 更多