Тип перечисления проверки типов для условных типов? - PullRequest
0 голосов
/ 21 мая 2018

У меня есть службы restful, которые принимают значения enum как число ИЛИ строку, но всегда возвращают только число.Есть ли способ напечатать их?

Вот то, что я вроде хочу, но это не синтаксически допустимо:

enum Features {
  "A" = 1,
  "B" = 2,
  "C" = 2
}

type EnumOrString<T> = T extends enum
  ? T | keyof T
  : T

declare function getData(featureFilter: EnumOrString<Features>[]): Features[]

getData принимает массив значений перечисления или ключей перечисленияно возвращает только значения перечисления.

Я также хотел бы распространить это на сопоставленные типы, такие как DeepPartial, чтобы все вложенные перечисления получали эту обработку - без необходимости иметь отдельные иерархиитипы, разделенные запросом и ответом.

1 Ответ

0 голосов
/ 21 мая 2018

Я не думаю, что "определить enum" вещь возможна прямо сейчас.Даже если бы вы могли, вы не можете программно преобразовать тип Features (который является элементом перечисления Features) в тип typeof Features (отображение от ключей до Features элементов), не зная о Features в первую очередь.Опять же: тип Features.B, например, ничего не знает о строковом литерале "B".Только typeof Features имеет свойство, подобное {B: Features.B}.Если вы хотите, чтобы функция типа конвертировала из Features в Features | keyof typeof Features, вам нужно явно указать typeof Features.Таким образом, даже если у вас была нотация extends enum вашей мечты, вам все равно нужно написать код замены со списком отображений, которые вам нужны.Извините.


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

enum Features {
  "A" = 1,
  "B" = 2,
  "C" = 2
}
type ValueOf<T> = T[keyof T]
type FeatureKey<T extends Features> =
  Extract<ValueOf<{
    [K in keyof typeof Features]: [K, typeof Features[K]]
  }>, [any, T]>[0]

type DeepFeaturesOrKey<T> =
  T extends Features ? (T | FeatureKey<T>) :
  T extends Array<infer L> ? DeepFeaturesOrKeyArray<L> :
  T extends object ? { [K in keyof T]: DeepFeaturesOrKey<T[K]> } : T

interface DeepFeaturesOrKeyArray<L> extends Array<DeepFeaturesOrKey<L>> { }

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

По аналогии с union итипы пересечений, условные типы не могут ссылаться на себя рекурсивно (однако допускаются косвенные ссылки через интерфейсные типы или литеральные типы объектов)

Давайте проверим это:

interface Foo {
  bar: string,
  baz: Features,
  qux: {
    a: Features[],
    b: boolean
  },
  quux: Features.A,
  quuux: Features.B  
}

type DeepFeaturesOrKeyFoo = DeepFeaturesOrKey<Foo>
declare const deepFeaturesOrKeyFoo: DeepFeaturesOrKeyFoo
deepFeaturesOrKeyFoo.bar; // string
deepFeaturesOrKeyFoo.baz; // Features | "A" | "B" | "C"
deepFeaturesOrKeyFoo.qux.a[1];  // Features | "A" | "B" | "C"
deepFeaturesOrKeyFoo.qux.b; // boolean
deepFeaturesOrKeyFoo.quux; // Features.A | "A"
deepFeaturesOrKeyFoo.quuux; // Features.B | "B" | "C" 
// note that the compiler considers Features.B and Features.C to be the
// same value, so this becomes Features.B | "B" | "C"

Looksхорошо.Надеюсь, это поможет.

...