简体   繁体   中英

Can I infer types based on other properties in an object?

I've noticed a pattern in my code and I feel like there's a better solution for this.

Let's say I have these basic entities

type User = {
  id: string,
}

type Company = {
  id: number,
}

enum Member {
 Company = "Company",
 Member = "Member",
}

Now oftentimes I'm passing around payloads like this

type Payload = {
  type: Member,
  payload: Company["id"][] | User["id"][] // string[] | number[]
}

Now the question is, can I infer the type of the payload if I have checked the type? Right now I have to declare the type.

if (payload.type === Member.Company)
   result = (payload.payload as number[]).map(etc) // have to declare type of array, how to infer?

In order for that to work, you want your Payload type to be a discriminated union . Right now, your Payload is a non-union type, each of whose properties is a union; as such, it can accept values you don't want, such as where the type is Company but the payload has an id from User . The union should be pushed up to the top level, where you spell out the relationship involved:

type Payload =
  { type: Member.Company, payload: Company["id"][] } |
  { type: Member.Member, payload: User["id"][] };

Now that we have a top-level union, and since each member's type property is a unit type (only has a single possible value), the compiler will understand checking type to discriminate the union:

  if (payload.type === Member.Company) {
    payload.payload.map(x => x + 1); // okay
  } else {
    payload.payload.map(x => x.toUpperCase()); // okay
  }

If you have lots of enum members you might consider making a mapping interface representing the relationship between each enum member and the object it points to, and using it to calculate Payload so that you don't have to write out a big union with nearly identical {type: Member.XXX, payload: YYY["id"][]} members:

interface MemberMapping {
  [Member.Company]: Company;
  [Member.Member]: User;
}

type Payload = {
  [K in Member]: { type: K, payload: MemberMapping[K]["id"][] }
}[Member];
/* type Payload = {
    type: Member.Company;
    payload: number[];
} | {
    type: Member.Member;
    payload: string[];
} */

Playground link to code

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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