7

Typescript playground

I want to extract the string properties from SOME_OBJECT and get it as a union type. Therefore, in the example below, I expect STRING_KEYS to be of type "title" | "label"

interface SOME_OBJECT {
    title:      string,
    label:      string,
    someBool:   boolean,
    someDate:   Date,
    someNumber: number
}

type ExtractString<T> = keyof T extends string ? keyof T : never;

type STRING_KEYS = ExtractString<SOME_OBJECT>  // <----- THIS SHOULD BE "title" | "label"

This is what I'm getting:

enter image description here

I think I'm in the right direction here (by using conditional types), but I'm not quiet there yet. What is the best way to achieve this?

2 Answers 2

11

Got the idea from the example below from Typescript Docs: Distributive Conditional Types

enter image description here

So I've adapted it, and it works:

interface SOME_OBJECT {
    title:      string,
    label:      string,
    someBool:   boolean,
    someDate:   Date,
    someNumber: number
}

type ExtractStringPropertyNames<T> = {
    [K in keyof T]: T[K] extends string ? K : never
}[keyof T]

type STRING_KEYS = ExtractStringPropertyNames<SOME_OBJECT>

enter image description here

Typescript playground

If possible, I'm still interested in see other ways of doing that. Especially if there are more straightforward ways of doing it, as I think this answer feels like a "hack", 'cause it does not make it very clear what the code is doing.


UPDATE (HOW IT WORKS)

I've studied a bit more to understand what the code is actually doing, and I think I've figured it out.

I've broken it into two steps:

STEP 1

In the first step, a new object/type will be created.

The keys for this new object will the same keys K in keyof T as from the keys of the generic type T (SOME_OBJECT in the example).

And for the values of those keys, we'll check in the generic type T, if the values for those properties K extends string. Which means that those values are assignable to string. For example: string, "some_hard_coded_string_type" and any will all pass the test.

For each property that passes the test, we will repeat its key K name as the value for them, otherwise will pass never. You can see the result in STEP_1_RESULT below.

enter image description here

STEP 2

In this step, we'll simply get that object generated from the step 1, and ask Typescript to return all of its possible values, by doing type STEP_2<T> = T[keyof T].

Remember that keyof T represents the union of all properties from T. So Typescript will return a union with all possible values for the STEP_1_RESULT object when called with the members of the keyof T union.

In our example, it will return "title" | "label" | never | never | never.

But since the never types are meaningless inside a union type, those values are discarded and we are left with "title" | "label".

enter image description here

Sign up to request clarification or add additional context in comments.

3 Comments

Nice idea. Doesn't look hacky to me, just verbose.
@CertainPerformance , thanks. You're right. Now that I've studied in more details of how it works, it's feels less weird to me. I guess I'm just not used to see this pattern.
You can also create the type in one step. In case you run out of type names to give. type ExtractStringPropertyNames<T> = { [K in keyof T]: T[K] extends string ? K : never }[keyof T];
0

I am not really sure about the conditional types. What I would probably try to use here is the Pick and I would do something like

interface SOME_OBJECT {
    title:      string,
    label:      string,
    someBool:   boolean,
    someDate:   Date,
    someNumber: number
}

type ExtractString = Pick<SOME_OBJECT, "title" | "label">;

type STRING_KEYS = ExtractString

1 Comment

Thanks, Georgios. Your answer works for this specific example. But I need a generic way of doing it. One that should work for any object that might appear with any amount of properties and property names.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.