Есть ли более элегантный способ получить значение из перечисления TypeScript, если оно существует? - PullRequest
0 голосов
/ 14 февраля 2019

Если у меня есть Enum допустимых значений, например:

enum PackagingUnits {
  Be = 'becher',
  Bg = 'bogen',
  Bi = 'bidon'
}

Я хочу, чтобы функция передавала строку для получения значения enum по ключу, поэтому getPackagingUnitName('Be') вернет 'becher'.

Наивной версией этого будет:

function getPackagingUnitName(key: string): PackagingUnits | null {
  if (PackagingUnits[key]) {
    return PackagingUnits[key]
  }
  return null
}

Однако TypeScript (версия 3.3.3 atm) не любит этого и жалуется, что Element implicitly has an 'any' type because index expression is not of type 'number'. для каждого экземпляра PackagingUnits[key].

Я пробовал несколько вариантов условия if (например, Object.keys(PackagingUnits).includes(key), которое делает условие if без ошибок, но это все равно дает ту же ошибку для оператора return, return PackagingUnits[key].

return PackagingUnits[key as PackagingUnits] тоже не работает.

Единственный рабочий синтаксис, который мне удалось придумать, это:

function getPackagingUnitName(key: string): PackagingUnits | null {
  const puIndex = Object.keys(PackagingUnits).indexOf(key)
  if (puIndex !== -1) {
    return Object.values(PackagingUnits)[puIndex]
  }
  return null
}

Это действительно лучшийВозможный способ сделать это?

NB: Да, это можно было бы сделать проще, если бы PackagingUnits не было перечислением, но это должно быть так для других целей.

Ответы [ 2 ]

0 голосов
/ 14 февраля 2019

TypeScript не рассматривает доступ к неизвестному свойству как способ сужения потока управления для подтверждения существования этого свойства.Вы можете получить доступ к свойствам типа объекта только в том случае, если компилятор знает, что есть свойство (или может быть, в случае необязательных свойств) свойства для этого ключа.Поэтому, как только вы сделаете PackagingUnits[key], где key - произвольный string, компилятор пожалуется, что PackagingUnits не имеет индекса string.

Самый простой способ исправить это - использовать утверждение типа и двигаться дальше:

function getPackagingUnitName(key: string): PackagingUnits | null {      
  if ((PackagingUnits as any)[key]) { // assert your way out
    return PackagingUnits[key as keyof typeof PackagingUnits]; // assert your way out
  }
  return null;
}

Это работает, но не гарантируетсябыть типобезопасным компилятором.Возможно, вас это не волнует, поскольку проблема заключается в реализации функции, с которой другие разработчики не собираются связываться.Если это так, то отлично.


Если вы хотите, чтобы компилятор гарантировал, что то, что вы делаете, является безопасным типом, вам нужно провести его через анализ с осторожными шагами.Во-первых, вы бы хотели, чтобы компилятор обрабатывал объект PackagingUnits не как нечто с тремя известными ключами со значениями PackagingUnits типов, а вместо этого рассматривал его как нечто, имеющее значение каждого строкового ключа., значение которого PackagingUnits | undefined.(Это как можно ближе к необязательным индексным сигнатурам , поскольку TypeScript не всегда отличает отсутствующие свойства от undefined -значений свойств ).Итак, вы хотели бы это:

const PackagingUnitsIndexSignature: {[k: string]: PackagingUnits | undefined} =
  PackagingUnits; // error!

Это должно быть безопасно (тип typeof PackagingUnits является строгим подтипом из typeof PackagingUnitsIndexSignature), но компилятор не осознает этого.* * * * * * * * * * * * PackagingUnits природа *1036*, кажется, запутывает это.Таким образом, мы можем сделать двухэтапное расширение ... сначала для простого типа объекта, чьи ключи и значения соответствуют ключам перечисления:

const PackagingUnitsPlainObject: Record<keyof typeof PackagingUnits, PackagingUnits> =
  PackagingUnits; // okay!
const PackagingUnitsIndexSignature: { [k: string]: PackagingUnits | undefined } =
  PackagingUnitsPlainObject; // okay!

Теперь у нас наконец есть то, что мы можем индексировать с помощью string без ошибок:

function getPackagingUnitName(key: string): PackagingUnits | null {
  if (PackagingUnitsIndexSignature[key]) { // okay!
    return PackagingUnitsIndexSignature[key];  // error!!!!
  } else {
    return null;
  }       
}

Тьфу, что случилось?Не следует ли контролировать работу по сужению потока, так как мы только что проверили наличие свойства в key?К сожалению, индексированный доступ типа скобок не всегда сужает значения так, как нам хотелось бы .Вместо того, чтобы заставить это работать, давайте просто примем это как данность, что PackagingUnitsIndexSignature[key] возвращает PackagingUnits | undefined.Если это так, мы должны иметь возможность использовать логический оператор "или" (||) для замены undefined на null:

function getPackagingUnitName(key: string): PackagingUnits | null {
  return PackagingUnitsIndexSignature[key] || null;  // okay
}

И компилятор счастлив,Чтобы собрать все это вместе, вот окольный способ написать ту же логику, чтобы компилятор подтвердил, что она безопасна по типу:

const PackagingUnitsPlainObject: Record<keyof typeof PackagingUnits, PackagingUnits> =
  PackagingUnits; // okay!
const PackagingUnitsIndexSignature: { [k: string]: PackagingUnits | undefined } =
  PackagingUnitsPlainObject; // okay!
function getPackagingUnitName(key: string): PackagingUnits | null {
  return PackagingUnitsIndexSignature[key] || null;  // okay!
}

Хорошо, надеюсь, это поможет.Удачи!

0 голосов
/ 14 февраля 2019

Вы пробовали

function getPackagingUnitName(key: keyof typeof PackagingUnits): PackagingUnits | null {
    if (PackagingUnits[key]) {
        return PackagingUnits[key];
    }
    return null
}

Конечно, на всякий случай key получается из API или чего-то в этом роде.В противном случае функция не является действительно необходимой.Если вы хотите проверить тип, чтобы убедиться, что ключ является одним из предопределенных значений, просто используйте

key: keyof typeof PackagingUnits;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...