Приведение строк к перечислению в TypeScript

Я столкнулся с небольшой проблемой при работе с перечислениями в 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.


person Terry    schedule 05.10.2018    source источник
comment
const av = Object.keys (AllowedValues) .find (k = ›AllowedValues ​​[k] === 'dolor sir') as AllowedValues; console.log (av);   -  person softbear    schedule 05.10.2018


Ответы (1)


Здесь происходит несколько вещей. Во-первых, 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.


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

person jcalz    schedule 05.10.2018
comment
Спасибо за действительно подробный ответ! Теперь, когда это имеет для меня смысл: я не знал, что TypeSCript по умолчанию будет использовать исходный аннотированный тип, если переназначение переменной произойдет в любой момент. - person Terry; 13.10.2018