UNPKG

react-hook-form

Version:

Performant, flexible and extensible forms library for React Hooks

316 lines • 11.8 kB
import { IsAny, IsNever } from '../utils'; /** * Type alias to `string` which describes a lodash-like path through an object. * E.g. `'foo.bar.0.baz'` */ export type PathString = string; /** * Type which can be traversed through with a {@link PathString}. * I.e. objects, arrays, and tuples */ export type Traversable = object; /** * Type to query whether an array type T is a tuple type. * @typeParam T - type which may be an array or tuple * @example * ``` * IsTuple<[number]> = true * IsTuple<number[]> = false * ``` */ export type IsTuple<T extends ReadonlyArray<any>> = number extends T['length'] ? false : true; /** * Type which can be used to index an array or tuple type. */ export type ArrayKey = number; /** * Type which can be used to index an object. */ export type Key = string; /** * Type to assert that a type is a {@link Key}. * @typeParam T - type which may be a {@link Key} */ export type AsKey<T> = Extract<T, Key>; /** * Type to convert a type to a {@link Key}. * @typeParam T - type which may be converted to a {@link Key} */ export type ToKey<T> = T extends ArrayKey ? `${T}` : AsKey<T>; /** * Type which describes a path through an object * as a list of individual {@link Key}s. */ export type PathTuple = Key[]; /** * Type to assert that a type is a {@link PathTuple}. * @typeParam T - type which may be a {@link PathTuple} */ export type AsPathTuple<T> = Extract<T, PathTuple>; /** * Type to intersect a union type. * See https://fettblog.eu/typescript-union-to-intersection/ * @typeParam U - union * @example * ``` * UnionToIntersection<{ foo: string } | { bar: number }> * = { foo: string; bar: number } * ``` */ export type UnionToIntersection<U> = (U extends any ? (_: U) => any : never) extends (_: infer I) => any ? I : never; /** * Type which appends a {@link Key} to the {@link PathTuple} only if it is not * blank, i.e. not the empty string. * @typeParam PT - path * @typeParam K - key * @example * ``` * AppendNonBlankKey<['foo'], 'bar'> = ['foo', 'bar'] * AppendNonBlankKey<['foo'], ''> = ['foo'] * ``` */ type AppendNonBlankKey<PT extends PathTuple, K extends Key> = K extends '' ? PT : [...PT, K]; /** * Type to implement {@link SplitPathString} tail recursively. * @typeParam PS - remaining {@link PathString} which should be split into its * individual {@link Key}s * @typeParam PT - accumulator of the {@link Key}s which have been split from * the original {@link PathString} already */ type SplitPathStringImpl<PS extends PathString, PT extends PathTuple> = PS extends `${infer K}.${infer R}` ? SplitPathStringImpl<R, AppendNonBlankKey<PT, K>> : AppendNonBlankKey<PT, PS>; /** * Type to split a {@link PathString} into a {@link PathTuple}. * The individual {@link Key}s may be empty strings. * @typeParam PS - {@link PathString} which should be split into its * individual {@link Key}s * @example * ``` * SplitPathString<'foo'> = ['foo'] * SplitPathString<'foo.bar.0.baz'> = ['foo', 'bar', '0', 'baz'] * SplitPathString<'.'> = [] * ``` */ export type SplitPathString<PS extends PathString> = SplitPathStringImpl<PS, [ ]>; /** * Type to implement {@link JoinPathTuple} tail-recursively. * @typeParam PT - remaining {@link Key}s which needs to be joined * @typeParam PS - accumulator of the already joined {@link Key}s */ type JoinPathTupleImpl<PT extends PathTuple, PS extends PathString> = PT extends [infer K, ...infer R] ? JoinPathTupleImpl<AsPathTuple<R>, `${PS}.${AsKey<K>}`> : PS; /** * Type to join a {@link PathTuple} to a {@link PathString}. * @typeParam PT - {@link PathTuple} which should be joined. * @example * ``` * JoinPathTuple<['foo']> = 'foo' * JoinPathTuple<['foo', 'bar', '0', 'baz']> = 'foo.bar.0.baz' * JoinPathTuple<[]> = never * ``` */ export type JoinPathTuple<PT extends PathTuple> = PT extends [ infer K, ...infer R ] ? JoinPathTupleImpl<AsPathTuple<R>, AsKey<K>> : never; /** * Type which converts all keys of an object to {@link Key}s. * @typeParam T - object type * @example * ``` * MapKeys<{0: string}> = {'0': string} * ``` */ type MapKeys<T> = { [K in keyof T as ToKey<K>]: T[K]; }; /** * Type to access a type by a key. * - Returns undefined if it can't be indexed by that key. * - Returns null if the type is null. * - Returns undefined if the type is not traversable. * @typeParam T - type which is indexed by the key * @typeParam K - key into the type * ``` * TryAccess<{foo: string}, 'foo'> = string * TryAccess<{foo: string}, 'bar'> = undefined * TryAccess<null, 'foo'> = null * TryAccess<string, 'foo'> = undefined * ``` */ type TryAccess<T, K> = K extends keyof T ? T[K] : T extends null ? null : undefined; /** * Type to access an array type by a key. * Returns undefined if the key is non-numeric. * @typeParam T - type which is indexed by the key * @typeParam K - key into the type * ``` * TryAccessArray<string[], '0'> = string * TryAccessArray<string[], 'foo'> = undefined * ``` */ type TryAccessArray<T extends ReadonlyArray<any>, K extends Key> = K extends `${ArrayKey}` ? T[number] : TryAccess<T, K>; /** * Type to evaluate the type which the given key points to. * @typeParam T - type which is indexed by the key * @typeParam K - key into the type * @example * ``` * EvaluateKey<{foo: string}, 'foo'> = string * EvaluateKey<[number, string], '1'> = string * EvaluateKey<string[], '1'> = string * ``` */ export type EvaluateKey<T, K extends Key> = T extends ReadonlyArray<any> ? IsTuple<T> extends true ? TryAccess<T, K> : TryAccessArray<T, K> : TryAccess<MapKeys<T>, K>; /** * Type to evaluate the type which the given path points to. * @typeParam T - deeply nested type which is indexed by the path * @typeParam PT - path into the deeply nested type * @example * ``` * EvaluatePath<{foo: {bar: string}}, ['foo', 'bar']> = string * EvaluatePath<[number, string], ['1']> = string * EvaluatePath<number, []> = number * EvaluatePath<number, ['foo']> = undefined * ``` */ export type EvaluatePath<T, PT extends PathTuple> = PT extends [ infer K, ...infer R ] ? EvaluatePath<EvaluateKey<T, AsKey<K>>, AsPathTuple<R>> : T; /** * Type which given a tuple type returns its own keys, i.e. only its indices. * @typeParam T - tuple type * @example * ``` * TupleKeys<[number, string]> = '0' | '1' * ``` */ export type TupleKeys<T extends ReadonlyArray<any>> = Exclude<keyof T, keyof any[]>; /** * Type which extracts all numeric keys from an object. * @typeParam T - type * @example * ``` * NumericObjectKeys<{0: string, '1': string, foo: string}> = '0' | '1' * ``` */ type NumericObjectKeys<T extends Traversable> = ToKey<Extract<keyof T, ArrayKey | `${ArrayKey}`>>; /** * Type which extracts all numeric keys from an object, tuple, or array. * If a union is passed, it evaluates to the overlapping numeric keys. * @typeParam T - type * @example * ``` * NumericKeys<{0: string, '1': string, foo: string}> = '0' | '1' * NumericKeys<number[]> = `${number}` * NumericKeys<[string, number]> = '0' | '1' * NumericKeys<{0: string, '1': string} | [number] | number[]> = '0' * ``` */ export type NumericKeys<T extends Traversable> = UnionToIntersection<T extends ReadonlyArray<any> ? IsTuple<T> extends true ? [TupleKeys<T>] : [ToKey<ArrayKey>] : [NumericObjectKeys<T>]>[never]; /** * Type which extracts all keys from an object. * If a union is passed, it evaluates to the overlapping keys. * @typeParam T - object type * @example * ``` * ObjectKeys<{foo: string, bar: string}, string> = 'foo' | 'bar' * ObjectKeys<{foo: string, bar: number}, string> = 'foo' * ``` */ export type ObjectKeys<T extends Traversable> = Exclude<ToKey<keyof T>, `${string}.${string}` | ''>; /** * Type to check whether a type's property matches the constraint type * and return its key. Converts the key to a {@link Key}. * @typeParam T - type whose property should be checked * @typeParam K - key of the property * @typeParam U - constraint type * @example * ``` * CheckKeyConstraint<{foo: string}, 'foo', string> = 'foo' * CheckKeyConstraint<{foo: string}, 'foo', number> = never * CheckKeyConstraint<string[], number, string> = `${number}` * ``` */ export type CheckKeyConstraint<T, K extends Key, U> = K extends any ? EvaluateKey<T, K> extends U ? K : never : never; /** * Type which evaluates to true when the type is an array or tuple or is a union * which contains an array or tuple. * @typeParam T - type * @example * ``` * ContainsIndexable<{foo: string}> = false * ContainsIndexable<{foo: string} | number[]> = true * ``` */ export type ContainsIndexable<T> = IsNever<Extract<T, ReadonlyArray<any>>> extends true ? false : true; /** * Type to implement {@link Keys} for non-nullable values. * @typeParam T - non-nullable type whose property should be checked */ type KeysImpl<T> = [T] extends [Traversable] ? ContainsIndexable<T> extends true ? NumericKeys<T> : ObjectKeys<T> : never; /** * Type to find all properties of a type that match the constraint type * and return their keys. * If a union is passed, it evaluates to the overlapping keys. * @typeParam T - type whose property should be checked * @typeParam U - constraint type * @example * ``` * Keys<{foo: string, bar: string}, string> = 'foo' | 'bar' * Keys<{foo?: string, bar?: string}> = 'foo' | 'bar' * Keys<{foo: string, bar: number}, string> = 'foo' * Keys<[string, number], string> = '0' * Keys<string[], string> = `${number}` * Keys<{0: string, '1': string} | [number] | number[]> = '0' * ``` */ export type Keys<T, U = unknown> = IsAny<T> extends true ? Key : IsNever<T> extends true ? Key : IsNever<NonNullable<T>> extends true ? never : CheckKeyConstraint<T, KeysImpl<NonNullable<T>>, U>; /** * Type to check whether a {@link Key} is present in a type. * If a union of {@link Key}s is passed, all {@link Key}s have to be present * in the type. * @typeParam T - type which is introspected * @typeParam K - key * @example * ``` * HasKey<{foo: string}, 'foo'> = true * HasKey<{foo: string}, 'bar'> = false * HasKey<{foo: string}, 'foo' | 'bar'> = false * ``` */ export type HasKey<T, K extends Key> = IsNever<Exclude<K, Keys<T>>>; /** * Type to implement {@link ValidPathPrefix} tail recursively. * @typeParam T - type which the path should be checked against * @typeParam PT - path which should exist within the given type * @typeParam VPT - accumulates the prefix of {@link Key}s which have been * confirmed to exist already */ type ValidPathPrefixImpl<T, PT extends PathTuple, VPT extends PathTuple> = PT extends [infer K, ...infer R] ? HasKey<T, AsKey<K>> extends true ? ValidPathPrefixImpl<EvaluateKey<T, AsKey<K>>, AsPathTuple<R>, AsPathTuple<[...VPT, K]>> : VPT : VPT; /** * Type to find the longest path prefix which is still valid, * i.e. exists within the given type. * @typeParam T - type which the path should be checked against * @typeParam PT - path which should exist within the given type * @example * ``` * ValidPathPrefix<{foo: {bar: string}}, ['foo', 'bar']> = ['foo', 'bar'] * ValidPathPrefix<{foo: {bar: string}}, ['foo', 'ba']> = ['foo'] * ``` */ export type ValidPathPrefix<T, PT extends PathTuple> = ValidPathPrefixImpl<T, PT, [ ]>; /** * Type to check whether a path through a type exists. * @typeParam T - type which the path should be checked against * @typeParam PT - path which should exist within the given type * @example * ``` * HasPath<{foo: {bar: string}}, ['foo', 'bar']> = true * HasPath<{foo: {bar: string}}, ['foo', 'ba']> = false * ``` */ export type HasPath<T, PT extends PathTuple> = ValidPathPrefix<T, PT> extends PT ? true : false; export {}; //# sourceMappingURL=common.d.ts.map