enum E {
A = 'A',
B = 'B'
}
type O = keyof typeof E
const state: { [key in keyof typeof E]?: string } = {};
如果你想让state 可变,只需使用-readonly:
const state: { -readonly [key in keyof typeof E]?: string } = {};
我如何将对象键约束为枚举值,而不需要它们的存在并且值不是未定义的?
能否提供一些伪代码?
因为如果不需要key,你会尝试获取不存在key的值,根据JS标准你应该收到undefined
更新
enum E {
A = 'A',
B = 'B',
C = 'C',
D = 'D'
}
// credits goes to https://stackoverflow.com/a/50375286
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
k: infer I
) => void
? I
: never;
// //https://github.com/microsoft/TypeScript/issues/13298#issuecomment-468114901
type UnionToOvlds<U> = UnionToIntersection<
U extends any ? (f: U) => void : never
>;
type PopUnion<U> = UnionToOvlds<U> extends (a: infer A) => void ? A : never;
type IsUnion<T> = [T] extends [UnionToIntersection<T>] ? false : true;
type UnionToArray<T, A extends unknown[] = []> = IsUnion<T> extends true
? UnionToArray<Exclude<T, PopUnion<T>>, [PopUnion<T>, ...A]>
: [T, ...A];
type State<T extends string, R extends string[] = []> = {
[P in T]: IsUnion<T> extends true ? State<Exclude<T, P>, [...R, Exclude<T, P>]> : R
}[T]
// Array of all possible keys
type Result = UnionToArray<State<keyof typeof E>>
// convert union to object with appropriate keys
type MapPredicate<T> = UnionToIntersection<T extends string ? {
[P in T]: string
} : never>
// don't allow empty object because value can't be undefined
type Empty = { __tag: 'empty' }
// iterate through array of strings
type MappedString<
Arr,
Result = Empty
> = Arr extends []
? []
: Arr extends [infer H]
? Result | MapPredicate<H>
: Arr extends [infer Head, ...infer Tail]
? MappedString<[...Tail], Result | MapPredicate<Head>>
: Readonly<Result>;
// iterate through array of array of string
type MappedArray<
Arr extends Array<unknown>,
Result extends Array<unknown> = []
> = Arr extends []
? []
: Arr extends [infer H]
? [...Result, MappedString<H>]
: Arr extends [infer Head, ...infer Tail]
? MappedArray<[...Tail], [...Result, MappedString<Head>]>
: Readonly<Result>;
type AllPossibleValues = MappedArray<Result>[number];
const A: AllPossibleValues = { A: 'A' }
const AB: AllPossibleValues = { A: 'A', B: 'B' }
const ABC: AllPossibleValues = { A: 'A', B: 'B', C: 'C' }
const ABCD: AllPossibleValues = { A: 'A', B: 'B', C: 'C', D: 'D' }
const CD: AllPossibleValues = { C: 'C', D: 'D' }
const AD: AllPossibleValues = { A: 'A', D: 'D' }
const BD: AllPossibleValues = { B: 'B', D: 'D' }
const BE: AllPossibleValues = {} // expected error
const QA: AllPossibleValues = {A:'A', Q:'Q'} // expected error
const state:AllPossibleValues={A:'A'}
const x = Object.entries(state).forEach(([key, value]) => { /* [key: string, value: string] */
})
优点:没有断言,没有类型转换
缺点:我必须使用类似的映射实用程序,但我不知道如何重构。但无论如何它不会影响您编译的代码。另外,如果你在枚举中添加第 5 个属性,上面的代码将无法编译,因为递归限制:)
因此,如果您的对象的道具少于 5 个,那么您就可以开始了。
TypeScript 允许您大约 50 次递归调用。
如果你有 obj 有 5 个 props,你应该用 parseInt('11111',2) 31 个项目创建 union。我想是因为我的 rec MappedArray 调用了 rec MappedString 我更快地达到了这个限制。
Playground link