After some experimentation, I have found a solution to my problem. Although there may be more efficient ways to handle these type conversions, the current method is functional:
type PromiseFunc = () => Promise<unknown>;
// Define types to exclude certain keys.
type SomeOtherType1 = {
key1: string[];
};
type SomeOtherType2 = {
key1: number;
};
type A = {
key1: string;
key2: string;
key3: PromiseFunc;
key4: string;
key5: PromiseFunc;
key6: SomeOtherType1[];
key7: SomeOtherType2[];
key8: string;
key9: PromiseFunc;
};
// Create a filter type to handle specified conditions and convert other types.
type Filter<T, ConditionOne, ConditionTwo> = {
[Key in keyof T]: T[Key] extends ConditionOne
? string
: T[Key] extends ConditionTwo
? string
: number;
};
// Convert to a new type with only string and number types.
type ConvertedType = Filter<A, string, PromiseFunc>;
// Drop keys that do not match the desired type (string).
type PickByValueType<T, U> = {
[K in keyof T as K extends K ? (T[K] extends U ? K : never) : never]: T[K];
};
// Generate final type with only string keys.
type FilteredType = PickByValueType<ConvertedType, string>;
The primary issue I faced was when using never
instead of number
, which did not effectively exclude unwanted keys. This discrepancy was clarified through an investigation into TypeScript's behavior, particularly in version 4.1.2. While further optimization may involve combining the Filter
and PickByValueType
types, the current implementation suffices.
In case future users encounter similar challenges, employing two type conversions proves essential for accommodating multiple types. To include various types, a flexible approach can be applied:
...
type GrabTypes<T, U, U2> = {
[K in keyof T as K extends K
? T[K] extends U
? K
: T[K] extends U2
? K
: never
: never]: T[K];
};
type GrabbedTypes = GrabTypes<A, string, SomeOtherType1[]>
Despite existing resources on utilizing omit
for excluding specific types, examples addressing the exclusion of multiple keys with diverse types remain scarce. Extending this approach to incorporate conversion could enhance its versatility within the confines of ternary operators:
...
type GrabTypesAndConvertOne<T, U, TypeToConvert> = {
[K in keyof T as K extends K
? T[K] extends U
? K
: T[K] extends TypeToConvert
? K // <- Implement type change logic here
: never
: never]: T[K];
};
type GrabbedTypes = GrabTypesAndConvertOne<A, string, PromiseFunc>