mirror of
https://github.com/sindresorhus/type-fest.git
synced 2025-12-08 19:25:05 +00:00
150 lines
4.0 KiB
TypeScript
150 lines
4.0 KiB
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;
|