Я не думаю, что "определить 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хорошо.Надеюсь, это поможет.