Home > Software design >  How to make typescript infer response type based on response's property (ex: "type")
How to make typescript infer response type based on response's property (ex: "type")

Time:10-04

I was trying to create a function that returns an object with a "type" property that'll determine the type of response (Either TResA or TResB)

Basically this:

enum SYSTEM_TYPE {
A = 1, 
B = 2
}
export interface IResCommon {
   type: SYSTEM_TYPE;
}
export type TResA = {
   name: string;
} & IResCommon;
export type TResB = {
  age: number;
} & IResCommon;

export type TResAll = TResA | TResB;



// Don't know how to apply this: 
export type TResCondition<T extends TResA & TResB> = T['type'] extends SYSTEM_TYPE.A ? TResA : TResB;

function run():TResAll /*TResCondition<T>*/{
   const math = Math.floor(Math.random()*2)   1;
   if (math === SYSTEM_TYPE.A) {
     return { name: "foo", type: SYSTEM_TYPE.A}
   } else {
     return { age: 12, type: SYSTEM_TYPE.B}
   }
}

var o = run()
if (o.type === SYSTEM_TYPE.A) {
     // Should assume o as type TResA
   console.log(o.name) //<--- Property 'name' does not exist on type 'TResAll'
} else {
     // Should assume o as type TResB
   console.log(o.age) // Property 'age' does not exist on type 'TResAll'
}

How can I correctly apply TResCondition in order to avoid property 'X' is undefined after checking the value of o.type?

Here's a link to the typescript playground;

CodePudding user response:

You need to make the union a discriminated union. To do this you need to add the type property, with the appropriate enum literal type to TResA and TResB:

export type TResA = {
   name: string;
   type: SYSTEM_TYPE.A;
} & IResCommon;
export type TResB = {
  age: number;
  type: SYSTEM_TYPE.B;
} & IResCommon;

Playground Link

  • Related