简体   繁体   English

创建一个类型,它是对象属性的数组

[英]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.谢谢您的帮助。

Use keyof :使用keyof

type PropertyArray = Array<keyof A>; // 'prop1' | 'prop2' | 'prop3'
type PropertyArray<T> = Array<keyof T>;

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM