remirror/remirror

View on GitHub
packages/remirror__types/src/types.ts

Summary

Maintainability
A
0 mins
Test Coverage
F
0%
/* Utility Types */

import type { ConditionalExcept, ConditionalPick } from 'type-fest';

/**
 * An alternative to keyof that only extracts the string keys.
 */
export type StringKey<Type> = Extract<keyof Type, string>;

/**
 * Extract the values of an object as a union type.
 *
 * @remarks
 *
 * ```ts
 * const myRecord = { A: 'a', B: 'b', C: 'c' } as const;
 *
 * type MyRecord = Value<typeof myRecord>; // 'a' | 'b' | 'c'
 * ```
 */
export type Value<Type> = Type[keyof Type];

/**
 * Makes a type nullable or undefined.
 */
export type Nullable<Type> = Type | null | undefined;

/**
 * A shorthand for creating and intersection of two object types.
 */
export type And<Type extends Shape, Other extends Shape> = Type & Other;

/**
 * When the type is never use a default type instead.
 *
 * TODO why doesn't this work
 */
export type UseDefault<Type, Default> = Type extends never ? Default : Type;

/**
 * Extract the values of a tuple as a union type.
 *
 * @remarks
 *
 * ```ts
 * const myTuple = ['a', 'b', 'c'] as const;
 *
 * type MyTuple = TupleValue<typeof myTuple>; // 'a' | 'b' | 'c'
 * ```
 */
export type TupleValue<Tuple extends readonly unknown[]> = Tuple[number];

/**
 * Creates a predicate type.
 */
export type Predicate<Type> = (value: unknown) => value is Type;

declare const _brand: unique symbol;
declare const _flavor: unique symbol;

/**
 * Used by Brand to mark a type in a readable way.
 */
interface Branding<Name> {
  readonly [_brand]: Name;
}

/**
 * Used by `Flavor` to mark a type in a readable way.
 */
export interface Flavoring<Name> {
  readonly [_flavor]?: Name;
}

/**
 * Remove the flavoring from a type.
 */
export type RemoveFlavoring<Type, Name> = Type extends Flavor<infer T, Name> ? T : Type;

/**
 * Create a "flavored" version of a type. TypeScript will disallow mixing
 * flavors, but will allow unflavored values of that type to be passed in where
 * a flavored version is expected. This is a less restrictive form of branding.
 */
export type Flavor<Type, Name> = Type & Flavoring<Name>;

/**
 * Create a "branded" version of a type. TypeScript won't allow implicit
 * conversion to this type
 */
export type Brand<Type, B> = Type & Branding<B>;

/**
 * An object with string keys and values of type `any`
 */
export interface Shape {
  [key: string]: any;
}

/**
 * An object with string keys and values of type `unknown`
 */
export type UnknownShape<Type = unknown> = Record<string, Type>;

/**
 * An alternative to usage of `{}` as a type.
 */
export type EmptyShape = Record<never, never>;

/**
 * Concisely and cleanly define an arbitrary function.
 *
 * @remarks
 * Taken from `simplytyped` Useful when designing many api's that don't care
 * what function they take in, they just need to know what it returns.
 */
export type AnyFunction<Type = any> = (...args: any[]) => Type;

/**
 * Matches any constructor type.
 */
export type AnyConstructor<Type = any> = new (...args: any[]) => Type;

/**
 * Create a type for an array (as a tuple) which has at least the provided
 * `Length`.
 *
 * This can be  useful when `noUncheckedIndexedAccess` is set to true in the
 * compiler options. Annotate types when you are sure the provided index will
 * always be available.
 *
 * ```ts
 * import { MinArray } from '@remirror/core-types';
 *
 * MinArray<string, 2>; // => [string, string, ...string[]];
 * ```
 */
export type MinArray<Type, Length extends number> = Length extends Length
  ? number extends Length
    ? Type[]
    : _MinArray<Type, Length, []>
  : never;
