Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I extract the names of all fields of a specific type from an interface in Typescript?

Tags:

typescript

Let's say I have an interface like this:

interface SomeInterface {
    field1: string;
    field2: number;
    field3: string;
    field4: SomeOtherType;
}

I'd like to have a type that can extract all the keys who's values are a specific type:

type ExtractFieldsOfType<Obj extends {}, Type> = // implementation

ExtractFieldsOfType<SomeInterface, string> // "field1" | "field3"
ExtractFieldsOfType<SomeInterface, number> // "field2"
ExtractFieldsOfType<SomeInterface, SomeOtherType> // "field4"

Is it possible to write something like this? I'm writing a function that operates on an object by key name, but it should only allow keys with specific values. So it'd be nice to express that through the type system.

like image 380
Brian Schlenker Avatar asked Jun 17 '18 21:06

Brian Schlenker


People also ask

How do you extend an interface in typescript?

interface s can also extend from multiple types. interface s allowed us to build up new types from other types by extending them. TypeScript provides another construct called intersection types that is mainly used to combine existing object types. An intersection type is defined using the & operator.

What is an object type in typescript?

In JavaScript, the fundamental way that we group and pass around data is through objects. In TypeScript, we represent those through object types. As we’ve seen, they can be anonymous:

What is interfacepersonreadonly in typescript?

interfacePersonReadonly{ readonlyname: string; readonlyage: number; This happens often enough in JavaScript that TypeScript provides a way to create new types based on old types — mapped types. In a mapped type, the new type transforms each property in the old type in the same way.

What is the use of filter in typescript?

In Typescript, Filter () is a built-in array method which is defined as a method for creating a new array or set of elements that contains a subset of the given array elements by returning the array of all the values of the elements in the newly created sub-array over the given array.


Video Answer


1 Answers

You can do it in TypeScript 2.8, with conditional types.

I created support type AllowedFieldsWithType to make cleaner implementation.

type AllowedFieldsWithType<Obj, Type> = {
    [K in keyof Obj]: Obj[K] extends Type ? K : never
};

type ExtractFieldsOfType<Obj, Type> = AllowedFieldsWithType<Obj, Type>[keyof Obj]

type StringFields = ExtractFieldsOfType<SomeInterface, string> // "field1" | "field3"
type NumberFields = ExtractFieldsOfType<SomeInterface, number> // "field2"
type ObjectFields = ExtractFieldsOfType<SomeInterface, SomeOtherType> // "field4"

How it works?

First type:

type AllowedFieldsWithType<Obj, Type> = {
    [K in keyof Obj]: Obj[K] extends Type ? K : never
};

1) For each field in object, we check if it extends Type, then we return name of the key as it's type. If not (e.g. we search for string, but field2 is type number - we return never. It's special type that nothing can be assigned to.

AllowedFieldsWithType<SomeInterface, String> equals to: {
  field1: 'field1';
  field2: never;
  field3: 'field3';
  field4: never;
}

2) Then we have to filter out never properties, we can do it with Pick, but for this use case we need only key names.

3) Eventually we use someType[keyof Obj] which gather values of given types, skipping never.

I've written article which explains this topic in depth: Create a condition-based subset types

like image 164
Piotr Lewandowski Avatar answered Sep 22 '22 21:09

Piotr Lewandowski