Ссылайте ключи одного перечисления на ключи или значения другого перечисления - PullRequest
0 голосов
/ 14 июля 2020

В приведенном ниже коде ключи PropertiesNamesInDataBase должны быть даже с User.Keys. Значения PropertiesNamesInDataBase, как очевидно из названия, используются в бэкенде, но имена во фронтенде должны быть унифицированы.

namespace User {
    export enum Keys {
        ID = "ID",
        name = "name"
    }
}

enum PropertiesNamesInDataBase {
    ID = "id",
    name = "nm"
}

Здесь как минимум две проблемы:

  1. Нам нужно повторно набрать ключи или скопировать и вставить их
  2. PropertiesNamesInDataBase полностью не зависит от User.Keys, однако концептуально ключи PropertiesNamesInDataBase должны каким-то образом относиться к User.Keys.

Вот попытка решить вторую проблему путем ссылки PropertiesNamesInDataBase ключей на User.Keys:

namespace User {
    export enum Keys {
        ID = "ID",
        name = "name"
    }
}

enum PropertiesNamesInDataBase {
    [User.Keys.ID] = "id",
    [User.Keys.name] = "nm"
}

Однако это невозможно в TypeScript:

Computed property names are not allowed in enums. (1164)

Пожалуйста, объясните мне, как повторно использовать ключи перечисления или как ссылаться на ключи PropertiesNamesInDataBase на значения User.Keys.

1 Ответ

1 голос
/ 15 июля 2020

Я был бы склонен полностью забыть о enum и вместо этого создавать свои собственные объекты, подобные перечислению. Это даст вам гибкость, необходимую для записи ключей только один раз, но вы потеряете часть встроенной выразительности, которая приходит с enum (а именно, что enum Foo {...} создает оба объекта с именем Foo и типа с именем Foo. Если вместо этого вы создадите const Foo = ..., вы получите только объект , и вам нужно будет определить свой собственный тип самостоятельно, если вы этого хотите) .

Вот один из подходов:

const KeysAndPropNames = {
  ID: { key: "ID", propName: "id" },
  name: { key: "name", propName: "nm" }
} as const;

У вас есть только один объект, который является отображением ключа и значений, которые были в ваших старых перечислениях User.Keys и PropertiesNamesInDataBase. Вы можете самостоятельно извлечь эти перечислимые объекты следующим образом:

const User = { Keys: objMapProp(KeysAndPropNames, "key") };
const PropertiesNamesInDataBase = objMapProp(KeysAndPropNames, "propName");

, где objMapProp() - это функция, которую вы можете поместить в библиотеку, которая отображает доступ к свойству для объекта:

// library
function objMapProp<T extends Record<keyof T, Record<K, any>>, K extends keyof any>(
  obj: T,
  key: K
) {
  const ret = {} as { [P in keyof T]: T[P][K] };
  for (let k in obj) {
    ret[k] = obj[k][key];
  }
  return ret;
}

Если вы исследуете тип новых объектов User и PropertiesNamesInDataBase с помощью IntelliSense, вы увидите, что они соответствуют старым значениям:

/* const User: {
    Keys: {
        readonly ID: "ID";
        readonly name: "name";
    };
} */

/* const PropertiesNamesInDataBase: {
    readonly ID: "id";
    readonly name: "nm";
} */

Если вам нужны типы с именами User.Keys и PropertiesNamesInDataBase, вы можете сделать их как объединение всех типов значений соответствующих объектов:

namespace User {
  export type Keys = (typeof User.Keys)[keyof typeof User.Keys];
  // type User.Keys = "ID" | "name"
}
type PropertiesNamesInDataBase = typeof PropertiesNamesInDataBase[keyof typeof PropertiesNamesInDataBase];
// type PropertiesNamesInDataBase = "id" | "nm"

В любом случае, надеюсь, что это либо отвечает вашим потребностям, либо дает вам представление о том, как действовать дальше. Удачи!

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

...