具有定义值的 Typescript 动态对象键 [英] Typescript dynamic object keys with defined values

查看:30
本文介绍了具有定义值的 Typescript 动态对象键的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到了一个问题,试图让 typescript 为我识别 javascript 对象的键,同时强制每个键的值类型,因为我想创建对象键的类型,所以我不能只创建一个常规type MyObject = { [key: string]: <插入类型>}.

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> }.

想象一个对象 myobject,我在其中提取它的键,例如:

Imagine an object myobject where I extract the keys of it like:

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):

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 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?

这样做的目的是让 TypeScript 帮助指出正确的对象键,例如(实际用法有点复杂,所以我希望这能很好地描述它):

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):

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

总结:我想将一个对象定义为 const(实际上是使用 Object.freeze)并且能够:

Wrap up: I want to define an object as const (in fact with Object.freeze) and be able to:

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

完整示例

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

<小时>

另一个例子:例如参见这个要点并将其复制到typescript playground 如果你想更改代码


Another example: See this gist for example and copy it to typescript playground if you want to change the code

推荐答案

虽然我还没有找到完全避免创建 javascript 函数来解决这个问题的方法(也被告知可能根本不可能,目前),我找到了我认为可以接受的解决方案:

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:

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

这样我们得到:

  1. 关于缺少属性的编译错误.
  2. 自动完成缺失的属性.
  3. 能够提取对象中定义的确切键,而无需在其他地方定义,例如在枚举中(复制它).

我已更新我的要点以包含此示例,您可能会看到它在上打字稿游乐场.

I have updated my Gist to include this example and you might be able to see it in practice on typescript playground.

这篇关于具有定义值的 Typescript 动态对象键的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