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

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>

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.

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".
