type-fest
Version:
A collection of essential TypeScript types
150 lines (136 loc) • 4.14 kB
TypeScript
import type {BuildObject, BuildTuple, NonRecursiveType, ObjectValue} from './internal';
import type {IsNever} from './is-never';
import type {Paths} from './paths';
import type {Simplify} from './simplify.d';
import type {UnionToIntersection} from './union-to-intersection.d';
import type {UnknownArray} from './unknown-array';
/**
Pick properties from a deeply-nested object.
It supports recursing into arrays.
Use-case: Distill complex objects down to the components you need to target.
@example
```
import type {PickDeep, PartialDeep} from 'type-fest';
type Configuration = {
userConfig: {
name: string;
age: number;
address: [
{
city1: string;
street1: string;
},
{
city2: string;
street2: string;
}
]
};
otherConfig: any;
};
type NameConfig = PickDeep<Configuration, 'userConfig.name'>;
// type NameConfig = {
// userConfig: {
// name: string;
// }
// };
// Supports optional properties
type User = PickDeep<PartialDeep<Configuration>, 'userConfig.name' | 'userConfig.age'>;
// type User = {
// userConfig?: {
// name?: string;
// age?: number;
// };
// };
// Supports array
type AddressConfig = PickDeep<Configuration, 'userConfig.address.0'>;
// type AddressConfig = {
// userConfig: {
// address: [{
// city1: string;
// street1: string;
// }];
// };
// }
// Supports recurse into array
type Street = PickDeep<Configuration, 'userConfig.address.1.street2'>;
// type Street = {
// userConfig: {
// address: [
// unknown,
// {street2: string}
// ];
// };
// }
```
@category Object
@category Array
*/
export type PickDeep<T, PathUnion extends Paths<T>> =
T extends NonRecursiveType
? never
: T extends UnknownArray
? UnionToIntersection<{
[P in PathUnion]: InternalPickDeep<T, P>;
}[PathUnion]
>
: T extends object
? Simplify<UnionToIntersection<{
[P in PathUnion]: InternalPickDeep<T, P>;
}[PathUnion]>>
: never;
/**
Pick an object/array from the given object/array by one path.
*/
type InternalPickDeep<T, Path extends string | number> =
T extends NonRecursiveType
? never
: T extends UnknownArray ? PickDeepArray<T, Path>
: T extends object ? Simplify<PickDeepObject<T, Path>>
: never;
/**
Pick an object from the given object by one path.
*/
type PickDeepObject<RecordType extends object, P extends string | number> =
P extends `${infer RecordKeyInPath}.${infer SubPath}`
? ObjectValue<RecordType, RecordKeyInPath> extends infer ObjectV
? IsNever<ObjectV> extends false
? BuildObject<RecordKeyInPath, InternalPickDeep<NonNullable<ObjectV>, SubPath>, RecordType>
: never
: never
: ObjectValue<RecordType, P> extends infer ObjectV
? IsNever<ObjectV> extends false
? BuildObject<P, ObjectV, RecordType>
: never
: never;
/**
Pick an array from the given array by one path.
*/
type PickDeepArray<ArrayType extends UnknownArray, P extends string | number> =
// Handle paths that are `${number}.${string}`
P extends `${infer ArrayIndex extends number}.${infer SubPath}`
// When `ArrayIndex` is equal to `number`
? number extends ArrayIndex
? ArrayType extends unknown[]
? Array<InternalPickDeep<NonNullable<ArrayType[number]>, SubPath>>
: ArrayType extends readonly unknown[]
? ReadonlyArray<InternalPickDeep<NonNullable<ArrayType[number]>, SubPath>>
: never
// When `ArrayIndex` is a number literal
: ArrayType extends unknown[]
? [...BuildTuple<ArrayIndex>, InternalPickDeep<NonNullable<ArrayType[ArrayIndex]>, SubPath>]
: ArrayType extends readonly unknown[]
? readonly [...BuildTuple<ArrayIndex>, InternalPickDeep<NonNullable<ArrayType[ArrayIndex]>, SubPath>]
: never
// When the path is equal to `number`
: P extends `${infer ArrayIndex extends number}`
// When `ArrayIndex` is `number`
? number extends ArrayIndex
? ArrayType
// When `ArrayIndex` is a number literal
: ArrayType extends unknown[]
? [...BuildTuple<ArrayIndex>, ArrayType[ArrayIndex]]
: ArrayType extends readonly unknown[]
? readonly [...BuildTuple<ArrayIndex>, ArrayType[ArrayIndex]]
: never
: never;