简体   繁体   English

Typescript 动态 object 具有定义值的键

[英]Typescript dynamic object keys with defined values

I am encountering an issue trying to make typescript recognise the keys of a javascript object for me, while enforcing each key's value type because I want to create a typeof the keys of the object, so I can't just create a regular type MyObject = { [key: string]: <insert type> } . I am encountering an issue trying to make typescript recognise the keys of a javascript object for me, while enforcing each key's value type because I want to create a typeof the keys of the object, so I can't just create a regular type MyObject = { [key: string]: <insert type> }

Imagine an object myobject where I extract the keys of it like:想象一个 object myobject我提取它的键,如:

const myobject = {
  foo: {},
  bar: {}
};

type MyObjectKeys = keyof typeof myobject; // 'foo' | 'bar'

How can I add type definitions to the values of the keys, while still being able to extract/inherit the definitions of the keys?如何将类型定义添加到键的值中,同时仍然能够提取/继承键的定义? If I do something like this, then I will no longer be able to extract the exact keys of the object, but only the type (string):如果我这样做,那么我将不再能够提取 object 的确切键,而只能提取类型(字符串):

type MyObject = { [key: string]: { value: boolean }}
const myobject = {
  foo: { value: true },
  bar: { value: false }
};

type MyObjectKeys = keyof typeof myobject; // string

I figured that I could achieve this by creating a helper function like:我想我可以通过创建一个助手 function 来实现这一点,例如:

function enforceObjectType<T extends MyObject>(o: T) {
    return Object.freeze(o);
}
const myobject = enforceObjectType({
  foo: {},
  bar: {}
});

But I'd prefer to define a clear type for it, without having to pollute the code, writing functions only related to types.但是我更愿意给它定义一个明确的类型,不用污染代码,只写与类型相关的函数。 Is there a way to allow a set of strings as keys of a type without repetition?有没有办法允许一组字符串作为一种类型的键而不重复?

The purpose of this is to get TypeScript to help pointing out the right object keys like (the real usage is a bit more complex, so I hope this describes it well enough):这样做的目的是让 TypeScript 帮助指出正确的 object 键(实际用法有点复杂,所以我希望这描述得足够好):

type MyObjectKeys = keyof typeof myobject; // string
function getMyObjectValue(key: MyObjectKeys) {
   const objectValue = myobject[key];
}

// suggest all available keys, while showing an error for unknown keys
getMyObjectValue('foo'); // success
getMyObjectValue('bar'); // success 
getMyObjectValue('unknown'); // failure

Wrap up: I want to define an object as const (in fact with Object.freeze ) and be able to:总结:我想将 object 定义为 const (实际上是Object.freeze )并能够:

  1. Extract the exact keys of the object (without having to type a definition of each key).提取 object 的确切键(无需键入每个键的定义)。
  2. Define the type of each key, without overwriting the keys to string instead of what they are - like 'foo' | 'bar'定义每个键的类型,而不是将键覆盖为string而不是它们的本质——比如'foo' | 'bar' 'foo' | 'bar' . 'foo' | 'bar'

Complete example完整的例子

type GameObj = { skillLevel: EnumOfSkillLevels }; // ADD to each key.
const GAMES_OBJECT = Object.freeze({
   wow: { skillLevel: 'average' },
   csgo: { skillLevel 'good' }
)};

type GamesObjectKeys = keyof typeof GAMES_OBJECT;

function getSkillLevel(key: GamesObjectKeys) {
  return GAMES_OBJECT[key]
}

getSkillLevel('wow') // Get the actual wow object
getSkillLevel('unknown') // Get an error because the object does not contain this.

In accordance to above, I can't do the following because that will overwrite the known keys to just any string:根据上述,我不能执行以下操作,因为这会将已知键覆盖为任何字符串:

type GameObj = { [key: string]: skillLevel: EnumOfSkillLevels };
const GAMES_OBJECT: GameObj = Object.freeze({
   wow: { skillLevel: 'average' },
   csgo: { skillLevel 'good' }
)};

type GamesObjectKeys = keyof typeof GAMES_OBJECT;

function getSkillLevel(key: GamesObjectKeys) {
  return GAMES_OBJECT[key]
}

getSkillLevel('wow') // Does return wow object, but gives me no real-time TS help
getSkillLevel('unknown') // Does not give me a TS error

Another example: See this gist for example and copy it to typescript playground if you want to change the code另一个例子:如果你想更改代码,请参阅此要点并将其复制到typescript 操场

Hope this help you now:希望现在对您有所帮助:

enum EnumOfSkillLevels {
  Average = 'average',
  Good = 'good'
}

type GameObject<T extends { [key: string]: {skillLevel: EnumOfSkillLevels} }> = {
  [key in keyof T ]: {skillLevel: EnumOfSkillLevels}
}
const GAMES_OBJECT = {
   wow: { skillLevel: EnumOfSkillLevels.Average },
  csgo: { skillLevel: EnumOfSkillLevels.Good },
   lol: { skillLevel: EnumOfSkillLevels.Good }
} as const;


function getSkillLevel(key: keyof GameObject<typeof GAMES_OBJECT>) {
  return GAMES_OBJECT[key]
}

getSkillLevel('wow') // Does return wow object, but gives me no real-time TS help
getSkillLevel('lol') // Does return wow object, but gives me no real-time TS help
getSkillLevel('unknown') // Does give me a TS error

Playground link .游乐场链接

While I have not found a way to completely avoid creating a javascript function to solve this (also told that might not be possible at all, at this moment), I have found what I believe is an acceptable solution:虽然我还没有找到一种方法来完全避免创建 javascript function 来解决这个问题(也告诉这可能根本不可能,此时此刻),我找到了我认为是可接受的解决方案:

type GameInfo = { [key: string]: { skillLevel: 'good' | 'average' | 'bad' }}

type EnforceObjectType<T> = <V extends T>(v: V) => V;
const enforceObjectType: EnforceObjectType<GameInfo> = v => v;

const GAMES2 = enforceObjectType({
  CSGO: {
    skillLevel: 'good',
  },
  WOW: {
    skillLevel: 'average'
  },
  DOTA: {
    // Compile error - missing property skillLevel
  }
});

type GameKey2 = keyof typeof GAMES2;

function getGameInfo2(key: GameKey2) {
  return GAMES2[key];
}

getGameInfo2('WOW');
getGameInfo2('CSGO');
getGameInfo2('unknown') // COMPILE ERROR HERE

This way we get:这样我们得到:

  1. Compile errors on missing properties.编译缺少属性的错误。
  2. Autocomplete of missing properties.自动完成缺失的属性。
  3. Able to extract the exact keys defined in the object, without having to define those elsewhere, eg in an enum (duplicating it).能够提取 object 中定义的确切键,而不必在其他地方定义它们,例如在枚举中(复制它)。

I have updated my Gist to include this example and you might be able to see it in practice on typescript playground .我已经更新了我的 Gist以包含这个示例,您也许可以在typescript 操场上实际看到它。

I write my codes in this way:我以这种方式编写代码:

function define<T>(o:T){
  return o;
}
type Node = {
  value: number,
  key: string
}
const NODE_DIC = {
  LEFT: define<Node>({
    value: 1,
    key: '1'
  }),
  RIGHT: define<Node>({
    value: 2,
    // compile error for missing `key`
  })
}

Here is the type of the object:这是 object 的类型:

{
    LEFT: Node;
    RIGHT: Node;
}

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

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