type _MinArray<
  Type,
  Length extends number,
  Accumulated extends unknown[],
> = Accumulated['length'] extends Length
  ? [...Accumulated, ...Type[]]
  : _MinArray<Type, Length, [Type, ...Accumulated]>;

/**
 * An array which must include the first item.
 */
export type Array1<Type> = MinArray<Type, 1>;

/**
 * An array which must include the first 2 items.
 */
export type Array2<Type> = MinArray<Type, 2>;

/**
 * An array which must include the first 3 items.
 */
export type Array3<Type> = MinArray<Type, 3>;

/**
 * Allow a type of a list of types.
 */
export type Listable<Type> = Type | Type[];

/**
 * When a type is really deep and has retained an unnecessary amount of type
 * information, this flattens it to a single array/object/value.
 *
 * TODO not using it right now as it's breaking with globally available types
 * via namespace.
 */
export type Simplify<T> = T extends object | any[] ? { [K in keyof T]: T[K] } : T;

/**
 * Returns tuple types that include every string in union TupleUnion<keyof {bar:
 * string; leet: number }>; ["bar", "leet"] | ["leet", "bar"];
 *
 * Taken from ❤️
 * https://github.com/microsoft/TypeScript/issues/13298#issuecomment-692864087
 *
 * Problem it is recursive and has quickly eats into the maximum depth.
 */
// export type TupleUnion<U extends string, R extends string[] = []> = {
//   [S in U]: Exclude<U, S> extends never ? [...R, S] : TupleUnion<Exclude<U, S>, [...R, S]>;
// }[U] &
//   string[];

export type TupleUnion<T> = (
  (T extends any ? (t: T) => T : never) extends infer U
    ? (U extends any ? (u: U) => any : never) extends (v: infer V) => any
      ? V
      : never
    : never
) extends (_: any) => infer W
  ? [...TupleUnion<Exclude<T, W>>, W]
  : [];

/**
 * Extract the valid index union from a provided tuple.
 *
 * ```ts
 * import { IndexUnionFromTuple } from '@remirror/core-types';
 *
 * const tuple = ['a', 'b', 'c'];
 * type Index = IndexUnionFromTuple<typeof tuple> => 0 | 1 | 2
 * ```
 */
export type IndexUnionFromTuple<Tuple extends readonly unknown[]> = Tuple extends Tuple
  ? number extends Tuple['length']
    ? number
    : _IndexUnionFromTuple<[], Tuple['length']>
  : never;
type _IndexUnionFromTuple<
  Tuple extends readonly unknown[],
  Length extends number,
> = Tuple['length'] extends Length
  ? Tuple[number]
  : _IndexUnionFromTuple<[...Tuple, Tuple['length']], Length>;

/**
 * Create a tuple of `Size` from the provided `Type`.
 */
export type TupleOf<Type, Size extends number> = Size extends Size
  ? number extends Size
    ? Type[]
    : _TupleOf<Type, Size, []>
  : never;
type _TupleOf<Type, Size extends number, Tuple extends unknown[]> = Tuple['length'] extends Size
  ? Tuple
  : _TupleOf<Type, Size, [Type, ...Tuple]>;

/**
 * Make the whole interface partial except for some specified keys which will be
 * made required.
 */
export type PartialWithRequiredKeys<Type extends object, Keys extends keyof Type> = Partial<
  Pick<Type, Exclude<keyof Type, Keys>>
> &
  Required<Pick<Type, Keys>>;

/**
 * Remove all readonly modifiers from the provided type.
 */
export type Writeable<Type> = { -readonly [Key in keyof Type]: Type[Key] };

/**
 * Makes specified keys of an interface optional while the rest stay the same.
 */
export type MakeOptional<Type extends object, Keys extends keyof Type> = Omit<Type, Keys> & {
  [Key in Keys]+?: Type[Key];
};

/**
 * Makes specified keys of an interface optional while the rest stay the same.
 */
