简体   繁体   English

根据类型 typescript 创建 object

[英]Create object based on types typescript

Lest say I have this type免得说我有这种类型

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

How can I dynamicaly made a function that will return我怎样才能动态地制作一个 function 会返回

{ go: '', start: '' }

Is there any way on Type Script we could dynamically generate empty object based on solely just type?有什么方法可以在 Type Script 上动态生成空 object 仅基于类型? Or this is impossible because we cant just loop或者这是不可能的,因为我们不能只是循环

I was thinking something like these我在想这样的事情

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

When you run the TypeScript compiler ( tsc ) to transpile TypeScript code into runnable JavaScript, the language's static type system is erased . When you run the TypeScript compiler ( tsc ) to transpile TypeScript code into runnable JavaScript, the language's static type system is erased . So your Foo type (renamed to uppercase to meet TS naming conventions) is not present in any form at runtime.因此,您的Foo类型(重命名为大写以满足 TS 命名约定)在运行时不会以任何形式出现。 There is nothing you can iterate over to get the keys go and start .没有什么可以迭代来获取密钥gostart


The most straightforward way to get something to happen at runtime is to write the JavaScript necessary to do it, and then to make sure the TypeScript compiler can give you the strong types you need while you're writing it.在运行时让某些事情发生的最直接的方法是编写必要的 JavaScript,然后确保 TypeScript 编译器可以在您编写它时为您提供所需的强类型。 This is basically the reverse of what you're trying to do.这基本上与您尝试做的相反

In your case: what does generate() need in order to work at runtime?在您的情况下: generate()需要什么才能在运行时工作? Well, if we can assume that the values generated by generate() will be objects holding only string -valued properties, then we need to pass it a list of the keys of the object.好吧,如果我们可以假设generate()生成的值将是只包含string值属性的对象,那么我们需要将 object 的键列表传递给它。 So what if we write generate() to do that, and then define Foo in terms of the output of generate() instead of the other way around?那么如果我们编写generate()来做到这一点,然后根据generate()的 output 而不是相反的方式定义Foo呢?

For example:例如:

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": ""
} */

Here generate() is a generic function that takes a list of keys (of type K ) and produces a value of a type with keys in K and values of type string .这里的generate()是一个通用的 function ,它接受一个键列表(类型为K )并生成一个类型的值,其中键为K ,值类型为string That {[P in K]: string} is a mapped type equivalent to Record<K, string> using the Record<K, V> utility type .{[P in K]: string}是一个映射类型,等效于Record<K, string>使用Record<K, V>实用程序类型

The implementation uses Object.fromEntries() to build the object, and the return type is asserted to be the right type because TypeScript sees Object.fromEntries() as returning a type that's too wide for our purposes. The implementation uses Object.fromEntries() to build the object, and the return type is asserted to be the right type because TypeScript sees Object.fromEntries() as returning a type that's too wide for our purposes.

Anyway when you call const myFooObject = generate("go", "start") , it produces a value of type {go: string; start: string}无论如何,当您调用const myFooObject = generate("go", "start")时,它会产生一个{go: string; start: string}类型的值{go: string; start: string} , which is the same as your Foo type. {go: string; start: string} ,与您的Foo类型相同。 So we can just define Foo as type Foo = typeof myFooObject instead of doing it manually.所以我们可以将Foo定义为type Foo = typeof myFooObject而不是手动进行。 You could still do it manually, but the point I'm showing here is that it's much easier to write DRY code in TypeScript if you start with values and generate types from them, instead of trying to do it the other way around.仍然可以手动执行此操作,但我在这里展示的要点是,如果您从值开始并从中生成类型,而不是尝试以相反的方式执行,那么在 TypeScript 中编写DRY 代码要容易得多。


Again, if you are using the TypeScript compiler tsc as-is, then type erasure prevents you from writing generate() from the definition of Foo .同样,如果您按原样使用 TypeScript 编译器tsc ,则类型擦除会阻止您从Foo的定义中编写generate() But...但...

If you are willing to add a build step to your project and perform code generation using the TypeScript compiler API or something like it, you can then do arbitrary things with types.如果您愿意为您的项目添加构建步骤并使用TypeScript 编译器 API或类似的东西执行代码生成,那么您可以对类型执行任意操作。 There are libraries that do similar things to what you want;有一些库可以做与您想要的类似的事情; for example, ts-auto-mock claims to generate mock objects given an object type, which looks like exactly your use case.例如, ts-auto-mock声称在给定 object 类型的情况下生成模拟对象,这看起来与您的用例完全一样。

Such an extra build step might be a reasonable approach for you, but if you go down that route you should note that you're not using just plain TypeScript anymore (and therefore the topic is probably out of scope for a question with just a TypeScript tag). Such an extra build step might be a reasonable approach for you, but if you go down that route you should note that you're not using just plain TypeScript anymore (and therefore the topic is probably out of scope for a question with just a TypeScript标签)。

Playground link to code Playground 代码链接

You could make it generic, like this:你可以让它通用,像这样:

type foo<T extends string = string> = {
    go: T;
    start: T;
}

const test: foo<''> = {
    go: '',
    start: '',
};

TypeScript Playground TypeScript 游乐场

This function should do what I think you want:这个 function 应该做我认为你想要的:

function generate<T>(type: (new () => T)): T {
    let obj = new type();
    Object.keys(obj).forEach(key => obj[key] = '');
    return obj;
}

It looks like you are passing type of the object as the argument, however what you are actually passing is a constructor for the object.看起来您正在传递 object 的类型作为参数,但是您实际上传递的是 object 的构造函数。 This constructor is then called inside the function to create an object.然后在 function 内部调用此构造函数以创建 object。 The code then iterates over the fields in the object and sets them to ''.然后代码遍历 object 中的字段并将它们设置为 ''。 You could add a second argument to the function if you wanted to provide a string when calling generate.如果您想在调用 generate 时提供一个字符串,您可以向 function 添加第二个参数。

Here is an example of its use:以下是它的使用示例:

class Foo {
    go: string;
    start: string;

    constructor(go: string, start: string) {
        this.go = go;
        this.start  = start;
    }
}

let o = generate(Foo);
console.log(o);

Output: Output:

Foo: {
  "go": "",
  "start": ""
} 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM