Я не думаю, что «идентифицировать enum
» возможно прямо сейчас. Даже если бы вы могли, вы не можете программно преобразовать из типа Features
(который является элементом перечисления Features
) в тип typeof Features
(отображение из ключей в Features
elements), не зная в первую очередь о 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>> { }
Сложные биты извлекают подмножество перечисления, если вы не укажете все (например, вы используете размеченное объединение с ключом определенного значения перечисления), и, конечно же, весь трюк с глубоким массивом, чтобы избежать упомянутого ужасного сообщения об ошибке" циклической ссылки "здесь:
Подобно типам объединения и пересечения, условным типам не разрешается ссылаться на себя рекурсивно (однако разрешены косвенные ссылки через типы интерфейса или типы литералов объекта)
Давайте протестируем это:
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"
Выглядит неплохо. Надеюсь, это поможет.
person
jcalz
schedule
21.05.2018
enum
программно. Самое близкое, что я когда-либо получал, - это что-то вродеtype IsEnum<T> = T extends Record<keyof T, string | number> ? true : false
, но в любом случае вам нужно сделатьEnumOrString<typeof Features>
. - person jcalz   schedule 21.05.2018Features
наFeatures | keyof typeof Features
? - person jcalz   schedule 21.05.2018Features
). - person jcalz   schedule 21.05.2018