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.
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.
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:
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.
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.
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"
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With