You can use mapped types combined with conditional types here:
type ExtractKeysOfType<T, Target> = {
[K in keyof T]: T[K] extends Target ? K : never
}[keyof T];
This essentially works by going over each key in type T. Does T[K]
extend our Target type? if so, great, and the property value is simply that Key. If not, that key has type never
.
This intermediary type, for your case, looks like:
{
a: "a";
b: never;
c: "c";
d: never;
}
Then, that intermediary type is indexed again by keys of T. This will produce your desired union, since the never
types are ignored by the compiler here.
Playground