【问题标题】:Create object based on types typescript根据类型 typescript 创建对象
【发布时间】:2021-11-25 12:07:53
【问题描述】:

免得说我有这种类型

type foo = {
 go: string;
 start: string;
}

我怎样才能动态地创建一个返回的函数

{ go: '', start: '' }

在 Type Script 上有什么方法可以让我们仅根据类型动态生成空对象吗?或者这是不可能的,因为我们不能循环

我在想这样的事情

function generate<T>(T)<T>  {
 const obj = {}
 for (const key of keyof T) {
   obj[key] = ''
 }
 return obj
}

【问题讨论】:

  • 不,这是不可能的。当您将 TypeScript 编译为 JavaScript 时,类型会被擦除;您不能在运行时使用reflection 来观察foo。为什么你认为你需要这个功能?您可以开始构建类似模式的功能,这样就会有一个像const fooSchema = {go: "string", start: "string"} as constgenerate things from that 这样的object,但我不知道您是否真的在寻找它。如果你是,它需要由某人建造。
  • 只是出于好奇~问题是我已经有了一个大接口,所有的都是字符串。如果我可以遍历那个界面,我想我可以用它产生一些价值。
  • 好吧,你不能用纯打字稿迭代一个接口;您需要编写或使用像 TS 转换器这样的代码生成器。 (例如,npmjs.com/package/ts-auto-mock)如果你只想使用 TypeScript,你必须在运行时自己做。你想要this beginning of a general schema solution吗?或者你想iterate over an array of key names?我正在寻求您的指导,这样我写的任何答案都会有重点,而不是二十页长(我倾向于写长答案)
  • 谢谢,我认为迭代键名数组对我的问题要好得多。它足够小,不需要我创建一个完整的对象。

标签: typescript


【解决方案1】:

当您运行 TypeScript 编译器 (tsc) 以将 TypeScript 代码转换为可运行的 JavaScript 时,该语言的静态类型系统为 erased。因此,您的 Foo 类型(重命名为大写以符合 TS 命名约定)在运行时不会以任何形式出现。没有什么可以迭代来获取密钥 gostart


在运行时让某些事情发生的最直接的方法是编写执行此操作所需的 JavaScript,然后确保 TypeScript 编译器可以在您编写它时为您提供所需的强类型。这基本上是您尝试做的事情的逆向

在您的情况下:generate() 需要什么才能在运行时工作?好吧,如果我们可以假设generate() 生成的值将是只包含string-valued 属性的对象,那么我们需要向它传递对象的键列表。那么,如果我们写 generate() 来做到这一点,然后根据 generate() 的输出定义 Foo 而不是相反呢?

例如:

function generate<K extends PropertyKey>(...keys: K[]) {
  return Object.fromEntries(keys.map(k => [k, ""])) as { [P in K]: string };
}

const myFooObject = generate("go", "start");

type Foo = typeof myFooObject;
/* type Foo = {
    go: string;
    start: string;
} */

console.log(myFooObject)
/* {
  "go": "",
  "start": ""
} */

这里的generate() 是一个generic 函数,它接受一个键列表(K 类型)并生成一个类型的值,其中键在K 中,值类型为string{[P in K]: string} 是一个 mapped type 等价于 Record&lt;K, string&gt; 使用 the Record&lt;K, V&gt; utility type

实现使用Object.fromEntries() 构建对象,返回类型为asserted 是正确的类型,因为TypeScript 将Object.fromEntries() 视为返回的类型对于我们的目的来说太宽了。

无论如何,当您调用const myFooObject = generate("go", "start") 时,它会产生一个{go: string; start: string} 类型的值,这与您的Foo 类型相同。所以我们可以将Foo 定义为type Foo = typeof myFooObject,而不是手动进行。您可以仍然手动完成,但我在这里展示的一点是,如果您从值开始并从它们生成类型,那么在 TypeScript 中编写 DRY code 会容易得多,而不是尝试反之亦然。


同样,如果您按原样使用 TypeScript 编译器 tsc,则类型擦除会阻止您从 Foo 的定义中写入 generate()。但是……

如果您愿意在您的项目中添加构建步骤并使用TypeScript compiler API 或类似的方式执行code generation,那么您可以对类型执行任意操作。有些库可以做与您想要的类似的事情;例如,ts-auto-mock 声称在给定对象类型的情况下生成模拟对象,这看起来与您的用例完全一样。

这样一个额外的构建步骤对您来说可能是一种合理的方法,但如果您沿着这条路线走,您应该注意您不再只使用普通的 TypeScript(因此该主题可能超出了与只是一个 TypeScript 标记)。

Playground link to code

【讨论】:

    【解决方案2】:

    你可以把它变成通用的,像这样:

    type foo<T extends string = string> = {
        go: T;
        start: T;
    }
    
    const test: foo<''> = {
        go: '',
        start: '',
    };
    

    TypeScript Playground

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-09-12
      • 2020-09-19
      • 2021-08-02
      • 2023-01-20
      • 2021-05-10
      • 1970-01-01
      • 2018-06-19
      • 2021-12-20
      相关资源
      最近更新 更多