Vie*_*ele 2 javascript types typescript
如果我有:
type Op = 'AND' | 'OR';
Run Code Online (Sandbox Code Playgroud)
我知道我可以写:
type Exp<T> = {
[_ in Op]: T[];
};
Run Code Online (Sandbox Code Playgroud)
这样所有以下行都是有效的
let a: Exp<number> = { OR: [4] };
let b: Exp<number> = { AND: [5] };
let c: Exp<number> = { AND: [1], OR: [2, 3] };
Run Code Online (Sandbox Code Playgroud)
我的问题是,我可以用这样的方式编写它,a并且b有效,但c不是吗?即,只允许一个密钥,密钥是类型Op.
即使我迭代所有这样的Op类型:
type Exp<T> = { AND: T[] } | { OR: T[] };
Run Code Online (Sandbox Code Playgroud)
它仍然不会做我想做的事情.我猜它是由于声明合并?
打字稿很新.寻找一种优雅的方式来表达这一点.
有几种不同的方法来实现你想要的东西,哪种方式最好取决于你的实际用例,我无法从这个例子中得出.
这些是实现你想要的一些方法.
方式1:传递钥匙.
type Exp<T, K extends Op> = Record<K, T[]>
let a: Exp<number, "OR"> = { OR: [4] }; //pass
let b: Exp<number, "AND"> = { AND: [5] }; //pass
let c: Exp<number, "And"> = { AND: [1], OR: [2, 3] }; //fail
let c: Exp<number, Op> = { AND: [1], OR: [2, 3] }; // pass
Run Code Online (Sandbox Code Playgroud)
方式2:在这种情况下,将OP的每个键设为XOR,您不再需要传递密钥.
export type XOR<T, U> = (T | U) extends object ? (Without<T, U> & U) | (Without<U, T> & T) : T | U;
export type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };
type Op = 'AND' | 'OR';
type Exp<T> = XOR<Pick<Record<Op, T[]>, "AND">, Pick<Record<Op, T[]>, "OR">>
let a: Exp<number> = { OR: [4] }; //pass
let b: Exp<number> = { AND: [5] }; //pass
let c: Exp<number> = { AND: [1], OR: [2, 3] }; //fail
Run Code Online (Sandbox Code Playgroud)
方式3:注意这是自动生成的版本,它并不漂亮,我这样做只是为了表明它是可能的.这个解决方案不能在没有重载的情况下达到无限深度,这是因为Unions不能迭代,但这显示它是深度九.我发布这个只是因为它的兴趣.
// add an element to the end of a tuple
type Push<L extends any[], T> =
((r: any, ...x: L) => void) extends ((...x: infer L2) => void) ?
{ [K in keyof L2]-?: K extends keyof L ? L[K] : T } : never
// convert a union to an intersection: X | Y | Z ==> X & Y & Z
type UnionToIntersection<U> =
(U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never
// convert a union to an overloaded function X | Y ==> ((x: X)=>void) & ((y:Y)=>void)
type UnionToOvlds<U> = UnionToIntersection<U extends any ? (f: U) => void : never>;
// convert a union to a tuple X | Y => [X, Y]
// a union of too many elements will become an array instead
type UnionToTuple<U> = UTT0<U> extends infer T ? T extends any[] ?
Exclude<U, T[number]> extends never ? T : U[] : never : never
// each type function below pulls the last element off the union and
// pushes it onto the list it builds
type UTT0<U> = UnionToOvlds<U> extends ((a: infer A) => void) ? Push<UTT1<Exclude<U, A>>, A> : []
type UTT1<U> = UnionToOvlds<U> extends ((a: infer A) => void) ? Push<UTT2<Exclude<U, A>>, A> : []
type UTT2<U> = UnionToOvlds<U> extends ((a: infer A) => void) ? Push<UTT3<Exclude<U, A>>, A> : []
type UTT3<U> = UnionToOvlds<U> extends ((a: infer A) => void) ? Push<UTT4<Exclude<U, A>>, A> : []
type UTT4<U> = UnionToOvlds<U> extends ((a: infer A) => void) ? Push<UTT5<Exclude<U, A>>, A> : []
type UTT5<U> = UnionToOvlds<U> extends ((a: infer A) => void) ? Push<UTT6<Exclude<U, A>>, A> : []
type UTT6<U> = UnionToOvlds<U> extends ((a: infer A) => void) ? Push<UTT7<Exclude<U, A>>, A> : []
type UTT7<U> = UnionToOvlds<U> extends ((a: infer A) => void) ? Push<UTT8<Exclude<U, A>>, A> : []
type UTT8<U> = UnionToOvlds<U> extends ((a: infer A) => void) ? Push<UTT9<Exclude<U, A>>, A> : []
type UTT9<U> = UnionToOvlds<U> extends ((a: infer A) => void) ? Push<UTTX<Exclude<U, A>>, A> : []
type UTTX<U> = []; // bail out
export type XOR<T, U> = (T | U) extends object ? (Without<T, U> & U) | (Without<U, T> & T) : T | U;
export type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };
type DeepXOR<Obj, T extends any[]> = {
[K in keyof T]: T[K] extends keyof Obj
? XOR<Pick<Obj, T[K]>, ((..._: T) => any) extends ((_: any, ..._1: infer TAIL) => any)
? DeepXOR<Obj, TAIL>[0] extends never
? never
: DeepXOR<Obj, TAIL>[0]
: never
>
: never;
};
interface Person {
name: string;
age: number;
dob: Date;
}
type PersonXOR = DeepXOR<Person, UnionToTuple<keyof Person>>[0]
const test: PersonXOR = {name: "shanon"}; // pass
const test1: PersonXOR = {age: 24}; // pass
const test2: PersonXOR = {dob: new Date()}; // pass
const test3: PersonXOR = {name: "shanon", dob: new Date()} // fail
Run Code Online (Sandbox Code Playgroud)
编辑方式4:
@dragomirtitian提醒我,您实际上可以通过连接每个对象/键对(奇异对)并将其与其他对的部分相交,并将其类型值设置为never,从而创建严格的并集.可能是最干净的方式
type Op = 'AND' | 'OR' | "NOT";
type Exp<T> = Op extends infer D ?
D extends any ? {
[_ in D]: T[];
}: never : never;
type UnionKeys<T> = T extends any ? keyof T : never;
type StrictUnionHelper<T, TAll> = T extends any ? T & Partial<Record<Exclude<UnionKeys<TAll>, keyof T>, never>> : never;
type StrictUnion<T> = StrictUnionHelper<T, T>
let a: StrictUnion<Exp<number>> = { OR: [4] }; // no error
let b: StrictUnion<Exp<number>> = { AND: [5] }; // no error
let c: StrictUnion<Exp<number>> = { AND: [1], OR: [2, 3] }; // error
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
312 次 |
| 最近记录: |