Typescript Dynami c ключи объекта с определенными значениями - PullRequest
1 голос
/ 03 апреля 2020

Я сталкиваюсь с проблемой, пытающейся заставить машинопись распознавать ключи объекта javascript для меня, в то же время применяя тип значения каждого ключа, потому что я хочу создать тип ключей объекта, поэтому я не могу просто создать обычный type MyObject = { [key: string]: <insert type> }.

Представьте себе объект myobject, где я извлекаю его ключи, например:

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

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

Как я могу добавить определения типов к значениям ключей, сохраняя при этом возможность извлекать / наследовать определения ключей? Если я сделаю что-то подобное, я больше не смогу извлечь точные ключи объекта, а только тип (строка):

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

type MyObjectKeys = keyof typeof myobject; // string

Я подумал, что смогу добиться этого, создав Вспомогательная функция, например:

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

Но я бы предпочел определить для нее чистый тип, не загрязняя код, а писать функции, относящиеся только к типам. Есть ли способ разрешить набор строк в качестве ключей типа без повторений?

Цель этого состоит в том, чтобы получить TypeScript, помогающий указывать правильные ключи объекта, такие как (реальное использование немного сложнее , поэтому я надеюсь, что это достаточно хорошо описывает это):

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) и иметь возможность:

  1. Извлечение точных ключей объекта (без необходимости определения каждого ключа).
  2. Определение типа каждого ключа без перезаписи ключи к string вместо того, что они есть - как '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.

В соответствии с вышеизложенным, я не могу сделать следующее, потому что перезапишет известные ключи для любой строки:

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

Другой пример: посмотрите, например, this gist и скопируйте его в машинописный текст детская площадка если вы хотите изменить код

Ответы [ 2 ]

1 голос
/ 03 апреля 2020

Надеюсь, это поможет вам сейчас:

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

Детская площадка ссылка .

0 голосов
/ 04 апреля 2020

Хотя я не нашел способа полностью избежать создания функции javascript, чтобы решить эту проблему (также сказал, что в данный момент это вообще невозможно), я нашел то, что я считаю приемлемым решением:

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. Возможность извлечь точное ключи, определенные в объекте, без необходимости определять их в другом месте, например, в enum (дублируя его).

Я обновил my Gist , чтобы включить этот пример, и вы можете возможность увидеть это на практике на машинописной игровой площадке .

...