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 = false * ``` */ export type IsTuple> = 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 = Extract; /** * Type to convert a type to a {@link Key}. * @typeParam T - type which may be converted to a {@link Key} */ export type ToKey = T extends ArrayKey ? `${T}` : AsKey; /** * 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 = Extract; /** * 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 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 = 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 `${infer K}.${infer R}` ? SplitPathStringImpl> : AppendNonBlankKey; /** * 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 = SplitPathStringImpl; /** * 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 [infer K, ...infer R] ? JoinPathTupleImpl, `${PS}.${AsKey}`> : 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 [ infer K, ...infer R ] ? JoinPathTupleImpl, AsKey> : 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 = { [K in keyof T as ToKey]: 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 * TryAccess = undefined * ``` */ type TryAccess = 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 * TryAccessArray = undefined * ``` */ type TryAccessArray, K extends Key> = K extends `${ArrayKey}` ? T[number] : TryAccess; /** * 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 * ``` */ export type EvaluateKey = T extends ReadonlyArray ? IsTuple extends true ? TryAccess : TryAccessArray : TryAccess, 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 * EvaluatePath = undefined * ``` */ export type EvaluatePath = PT extends [ infer K, ...infer R ] ? EvaluatePath>, AsPathTuple> : 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> = Exclude; /** * Type which extracts all numeric keys from an object. * @typeParam T - type * @example * ``` * NumericObjectKeys<{0: string, '1': string, foo: string}> = '0' | '1' * ``` */ type NumericObjectKeys = ToKey>; /** * 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}` * NumericKeys<[string, number]> = '0' | '1' * NumericKeys<{0: string, '1': string} | [number] | number[]> = '0' * ``` */ export type NumericKeys = UnionToIntersection ? IsTuple extends true ? [TupleKeys] : [ToKey] : [NumericObjectKeys]>[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 = Exclude, `${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 = `${number}` * ``` */ export type CheckKeyConstraint = K extends any ? EvaluateKey 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 = IsNever>> 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] extends [Traversable] ? ContainsIndexable extends true ? NumericKeys : ObjectKeys : 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 = `${number}` * Keys<{0: string, '1': string} | [number] | number[]> = '0' * ``` */ export type Keys = IsAny extends true ? Key : IsNever extends true ? Key : IsNever> extends true ? never : CheckKeyConstraint>, 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 = IsNever>>; /** * 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 = PT extends [infer K, ...infer R] ? HasKey> extends true ? ValidPathPrefixImpl>, AsPathTuple, 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 = ValidPathPrefixImpl; /** * 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 = ValidPathPrefix extends PT ? true : false; export {}; //# sourceMappingURL=common.d.ts.map