react-querybuilder
Version:
React Query Builder component for constructing queries and filters, with utilities for executing them in various database and evaluation contexts
212 lines (176 loc) • 7.4 kB
text/typescript
import type { Simplify } from "../simplify.mjs";
import type { UnknownArray } from "../unknown-array.mjs";
import type { IsEqual } from "../is-equal.mjs";
import type { KeysOfUnion } from "../keys-of-union.mjs";
import type { RequiredKeysOf } from "../required-keys-of.mjs";
import type { Merge } from "../merge.mjs";
import type { IfAny } from "../if-any.mjs";
import type { IfNever } from "../if-never.mjs";
import type { OptionalKeysOf } from "../optional-keys-of.mjs";
import type { FilterDefinedKeys, FilterOptionalKeys } from "./keys.mjs";
import type { NonRecursiveType } from "./type.mjs";
import type { ToString } from "./string.mjs";
/**
Create an object type with the given key `<Key>` and value `<Value>`.
It will copy the prefix and optional status of the same key from the given object `CopiedFrom` into the result.
@example
```
type A = BuildObject<'a', string>;
//=> {a: string}
// Copy `readonly` and `?` from the key `a` of `{readonly a?: any}`
type B = BuildObject<'a', string, {readonly a?: any}>;
//=> {readonly a?: string}
```
@group type-fest
*/
export type BuildObject<
Key extends PropertyKey,
Value,
CopiedFrom extends object = {}
> = Key extends keyof CopiedFrom ? Pick<{ [_ in keyof CopiedFrom] : Value }, Key> : Key extends `${infer NumberKey extends number}` ? NumberKey extends keyof CopiedFrom ? Pick<{ [_ in keyof CopiedFrom] : Value }, NumberKey> : { [_ in Key] : Value } : { [_ in Key] : Value };
/**
Returns a boolean for whether the given type is a plain key-value object.
@group type-fest
*/
export type IsPlainObject<T> = T extends NonRecursiveType | UnknownArray | ReadonlyMap<unknown, unknown> | ReadonlySet<unknown> ? false : T extends object ? true : false;
/**
Extract the object field type if T is an object and K is a key of T, return `never` otherwise.
It creates a type-safe way to access the member type of `unknown` type.
@group type-fest
*/
export type ObjectValue<
T,
K
> = K extends keyof T ? T[K] : ToString<K> extends keyof T ? T[ToString<K>] : K extends `${infer NumberK extends number}` ? NumberK extends keyof T ? T[NumberK] : never : never;
/**
For an object T, if it has any properties that are a union with `undefined`, make those into optional properties instead.
@example
```
type User = {
firstName: string;
lastName: string | undefined;
};
type OptionalizedUser = UndefinedToOptional<User>;
//=> {
// firstName: string;
// lastName?: string;
// }
```
@group type-fest
*/
export type UndefinedToOptional<T extends object> = Simplify<{ [Key in keyof Pick<T, FilterDefinedKeys<T>>] : T[Key] } & { [Key in keyof Pick<T, FilterOptionalKeys<T>>]? : Exclude<T[Key], undefined> }>;
/**
Works similar to the built-in `Pick` utility type, except for the following differences:
- Distributes over union types and allows picking keys from any member of the union type.
- Primitives types are returned as-is.
- Picks all keys if `Keys` is `any`.
- Doesn't pick `number` from a `string` index signature.
@example
```
type ImageUpload = {
url: string;
size: number;
thumbnailUrl: string;
};
type VideoUpload = {
url: string;
duration: number;
encodingFormat: string;
};
// Distributes over union types and allows picking keys from any member of the union type
type MediaDisplay = HomomorphicPick<ImageUpload | VideoUpload, "url" | "size" | "duration">;
//=> {url: string; size: number} | {url: string; duration: number}
// Primitive types are returned as-is
type Primitive = HomomorphicPick<string | number, 'toUpperCase' | 'toString'>;
//=> string | number
// Picks all keys if `Keys` is `any`
type Any = HomomorphicPick<{a: 1; b: 2} | {c: 3}, any>;
//=> {a: 1; b: 2} | {c: 3}
// Doesn't pick `number` from a `string` index signature
type IndexSignature = HomomorphicPick<{[k: string]: unknown}, number>;
//=> {}
@group type-fest
*/
export type HomomorphicPick<
T,
Keys extends KeysOfUnion<T>
> = { [P in keyof T as Extract<P, Keys>] : T[P] };
/**
Extract all possible values for a given key from a union of object types.
@example
```
type Statuses = ValueOfUnion<{ id: 1, status: "open" } | { id: 2, status: "closed" }, "status">;
//=> "open" | "closed"
```
@group type-fest
*/
export type ValueOfUnion<
Union,
Key extends KeysOfUnion<Union>
> = Union extends unknown ? Key extends keyof Union ? Union[Key] : never : never;
/**
Extract all readonly keys from a union of object types.
@example
```
type User = {
readonly id: string;
name: string;
};
type Post = {
readonly id: string;
readonly author: string;
body: string;
};
type ReadonlyKeys = ReadonlyKeysOfUnion<User | Post>;
//=> "id" | "author"
```
@group type-fest
*/
export type ReadonlyKeysOfUnion<Union> = Union extends unknown ? keyof { [Key in keyof Union as IsEqual<{ [K in Key] : Union[Key] }, { readonly [K in Key] : Union[Key] }> extends true ? Key : never] : never } : never;
/**
Merges user specified options with default options.
@example
```
type PathsOptions = {maxRecursionDepth?: number; leavesOnly?: boolean};
type DefaultPathsOptions = {maxRecursionDepth: 10; leavesOnly: false};
type SpecifiedOptions = {leavesOnly: true};
type Result = ApplyDefaultOptions<PathsOptions, DefaultPathsOptions, SpecifiedOptions>;
//=> {maxRecursionDepth: 10; leavesOnly: true}
```
@example
```
// Complains if default values are not provided for optional options
type PathsOptions = {maxRecursionDepth?: number; leavesOnly?: boolean};
type DefaultPathsOptions = {maxRecursionDepth: 10};
type SpecifiedOptions = {};
type Result = ApplyDefaultOptions<PathsOptions, DefaultPathsOptions, SpecifiedOptions>;
// ~~~~~~~~~~~~~~~~~~~
// Property 'leavesOnly' is missing in type 'DefaultPathsOptions' but required in type '{ maxRecursionDepth: number; leavesOnly: boolean; }'.
```
@example
```
// Complains if an option's default type does not conform to the expected type
type PathsOptions = {maxRecursionDepth?: number; leavesOnly?: boolean};
type DefaultPathsOptions = {maxRecursionDepth: 10; leavesOnly: 'no'};
type SpecifiedOptions = {};
type Result = ApplyDefaultOptions<PathsOptions, DefaultPathsOptions, SpecifiedOptions>;
// ~~~~~~~~~~~~~~~~~~~
// Types of property 'leavesOnly' are incompatible. Type 'string' is not assignable to type 'boolean'.
```
@example
```
// Complains if an option's specified type does not conform to the expected type
type PathsOptions = {maxRecursionDepth?: number; leavesOnly?: boolean};
type DefaultPathsOptions = {maxRecursionDepth: 10; leavesOnly: false};
type SpecifiedOptions = {leavesOnly: 'yes'};
type Result = ApplyDefaultOptions<PathsOptions, DefaultPathsOptions, SpecifiedOptions>;
// ~~~~~~~~~~~~~~~~
// Types of property 'leavesOnly' are incompatible. Type 'string' is not assignable to type 'boolean'.
```
@group type-fest
*/
export type ApplyDefaultOptions<
Options extends object,
Defaults extends Simplify<Omit<Required<Options>, RequiredKeysOf<Options>> & Partial<Record<RequiredKeysOf<Options>, never>>>,
SpecifiedOptions extends Options
> = IfAny<SpecifiedOptions, Defaults, IfNever<SpecifiedOptions, Defaults, Simplify<Merge<Defaults, { [Key in keyof SpecifiedOptions as Key extends OptionalKeysOf<Options> ? Extract<SpecifiedOptions[Key], undefined> extends never ? Key : never : Key] : SpecifiedOptions[Key] }> & Required<Options>>>>;