Приведение строк к перечислению в TypeScript - PullRequest
0 голосов
/ 05 октября 2018

Я столкнулся с небольшой проблемой при работе с перечислениями в TypeScript.Мой сценарий таков:

  • Я определил строковое перечисление, содержащее допустимые значения
  • Я определил метод, который принимает любое входящее значение (типа string), и долженприведите его к указанному перечислению

Проблема в том, что даже после проверки, является ли входящий value из метода, intellisense говорит мне, что value по-прежнему является типом string вместо перечисления,Как заставить value быть типом AllowedValues?

Вот пример проверки концепции:

/** enum */
enum AllowedValues {
    LOREM_IPSUM = 'lorem ipsum',
    DOLOR_SIT = 'dolor sir',
    AMET = 'amet'
}

/** @method */
function doSomething(value: string = AllowedValues.LOREM_IPSUM) {

    // If value is not found in enum, force it to a default
    if (!(Object as any).values(AllowedValues).includes(value))
        value = AllowedValues.LOREM_IPSUM;

    // Value should be of type `AllowedValues` here
    // But TypeScript/Intellisense still thinks it is `string`
    console.log(value);
}

doSomething('amet');    // Should log `amet`
doSomething('aloha');   // Should log `lorem ipsum`, since it is not found in `AllowedValues`

Вы также можете найти его на TypeScript детская площадка .

1 Ответ

0 голосов
/ 05 октября 2018

Здесь происходит несколько вещей.Одна из них заключается в том, что TypeScript не понимает, что Object.values(x).includes(y) - это защита типа на y.Он не соответствует встроенным способам, которые компилятор пытается сузить, например, проверкам typeof, instanceof или in.Чтобы помочь компилятору, вы можете использовать определяемый пользователем тип защиты , чтобы выразить этот способ проверки:

function isPropertyValue<T>(object: T, possibleValue: any): possibleValue is T[keyof T] {
  return Object.values(object).includes(possibleValue);
}

declare function onlyAcceptAllowedValues(allowedValue: AllowedValues): void;
declare const v: string;
if (isPropertyValue(AllowedValues, v)) {
  onlyAcceptAllowedValues(v); // v is narrowed to AllowedValues; it works!
}

Итак, давайте сначала изменим вашу функцию следующим образом:

function doSomething(value: string = AllowedValues.LOREM_IPSUM) {    
  if (!(isPropertyValue(AllowedValues, value)))
    value = AllowedValues.LOREM_IPSUM;

  // TypeScript/Intellisense still thinks it is `string`
  console.log(value);
}

Ой, все еще не работает.


Второе, что происходит: если вы переназначаете значение переменной, TypeScript, по сути, разочаровывается в ее сужении.Компилятор прилагает значительные усилия, чтобы понять влияние потока управления на типы переменных, но не идеально .Итак, хотя мы понимаем, что присвоение AllowedValues.LOREM_IPSUM value делает его AllowedValues, компилятор отказывается от и предполагает, что это его исходный аннотированный тип, которыйstring.

Способ работы с этим состоит в создании новой переменной, которая, как понимает компилятор, никогда не будет ничем иным, как AllowedValues.Самый простой способ сделать это - сделать ее переменной const, например:

function doSomething(value: string = AllowedValues.LOREM_IPSUM) {    
  const allowedValue = isPropertyValue(AllowedValues, value) ? value : AllowedValues.LOREM_IPSUM;
  console.log(allowedValue);
}

В приведенном выше примере новая переменная allowedValue выводится как AllowedValues, потому что она установленалибо value, если защита типа прошла успешно (в этот момент value является AllowedValues), либо AllowedValues.LOREM_IPSUM, если защита типа не работает.В любом случае, allowedValue - это AllowedValues.


Так что это было бы мое предлагаемое изменение, если вы хотите помочь компилятору понять вещи.Надеюсь, это поможет.Удачи!

...