创建作为对象属性数组的类型

时间:2019-04-28 19:01:38

标签: typescript

所以我正在尝试这种行为:

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

此代码的要点是,如果我在接口A中添加新字段,则还需要将该属性包括在数组中。如果缺少任何属性,构建将失败。

感谢您的帮助。

3 个答案:

答案 0 :(得分:1)

使用keyof

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

答案 1 :(得分:1)

如果您真的很在意财产顺序,那么如果您定义

,那么它实际上是this question about turning a union into a tuple的副本
type PropertyArray<T> = TuplifyUnion<keyof T>

但是希望您实际上并不关心财产顺序,['prop2', 'prop1', 'prop3']properties的可接受值。在这种情况下,我可以通过两种方式进行此操作:


一种方法是按照您的要求,将PropertyArray<T>实际计算为元组中所有可能的键排列的并集。这自然会涉及循环条件类型which is not currently supported。相反,我可以定义一个支持类型的属性,该类型最多包含一些固定数量的属性,例如:

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

对于您的情况:

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

这可以按您想要的方式工作,但是对于编译器来说是很多工作,并且可能很脆弱。


不太疯狂的解决方案(仍然有点疯狂)是使用辅助函数而不是类型别名。仅在辅助函数的参数完全包含一次相关对象类型的每个键的情况下,该函数才会编译:

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;

然后尝试:

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

也可以。如果我必须在生产代码中执行任何操作,则可能会使用后者。


好的,希望能有所帮助;祝你好运!

答案 2 :(得分:0)

如果需要A接口数组,则只需执行以下操作:

const properties: A[] = [ { 'string', 0, true }, { 'string', 1, false } ];

如果您的问题正确,请告诉我。

谢谢