Typescript - 根据给定键确定子对象的类型

时间:2018-01-10 14:55:29

标签: typescript types

我试图让Intellisense使用泛型类型的界面,我喜欢智能感知和打字稿检查以适应孩子的属性,但我无法弄清楚如何写我的界面。

这是我尝试做的一个非常简单的例子

interface Foo1 {
    prop1: string;
    prop2: boolean;
}

interface Foo2 {
    prop3: string;
    prop4: boolean;
}

interface Bar1 {
    thing1: Foo1;
    thing2: Foo2;
}
interface Bar2 {
    thing3: Foo1;
    thing4: Foo2;
}

鉴于这样的结构(在我的项目中有更多的Foo&#39;并且它们都具有更多属性)我希望能够使用所选择的键来生成这样的结构< / p>

interface TestItem<T> {
    field: keyof T; //This will be a key of a Bar object
    select: (keyof T[keyof T])[]; //This is trying to state that elements in this array must be a property of Bar[field], e.g if field is "thing1" the only valid elements would be "prop1" or "prop2"
}

interface TestObject<T> {
    itemArray: TestItem<T>[];
}


let test: TestObject<Bar1> = {
            itemArray: [
                {
                    field: "thing1",
                    select: ["prop1"] //error: string is not assignable to type never
                }, {
                    field: "thing2",
                    select: ["prop3"] //error: string is not assignable to type never
                }
            ]
        }

我没想到这会起作用,但我已经尝试了很多事情而没有更好的结果。我知道我可以添加更多GenericType参数并指定传递给每个TestItem的内容,但是我希望有人知道指定字段的方式足以让Typescript在TestItem中计算select的有效值。

2 个答案:

答案 0 :(得分:1)

无法通过单步对象分配完成 - 无法根据对象初始化中其他属性的值来限制对象属性的类型。

但语法略有不同,可能:

class  TestObject<T> {

  itemArray: { field: keyof T, select: string[] }[];
  // no constraint could be specified for select here
  // because there is not enough information about T here

  // but we can define a function that will apply constraint
  // when called from a subclass
  testItem<K extends keyof T>(item: { field: K, select: (keyof T[K])[] }) {
    return item
  }
}

class TestBar1 extends TestObject<Bar1> {
  itemArray = [
    this.testItem({ field: "thing1", select: ["prop1"] }), // ok
    this.testItem({ field: "thing2", select: ["prop3"] }), // ok

    this.testItem({ field: "thing1", select: ["prop3"] })  // error:
    // Argument of type '{ field: "thing1"; select: "prop3"[]; }'is not assignable 
    // to parameter of type '{ field: "thing1"; select: ("prop1" | "prop2")[]; }'.
    //  Types of property 'select' are incompatible.
    //    Type '"prop3"[]' is not assignable to type '("prop1" | "prop2")[]'.
    //      Type '"prop3"' is not assignable to type '"prop1" | "prop2"'.
  ]
}

let test = new TestBar1().itemArray;

删除错误的项目后,推断test的类型符合预期:

 ({ field: "thing1"; select: ("prop1" | "prop2")[]; } 
| { field: "thing2"; select: ("prop3" | "prop4")[]; })[]

答案 1 :(得分:0)

我不太确定我是否正确理解了您的问题,但这有效:

interface Foo1 {
    prop1: string;
    prop2: boolean;
}

interface Foo2 {
    prop3: string;
    prop4: boolean;
}

interface Bar1 {
    thing1: Foo1;
    thing2: Foo2;
}
interface Bar2 {
    thing3: Foo1;
    thing4: Foo2;
}

interface TestItem<T, K> {
    field: keyof T;
    select: (keyof K)[]; //This keyof T being the same as field
}

interface TestObject<T, K> {
    itemArray: TestItem<T, K>[];
}

let test: TestObject<Bar1, Foo1> = {
    itemArray: [
        {
            field: "thing1",
            select: ["prop1"]
        }, {
            field: "thing2",
            select: ["prop2"]
        }
    ]
}

您的示例无效,因为prop1prop2都不归Bar1所有,因此您必须将您的通用扩展另一个(使用K这里)