0

I have a bunch of data types that all contain a property dataType and I map each one to that value:

interface GroupData {
    dataType: "group";
    name: string;
    people: PersonData[];
}

interface PersonData {
    dataType: "person";
    name: string;
    email: string;
    messages: MessageData[];
}

interface MessageData {
    dataType: "message";
    message: string;
    timestamp: number;
}

interface DataTypeMap {
    group: GroupData;
    person: PersonData;
    message: MessageData;
}

This serves my purposes, but is a little bit manual. In my actual app I have a lot more data types, and they change somewhat frequently. What I'd like is to be able to automate the process a little bit more. So far this is as close as I've come:

type DataType = GroupData
    | PersonData
    | MessageData;

type DataTypeMap = {
    [(T in DataType)["dataType"]]: T;
};

Moderating a single union of data types is much easier than a map where keys must be of a certain value; however this isn't kosher in TypeScript. I'm getting the following TS errors:

A computed property name in a type literal must refer to an expression whose type is a literal type or a 'unique symbol' type.
Cannot find name 'T'.
'DataType' only refers to a type, but is being used as a value here.

As far as I can tell these are all problems with my syntax. I'm 90% sure you can't use in with unions that aren't strings and using bracket selectors in an index signature feels like a stretch.

Is it possible to map a list of data types based on a property that all of the data types have?

Sandy Gifford
  • 7,219
  • 3
  • 35
  • 65
  • 1
    This is possible using conditional types https://stackoverflow.com/questions/50125893/typescript-derive-map-from-discriminated-union – lissitz Oct 30 '20 at 00:34

1 Answers1

1

You are so close! Here's what we want:

type DataTypeMap = {
    [T in DataType['dataType']]: Extract<DataType, {dataType: T}>;
};

DataType is the union of all the type interfaces. DataType['dataType'] is the union of all the type name string literals.

In our map, we say that the keys are those string names. For each key, we want to find the corresponding interface. We use Extract to get only the members of the union which can have {dataType: T}.

This resolves to:

type DataTypeMap = {
    group: GroupData;
    person: PersonData;
    message: MessageData;
}

Typescript Playground Link

Linda Paiste
  • 38,446
  • 6
  • 64
  • 102