export type MakeUndefined<Type extends object, Keys extends keyof Type> = Omit<Type, Keys> & {
  [Key in Keys]: Type[Key] | undefined;
};

/**
 * Makes specified keys of an interface nullable while the rest stay the same.
 */
export type MakeNullable<Type extends object, Keys extends keyof Type> = Omit<Type, Keys> & {
  [Key in Keys]: Type[Key] | null;
};

/**
 * Makes specified keys of an interface Required while the rest remain
 * unchanged.
 */
export type MakeRequired<Type extends object, Keys extends keyof Type> = Omit<Type, Keys> & {
  [Key in Keys]-?: Type[Key];
};

/**
 * Makes specified keys of an interface readonly.
 */
export type MakeReadonly<Type extends object, Keys extends keyof Type> = Omit<Type, Keys> & {
  +readonly [Key in Keys]: NonNullable<Type[Key]>;
};

/**
 * All the literal types
 */
export type Literal = string | number | boolean | undefined | null | void | object;

/**
 * A recursive partial type. Useful for object that will be merged with
 * defaults.
 */
export type DeepPartial<Type> = Type extends object
  ? { [K in keyof Type]?: DeepPartial<Type[K]> }
  : Type;

/**
 * Converts every nested type to a string.
 */
export type DeepString<Type> = Type extends object
  ? { [K in keyof Type]: DeepString<Type[K]> }
  : string;

/**
 * A JSON representation of a prosemirror Mark.
 */
export interface ObjectMark {
  type: string;
  attrs?: Record<string, Literal>;
}

/**
 * Defines coordinates returned by the [[`EditorView.coordsAtPos`]] function.
 */
export interface Coords {
  /**
   * Vertical distance from the top of the page viewport to the top side of the
   * described position (px).
   */
  top: number;

  /**
   * Horizontal distance from the left of the page viewport to the left side of
   * the described position (px).
   */
  left: number;

  /**
   * Vertical distance from the top of the page viewport to the bottom side of
   * the described position (px).
   */
  bottom: number;

  /**
   * Horizontal distance from the left of the page viewport to the right side of
   * the described position (px).
   */
  right: number;
}

/**
 * Used for attributes which can be added to prosemirror nodes and marks.
 */
export type ProsemirrorAttributes<Extra extends object = object> = Record<string, unknown> &
  Remirror.Attributes &
  Extra & {
    /**
     * The class is a preserved attribute name.
     */
    class?: string;
  };

/**
 * A method that can pull all the extraAttributes from the provided dom node.
 */

/**
 * Checks the type provided and if it has any properties which are required it
 * will return the `Then` type. When none of the properties are required it will
 * return the `Else` type.
 *
 * @remarks
 *
 * This is a reverse of the `IfNoRequiredProperties` type.
 */
export type IfHasRequiredProperties<Type extends object, Then, Else> = IfNoRequiredProperties<
  Type,
  Else,
  Then
>;

type NeverBrand = Brand<object, never>;

/**
 * A conditional check on the type. When there are no required keys it outputs
 * the `Then` type, otherwise it outputs the `Else` type.
 *
 * @remarks
 *
 * This is useful for dynamically setting the parameter list of a method call
 * depending on whether keys are required.
 */
export type IfNoRequiredProperties<
  Type extends object,
  Then,
  Else,
> = GetRequiredKeys<Type> extends NeverBrand ? Then : Else;

/**
 * Get all the keys for required properties on this type.
 */
export type GetRequiredKeys<Type extends Shape> = keyof ConditionalPick<
  KeepPartialProperties<Type>,
  NeverBrand
>;

/**
 * Keeps the partial properties of a type unchanged. Transforms the rest to
 * `never`.
 */
export type KeepPartialProperties<Type extends Shape> = {
  [Key in keyof Type]: Type[Key] extends undefined ? Type[Key] : NeverBrand;
};

