[英]Create a type that is an array of an object properties
So I'm trying to get this behavior:所以我试图得到这种行为:
interface A {
prop1: string;
prop2: number;
prop3: boolean;
}
type PropertyArray<T> = magical code
// PropertyArray<A> should be the same as the type ['prop1', 'prop2', 'prop3']
const properties: PropertyArray<A> = ['prop1', 'prop2', 'prop3'];
const fail1: PropertyArray<A> = ['prop1', 'prop2']; // type error
const fail2: PropertyArray<A> = ['prop1', 'prop1', 'prop2', 'prop3']; // type error
The point of this code is that if I add a new field the interface A, I also need to include that property to the array.这段代码的重点是,如果我在接口 A 中添加一个新字段,我还需要将该属性包含到数组中。 If any of the properties is missing the build fails.
如果缺少任何属性,则构建失败。
Thanks for the help.谢谢您的帮助。
If you really care about property order then this is essentially a duplicate of this question about turning a union into a tuple if you define如果你真的关心财产顺序,那么这基本上是这个关于将联合变成元组的问题的重复,如果你定义
type PropertyArray<T> = TuplifyUnion<keyof T>
But hopefully you don't really care about property order, and ['prop2', 'prop1', 'prop3']
would be an acceptable value of properties
.但希望你并不真正关心属性顺序,并且
['prop2', 'prop1', 'prop3']
将是一个可接受的properties
值。 In that case, there are two ways I can think of doing this:在这种情况下,我可以想到两种方法:
One is to actually calculate PropertyArray<T>
as a union of all possible permutations of keys in tuples, as you asked.一种是按照您的要求,将
PropertyArray<T>
实际计算为元组中所有可能的键排列的联合。 This would naturally involve a circular conditional type which is not currently supported .这自然会涉及当前不支持的循环条件类型。 I can instead make a definition that supports types with up to some fixed number of properties, like this:
我可以做一个定义来支持最多具有固定数量属性的类型,如下所示:
type PropertyArray<T> = Tup<keyof T>
type Cons<H, T extends any[]> = T extends any ? ((h: H, ...t: T) => void) extends ((...r: infer R) => void) ? R : never : never
type Tup<U, V = U> = [U] extends [never] ? [] : U extends any ? Cons<U, Tup1<Exclude<V, U>>> : never
type Tup1<U, V = U> = [U] extends [never] ? [] : U extends any ? Cons<U, Tup2<Exclude<V, U>>> : never
type Tup2<U, V = U> = [U] extends [never] ? [] : U extends any ? Cons<U, Tup3<Exclude<V, U>>> : never
type Tup3<U, V = U> = [U] extends [never] ? [] : U extends any ? Cons<U, Tup4<Exclude<V, U>>> : never
type Tup4<U, V = U> = [U] extends [never] ? [] : U extends any ? Cons<U, Tup5<Exclude<V, U>>> : never
type Tup5<U, V = U> = [U] extends [never] ? [] : U extends any ? Cons<U, Tup6<Exclude<V, U>>> : never
type Tup6<U, V = U> = [U] extends [never] ? [] : U extends any ? Cons<U, Tup7<Exclude<V, U>>> : never
type Tup7<U, V = U> = [U] extends [never] ? [] : U extends any ? Cons<U, Tup8<Exclude<V, U>>> : never
type Tup8<U, V = U> = [U] extends [never] ? [] : U extends any ? Cons<U, Tup9<Exclude<V, U>>> : never
type Tup9<U, V = U> = [U] extends [never] ? [] : U extends any ? Cons<U, TupX<Exclude<V, U>>> : never
type TupX<U> = [] // bail out
And for your case:对于您的情况:
interface A {
prop1: string;
prop2: number;
prop3: boolean;
}
const properties: PropertyArray<A> = ['prop1', 'prop2', 'prop3'];
const fail1: PropertyArray<A> = ['prop1', 'prop2']; // type error
const fail2: PropertyArray<A> = ['prop1', 'prop1', 'prop2', 'prop3']; // type error
This works how you want, but is a lot of work for the compiler and could be brittle.这可以按照您的意愿工作,但对编译器来说是很多工作并且可能很脆弱。
A slightly less crazy solution (which is still kind of crazy) is to use a helper function instead of a type alias.一个稍微不那么疯狂的解决方案(这仍然有点疯狂)是使用辅助函数而不是类型别名。 The helper function will only compile if its parameters include each and every key of the relevant object type exactly once:
仅当其参数包含相关对象类型的每个键都恰好一次时,辅助函数才会编译:
type TupleHasRepeats<T extends any[]> = { [I in keyof T]: T[I] extends T[Exclude<keyof T, keyof any[] | I>] ? unknown : never}[number]
const propertyArray = <T>() => <A extends Array<keyof T>>(...a: A & (keyof T extends A[number] ? unknown : never) & (unknown extends TupleHasRepeats<A> ? never : unknown )) => a;
And then try it:然后试试看:
interface A {
prop1: string;
prop2: number;
prop3: boolean;
}
const propertyArrayA = propertyArray<A>();
const properties = propertyArrayA('prop1', 'prop2', 'prop3');
const fail1 = propertyArrayA('prop1', 'prop2'); // type error;
const fail2 = propertyArrayA('prop1', 'prop1', 'prop2', 'prop3'); // type error
That works also.这也有效。 I'd probably use the latter if I had to do anything in production code.
如果我必须在生产代码中做任何事情,我可能会使用后者。
Okay, hope that helps;好的,希望有帮助; good luck!
祝你好运!
If you need an array of A interface then simply do the following:如果您需要一个 A 接口数组,那么只需执行以下操作:
const properties: A[] = [ { 'string', 0, true }, { 'string', 1, false } ];
Let me know if I understand your question right.如果我理解你的问题,请告诉我。
Thanks谢谢
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.