How to dynamically get the interface or type alias property value types from conditional types in TypeScript?

Published January 29, 2022

To dynamically get an interface or type alias property value type from conditional types, you can use the infer keyword followed by declaring a generic type name wherever you need to get the value from and then using that value as the truthy value in the conditional type check in TypeScript.

TL;DR

// a generic conditional type
type GetSalaryType<T> = T extends { salary: infer value } ? value : never;

// an interface where the salary
// property is `string` type
interface Person {
  name: string;
  salary: string;
}

// an interface where the salary
// property is `number` type
interface Person2 {
  name: string;
  salary: number;
}

// an interface where the salary
// property is an `array of string` type
interface Person3 {
  name: string;
  salary: string[];
}

// get the type of the `salary` property
// from the all the above interfaces
type SalaryType = GetSalaryType<Person>; // string
type SalaryType2 = GetSalaryType<Person2>; // number
type SalaryType3 = GetSalaryType<Person3>; // string[]

For example, first, let's consider a small scenario where we need to choose between the type string and the number type by checking if the interface or the type alias contains a property called salary.

So in this case the better way is to use a conditional type and make the whole type into a generic type alias.

It can be done like this,

// a generic conditional type
type GetSalaryType<T> = T extends { salary: string } ? string : never;

Now let's create an interface called Person with properties called name and salary having the type of string.

It can be done like this,

// a generic conditional type
type GetSalaryType<T> = T extends { salary: string } ? string : never;

// a simple interface called Person
interface Person {
  name: string;
  salary: string;
}

Now let's pass the Person interface to the GetSalaryType generic conditional type like this,

// a generic conditional type
type GetSalaryType<T> = T extends { salary: string } ? string : never;

// a simple interface called Person
interface Person {
  name: string;
  salary: string;
}

// get the type of the `salary` property
// from the `Person` interface
type SalaryType = GetSalaryType<Person>; // string

Now if you hover over the SalaryType type you can see that the type is of string that is correct and satisfies our needs in this case.

But we know that salary can have many forms like string, number or even an array of salaries. If we pass a type where the salary type is number to the GetSalaryType then also we will get the type of string in the SalaryType type alias which may not suit our needs.

So to make the GetSalaryType conditional generic type into a more flexible type let's use the infer keyword after the salary property in the condition followed by a generic type name. Let's name the generic type name as value and then use that as the truthy value in the condition.

It can be done like this,

// a generic conditional type
type GetSalaryType<T> = T extends { salary: infer value } ? value : never;

// a simple interface called Person
interface Person {
  name: string;
  salary: string;
}

// get the type of the `salary` property
// from the `Person` interface
type SalaryType = GetSalaryType<Person>; // string

Now let's make 2 more interfaces called Person2 and Person3 where the salary property is of number type and an array of strings type respectively and then pass that to the GetSalaryType conditional generic type.

It can be done like this,

// a generic conditional type
type GetSalaryType<T> = T extends { salary: infer value } ? value : never;

// an interface where the salary
// property is `string` type
interface Person {
  name: string;
  salary: string;
}

// an interface where the salary
// property is `number` type
interface Person2 {
  name: string;
  salary: number;
}

// an interface where the salary
// property is an `array of string` type
interface Person3 {
  name: string;
  salary: string[];
}

// get the type of the `salary` property
// from the all the above interfaces
type SalaryType = GetSalaryType<Person>; // string
type SalaryType2 = GetSalaryType<Person2>; // number
type SalaryType3 = GetSalaryType<Person3>; // string[]

As you can see that the SalaryType, SalaryType2, and the SalaryType3 have different types according to the interfaces passed to the GetSalaryType conditional generic type.

We have dynamically got the interface property value type from conditional types successfully in TypeScript. Yay 🥳!

See the above code live in codesandbox.

That's all 😃!

Feel free to share if you found this useful 😃.


Share on: Facebook Twitter