Bhupesh
Bhupesh

Reputation: 141

Typescript how to define object in which key's value depends on another key

I am new to typescript and stuck in defining type of JSON object. In the following examples I want to define the type for myJSONObject. The required types which I defined are JSONType_1 and JSONType_2.

type RequireOnlyOne<T, Keys extends keyof T = keyof T> =
    Pick<T, Exclude<keyof T, Keys>>
    & {
      [K in Keys]-?: Required<Pick<T, K>> & Partial<Record<Exclude<Keys, K>, undefined>>
    }[Keys];

interface NetworkIdOptions {
  group?: string;
  individual?: string;
}

type NetworkId = RequireOnlyOne<NetworkIdOptions, 'group' | 'individual'>;

export interface JSONType_1 {
  networkId: NetworkId;
  tasks: Array<
  | taskInterface_1
  | taskInterface_2
  | taskInterface_3
  >;
}
export interface JSONType_2 {
  networkId: {
    group: 'myFavGroup';
  };
  tasks: Array<
  | taskInterface_4
  | taskInterface_5
  >;
}

As per this networkId can either have group key or individual key. When networkId has group key and who's value is = myFavGroup, then I want this object to be type of JSONType_2. Notice the tasks changes when group name is myFavGroup. Use case for above defined JSONType_1 & JSONType_2:

myFunc(myJSONObject: JSONType_1 | JSONType_2) {
  if (myJSONObject.networkId.group && myJSONObject.networkId.group === 'myFavGroup') {
    differentFunction(myJSONObject) // this function takes input only in `JSONType_2`.
  }
}

Inside this if condition I want myJSONObject to have JSONType_2 and not JSONType_1 | JSONType_2 types. As I want to send only JSONType_2 to a different function. Error which I get:

Argument of type 'JSONType_1 | JSONType_2' is not assignable to parameter of type 'JSONType_2'.
...
Types of property 'group' are incompatible.
Type 'string' is not assignable to type '"myFavGroup"'.

myJSONObject is received from another service who's structures are fixed. 2 Examples:

{
  networkId: {
    group: 'Desktop_group';
  };
  tasks: [
    { cmd: 'playMusic', songName: 'xyz' },
    { cmd: 'playVideo', videoName: 'abc' },
  ];
}
{
  networkId: {
    group: 'myFavGroup';
  };
  tasks: [
    { cmd: 'sendMusicList', path: '/home/user/musicFolder' },
    { cmd: 'sendFilesStats', path: '/home/user/someFolder' },
  ];
}

Notice all tasks JSON objects are also predefined and fixed. The only difference is that tasks for myFavGroup are different than any other group.

If there is a better way to code than what I am doing, I would appreciate your help in that too. Thanks in advance.

Upvotes: 0

Views: 1054

Answers (1)

Dmitry Papka
Dmitry Papka

Reputation: 1359

Your code looks a bit dirty and I would consider some refactor, but!

// if group = 'myFavGroup' only for JSONType_2 you can exclude it for JSONType_1
interface NetworkIdOptions {
  group?: Exclude<'myFavGroup', string>; // <---
  individual?: string;
}

and

to make types compatible, you need to make group property optional for JSONType_2

export interface JSONType_2 {
  networkId: {
    group?: 'myFavGroup'; // <---
  };
}

This way after checking that networkId.group is equal to 'myFavGroup' your Typescript compiler will automagically understand that your argument is of type JSONType_2 and you can successfully pass it to the differentFunction

Another solution would be just to use type assertion:

differentFunction(myJSONObject as JSONType_2)

Upvotes: 1

Related Questions