/**
 * Pick the `partial` properties from the provided Type and make them all
 * required.
 */
export type PickPartial<Type extends Shape> = {
  [Key in keyof ConditionalExcept<KeepPartialProperties<Type>, NeverBrand>]-?: Type[Key];
};

/**
 * Like pick partial but all types can still specify undefined.
 */
export type UndefinedPickPartial<Type extends Shape> = {
  [Key in keyof PickPartial<Type>]: PickPartial<Type>[Key] | undefined;
};

/**
 * Only pick the `required` (non-`partial`) types from the given `Type`.
 */
export type PickRequired<Type extends Shape> = {
  [Key in keyof ConditionalPick<KeepPartialProperties<Type>, NeverBrand>]: Type[Key];
};

/**
 * Reverses the partial and required keys for the type provided. If it was a
 * required property it becomes a partial property and if it was a partial
 * property it becomes a required property.
 */
export type FlipPartialAndRequired<Type extends Shape> = PickPartial<Type> &
  Partial<PickRequired<Type>>;

/**
 * Reverses the partial and required keys for the type provided. If it was a
 * required property it becomes a partial property and if it was a partial
 * property it becomes a required property.
 */
export type UndefinedFlipPartialAndRequired<Type extends Shape> = UndefinedPickPartial<Type> &
  Partial<PickRequired<Type>>;

/**
 * Get the diff between two types. All identical keys are stripped away.
 *
 * @remarks
 *
 * ```ts
 * type Fun = Diff<{notFun: false, fun: true}, {notFun: true, wow: string}>;
 * // => { fun: true, wow: string }
 * ```
 */
export type Diff<A, B> = Omit<A, keyof B> & Omit<B, keyof A>;

/**
 * Conditional type which checks if the provided `Type` is and empty object (no
 * properties). If it is uses the `Then` type if not falls back to the `Else`
 * type.
 */
export type IfEmpty<Type extends object, Then, Else> = keyof Type extends never ? Then : Else;

/**
 * Condition that checks if the keys of the two objects match. If so, respond
 * with `Then` otherwise `Else`.
 */
export type IfMatches<A, B, Then, Else> = IfEmpty<Diff<A, B>, Then, Else>;

/**
 * Replace only the current keys with different types.
 */
export type StrictReplace<Type, Replacements extends Record<keyof Type, unknown>> = Omit<
  Type,
  keyof Replacements
> &
  Replacements;

/**
 * Replace and extend any object keys.
 */
export type Replace<Type, Replacements extends Shape> = Omit<Type, keyof Replacements> &
  Replacements;

export type NonNullableShape<Type extends object> = {
  [Key in keyof Type]: NonNullable<Type[Key]>;
};

/**
 * Conditionally pick keys which are functions and have the requested return
 * type.
 */
export type ConditionalReturnKeys<Base, Return> = NonNullable<
  // Wrap in `NonNullable` to strip away the `undefined` type from the produced
  // union.
  {
    // Map through all the keys of the given base type.
    [Key in keyof Base]: Base[Key] extends AnyFunction<infer R> // Pick only keys with types extending the given `Return` type.
      ? // Check whether the inferred `R` type extends the requested `Return` type.
        R extends Return
        ? // The check passes therefor keep the key
          Key
        : // Discard this key since it the return type does not match.
          never
      : // Discard this key since it is not a function.
        never;

    // Convert the produced object into a union type of the keys which passed
    // the conditional test.
  }[keyof Base]
>;

/**
 * Pick the properties from an object that are methods with the requested
 * `Return` type.
 */
export type ConditionalReturnPick<Base, Return> = Pick<Base, ConditionalReturnKeys<Base, Return>>;

/**
 Matches any valid JSON primitive value.
 */
export type JsonPrimitive = string | number | boolean | null;

declare global {
  namespace Remirror {
    /**
     * Define globally available extra node attributes here.
     */
    interface Attributes {}
  }
}