Reputation: 1076
I'd like to come up with some conditional to extract only the properties from an object whose value is an array.
For example:
type Person = {
name: string
addresses: Address[]
age: number
phoneNumbers: PhoneNumber[]
}
PullOutArrays<Person> => {
phoneNumbers: PhoneNumber[]
addresses: Address[]
}
I attempted something like this to no avail:
type PulledOutArrays<T extends Record<string, unknown>> = {
[K in keyof T]: T[K] extends unknown[] ? T[K] : never
}
Upvotes: 3
Views: 3043
Reputation: 214927
Or if you are using TS 4.1+, a more direct approach could be to remove keys by asserting the key as never
based on the value type:
type OnlyArraysPerson = {
[K in keyof Person as Person[K] extends unknown[] ? K : never]: Person[K]
}
To be more generic:
type OnlyArrays<T> = {
[K in keyof T as T[K] extends unknown[] ? K : never]: T[K]
}
type ArrayPerson = OnlyArrays<Person>
Upvotes: 2
Reputation: 9354
Cribbing from this GitHub issue:
type FilteredKeys<T, U> = { [P in keyof T]: T[P] extends U ? P : never }[keyof T];
type FilteredProperties<T, U> = { [K in FilteredKeys<T, U>]: T[K]; };
type PullOutArrays<T> = FilteredProperties<T, unknown[]>;
type Person = {
name: string
addresses: string[]
age: number
phoneNumbers: number[]
}
type PersonKeys = FilteredKeys<Person, unknown[]>;
// "addresses" | "phoneNumbers"
type PersonArrays = PullOutArrays<Person>;
// {
// addresses: string[];
// phoneNumbers: number[];
// }
As you may have seen from your original attempt, what you end up with after mapping the conditional:
{ [P in keyof T]: T[P] extends U ? P : never }
Is an interface containing all the property keys, but with the non-array types changed to never
, apparently because TS does not initially check what the types assigned to property keys are. By adding an index on the end, you force the compiler to read the value of the property and ultimately omit it from the result.
Using the union which is generated, you can then create a new mapped type from that union on the original interface.
Upvotes: 6
Reputation: 1076
Came to a solution by following this answer:
type KeysOfType<T, U, B = false> = {
[P in keyof T]: B extends true
? T[P] extends U
? U extends T[P]
? P
: never
: never
: T[P] extends U
? P
: never
}[keyof T]
type PickByType<T, U, B = false> = Pick<T, KeysOfType<T, U, B>>
type PickArrays<T> = PickByType<T, unknown[]>
@lawrence-witt's answer is more elegant and should probably be used.
Upvotes: 1