简体   繁体   English

Typescript 创建一个类型,其属性是接口的属性

[英]Typescript Create a type whose properties are the properties of an interface

I have an interface definition as follows我有一个接口定义如下

export interface RoutingMap {
    ticket: {
        id: number
    },
    car: {
        model: string,
        make: number
    }
}

I want to be able to create an interface for an object, which has 2 properties - 1 being a key in the RoutingMap and the other being whatever the type of that Key in the RoutingMap is.我希望能够为 object 创建一个接口,该接口具有 2 个属性 - 1 是RoutingMap RoutingMapKey的类型。

In pseudo-code I image it looking like this在伪代码中,我想象它看起来像这样

export interface RoutingMessage {
    resource: keyof RoutingMap,
    params: RoutingMap["resource"]
}

The end goal is for me to be able to construct objects like this最终目标是让我能够构造这样的对象

const msg: RoutingMessage = {
    resource: "ticket",
    params: { id: 10 }
}

const invalidMsg: RoutingMessage = {
    resource: "something", //TS error - "something" is not a key of routing map
    params: { id: 10 }
}

const invalidMsg2: RoutingMessage = {
    resource: "car"
    params: { model: "tesla", make: true } //TS error - "make" is not a boolean
}

const invalidMsg3: RoutingMessage = {
    resource: "car"
    params: { id: 123 } //TS error - id is not assinable to  {model: string, make: boolean}
}

The RoutingMap is something which would be extended with time, and people should be able to create specific objects, based on the keys (and their values) RoutingMap会随着时间的推移而扩展,人们应该能够根据键(及其值)创建特定的对象

As you can see from the pseudo-code example I can set a constraint for the resource, but I need a way to constrain the params property to only allow objects, which match the signature of the Key in the RoutingMap正如您从伪代码示例中看到的那样,我可以为资源设置约束,但我需要一种方法来约束 params 属性以仅允许与RoutingMap中的Key签名匹配的对象

export interface RoutingMessage {
    resource: keyof RoutingMap,
    params: RoutingMap[this["resource"]]
}

Because there are a finite number of keys that match keyof RoutingMap , you can write RoutingMessage as a union of object types where each element of the union corresponds to a particular key, like this:因为有有限数量的键匹配keyof RoutingMap ,您可以将RoutingMessage编写为 object 类型的联合,其中联合的每个元素对应于特定的键,如下所示:

type RoutingMessage = {
    resource: "ticket";
    params: {
        id: number;
    };
} | {
    resource: "car";
    params: {
        model: string;
        make: number;
    };
}

You can compute RoutingMessage programmatically from RoutingMap by writing it as a "distributive object type" (terminology borrowed from microsoft/TypeScript#47109 ) where we make a mapped type where each property with key K in RoutingMap is mapped to the desired object type for that key, and then we immediately index into it with keyof RoutingMap , producing the desired union.您可以通过将RoutingMessage编写为“分布式 object 类型”(从microsoft/TypeScript#47109借用的术语)以编程方式从RoutingMap计算 RoutingMessage,其中我们制作了一个映射类型,其中RoutingMap中具有键K的每个属性都映射到所需的 object 类型key,然后我们立即使用keyof RoutingMap对其进行索引,生成所需的联合。

type RoutingMessage = { [K in keyof RoutingMap]:
  { resource: K, params: RoutingMap[K] }
}[keyof RoutingMap];

With the above definition, RoutingMessage will gain new union members whenever a new property is added to RoutingMap , as desired.使用上面的定义,每当向RoutingMap添加新属性时, RoutingMessage都会根据需要获得新的联合成员。


Let's make sure it behaves how you want:让我们确保它的行为符合您的要求:

const msg: RoutingMessage = {
  resource: "ticket",
  params: { id: 10 }
}; // okay

const invalidMsg: RoutingMessage = {
  resource: "something", // error!
  //~~~~~~ <-- Type '"something"' is not assignable to type '"ticket" | "car"'
  params: { id: 10 }
};

const invalidMsg2: RoutingMessage = {
  resource: "car",
  params: { model: "tesla", make: true } // error!
  //  --------------------> ~~~~
  // Type 'boolean' is not assignable to type 'number'.
};

const invalidMsg3: RoutingMessage = {
  resource: "car",
  params: { id: 123 } // error!
  // -----> ~~~~~~~
  // Type '{ id: number; }' is not assignable to type '{ model: string; make: number; }' 
};

Looks good!看起来不错!

Playground link to code Playground 代码链接

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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