Skip to content
This repository was archived by the owner on Jul 23, 2021. It is now read-only.

Type safe getIn/setIn (TypeScript) #64

Open
Methuselah96 opened this issue Oct 17, 2020 · 1 comment
Open

Type safe getIn/setIn (TypeScript) #64

Methuselah96 opened this issue Oct 17, 2020 · 1 comment

Comments

@Methuselah96
Copy link

From @dinony on Tue, 19 Dec 2017 11:15:48 GMT

I'm using v4.0.0-rc.9 with TypeScript a lot.
I wonder if it would be possible to preserve type information when using getIn or setIn.

const r = nested.getIn(['some', 'long', 'path']) // r: any :'(

Any TS pros out there, who could help with the TS declarations?

Copied from original issue: immutable-js#1462

@danielbady
Copy link

Hello I actually wanted to have getIn and setIn to preserve types as well and made a module.

declare module 'immutable' {
// This part takes an immutable value and return value converted to its corresponded non-immutable value.
// This is done so we can use Typescript with Immutablejs as they do not like to play with each other.
  type ExtractGeneric<T> =
  // Changes Map to object
    T extends Map<string, infer X>
      // Changes X if it is immutable
      ? X extends Map<string | number, infer _Y> | List<infer _Y> | Iterable<number | string, infer _Y>
      //  to { key: X } object
      ? { [key in string]: X }
      // Returns X if not immutable
      : X extends (string | Map<infer K, infer Y>)[][]
        ? { [key in string]: Map<K, Y> }
      : X
      // Changes List to Array
    : T extends Map<number, infer X>
    // Changes X if it is immutable
    ? X extends Map<string | number, infer _Y> | List<infer _Y> | Iterable<number | string, infer _Y>
      //  to { key: X } object
      ? { [key in number]: X }
      // Returns X if not immutable
      : X extends (string | Map<infer K, infer Y>)[][]
        ? { [key in number]: Map<K, Y> }
        : X
      // Changes List to Array
      : T extends List<infer X>
        ? Array<X>
        // Changes Iterable to { key: X } object
        : T extends Iterable<number | string, infer X>
          ? { [key in string]: X }
          : T extends (string | Map<string, infer X>)[][]
            ? { [key in string]: Map<string, X> }
            // default return
            : T;

  type GetReturnValue<K, V,
    K1 extends keyof ExtractGeneric<Map<K, V>>,
    K2 extends keyof ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>,
    K3 extends keyof ExtractGeneric<ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]>,
    K4 extends keyof ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]>[K3]>,
    K5 extends keyof ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]>[K3]>[K4]>,
    K6 extends keyof ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]>[K3]>[K4]>[K5]>> =
    [K2] extends [never]
      ? ExtractGeneric<Map<K, V>>[K1]
      : [K3] extends [never]
      ? ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]
      : [K4] extends [never]
        ? ExtractGeneric<ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]>[K3]
        : [K5] extends [never]
          ? ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]>[K3]>[K4]
          : [K6] extends [never]
            ? ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]>[K3]>[K4]>[K5]
            : ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]>[K3]>[K4]>[K5]>[K6]

  export function Map<K = string, V = any>(obj?: { [key in keyof V]: V[key] }): Map<K, V>;

  export interface Map<K, V> {
    get<K1 extends keyof ExtractGeneric<Map<K, V>>>(key: K1, notSetValue: ExtractGeneric<Map<K, V>>[K1]): ExtractGeneric<Map<K, V>>[K1];

    get<K1 extends keyof ExtractGeneric<Map<K, V>>>(key: K1): ExtractGeneric<Map<K, V>>[K1] | undefined;

    has(key: keyof V): boolean

    update<K1 extends keyof ExtractGeneric<V>>(updater: (value: ExtractGeneric<V>[K1]) => void): this;

    update<K1 extends keyof ExtractGeneric<V>>(key: K1, updater: (value: ExtractGeneric<V>[K1]) => void): this;

    update<K1 extends keyof ExtractGeneric<V>>(key: K1, notSetValue: ExtractGeneric<V>[K1], updater: (value: ExtractGeneric<V>[K1]) => void): this;

    getIn<K1 extends keyof ExtractGeneric<Map<K, V>>,
      K2 extends keyof ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]> = never,
      K3 extends keyof ExtractGeneric<ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]> = never,
      K4 extends keyof ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]>[K3]> = never,
      K5 extends keyof ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]>[K3]>[K4]> = never,
      K6 extends keyof ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]>[K3]>[K4]>[K5]> = never>(
      path: [K1, K2?, K3?, K4?, K5?, K6?],
      notSetValue?: GetReturnValue<K, V, K1, K2, K3, K4, K5, K6>
    ): GetReturnValue<K, V, K1, K2, K3, K4, K5, K6>;

    setIn<K1 extends keyof ExtractGeneric<Map<K, V>>,
      K2 extends keyof ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]> = never,
      K3 extends keyof ExtractGeneric<ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]> = never,
      K4 extends keyof ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]>[K3]> = never,
      K5 extends keyof ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]>[K3]>[K4]> = never,
      K6 extends keyof ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]>[K3]>[K4]>[K5]> = never>(
      path: [K1, K2?, K3?, K4?, K5?, K6?],
      value: GetReturnValue<K, V, K1, K2, K3, K4, K5, K6>
    ): this;

    updateIn<K1 extends keyof ExtractGeneric<Map<K, V>>,
      K2 extends keyof ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]> = never,
      K3 extends keyof ExtractGeneric<ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]> = never,
      K4 extends keyof ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]>[K3]> = never,
      K5 extends keyof ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]>[K3]>[K4]> = never,
      K6 extends keyof ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]>[K3]>[K4]>[K5]> = never>(
      path: [K1?, K2?, K3?, K4?, K5?, K6?],
      notSetValue?: GetReturnValue<K, V, K1, K2, K3, K4, K5, K6>,
      updater?: (value: GetReturnValue<K, V, K1, K2, K3, K4, K5, K6>) => GetReturnValue<K, V, K1, K2, K3, K4, K5, K6>,
    ): this

    updateIn<K1 extends keyof ExtractGeneric<Map<K, V>>,
      K2 extends keyof ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]> = never,
      K3 extends keyof ExtractGeneric<ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]> = never,
      K4 extends keyof ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]>[K3]> = never,
      K5 extends keyof ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]>[K3]>[K4]> = never,
      K6 extends keyof ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]>[K3]>[K4]>[K5]> = never>(
      path: [K1, K2?, K3?, K4?, K5?, K6?],
      updater?: (value: GetReturnValue<K, V, K1, K2, K3, K4, K5, K6>) => GetReturnValue<K, V, K1, K2, K3, K4, K5, K6>,
    ): this

    merge<K1 extends Partial<ExtractGeneric<Map<K, V>>>>(...collections: Array<K1>): this;

    mergeIn<K1 extends keyof ExtractGeneric<Map<K, V>>,
      K2 extends keyof ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]> = never,
      K3 extends keyof ExtractGeneric<ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]> = never,
      K4 extends keyof ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]>[K3]> = never,
      K5 extends keyof ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]>[K3]>[K4]> = never,
      K6 extends keyof ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]>[K3]>[K4]>[K5]> = never>(
      path: [K1, K2?, K3?, K4?, K5?, K6?],
      collections: Partial<ExtractGeneric<GetReturnValue<K, V, K1, K2, K3, K4, K5, K6>> | GetReturnValue<K, V, K1, K2, K3, K4, K5, K6>>,
    ): this;

    mergeDeep<K1>(...collections: Array<K1 | { [key: string]: V[keyof V] }>): this;

    mergeDeepIn<K1 extends keyof ExtractGeneric<Map<K, V>>,
      K2 extends keyof ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]> = never,
      K3 extends keyof ExtractGeneric<ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]> = never,
      K4 extends keyof ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]>[K3]> = never,
      K5 extends keyof ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]>[K3]>[K4]> = never,
      K6 extends keyof ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]>[K3]>[K4]>[K5]> = never>(
      path: [K1, K2?, K3?, K4?, K5?, K6?],
      collections: Partial<ExtractGeneric<GetReturnValue<K, V, K1, K2, K3, K4, K5, K6>> | GetReturnValue<K, V, K1, K2, K3, K4, K5, K6>>,
    ): this;

    mapEntries(
      mapper: (entry: [string | number, V], index: string | number, iter: V) => [number | string, V],
    ): this;

    filter<K1 extends keyof ExtractGeneric<Map<K, V>>>(
      predicate: (value?: ExtractGeneric<Map<K, V>>[K1], key?: string, iter?: this) => boolean,
    ): this;

    filterNot<K1 extends keyof ExtractGeneric<Map<K, V>>>(
      predicate: (value?: ExtractGeneric<Map<K, V>>[K1], key?: string, iter?: this) => boolean,
    ): this;

    set<K1 extends keyof ExtractGeneric<Map<K, V>>>(key: K1, value: null | undefined | ExtractGeneric<Map<K, V>>[K1]): this;

    forEach(
      sideEffect: (value: V, key: number | string, iter: this) => void,
    ): number;

    keySeq(): Seq.Indexed<keyof this>;

    maxBy<C, K1 extends keyof ExtractGeneric<Map<K, V>>>(
      comparatorValueMapper: (value?: V, key?: K1, iter?: Iterable<K1, V>) => C,
      comparator?: (valueA: C, valueB: C) => number
    ): ExtractGeneric<Map<K, V>> extends { [key: string]: infer X } ? X : V;

    minBy<C, K1 extends keyof ExtractGeneric<Map<K, V>>>(
      comparatorValueMapper: (value?: V, key?: K1, iter?: Iterable<K1, V>) => C,
      comparator?: (valueA: C, valueB: C) => number
    ): ExtractGeneric<Map<K, V>> extends { [key: string]: infer X } ? X : V;

    deleteIn<K1 extends keyof ExtractGeneric<Map<K, V>>,
      K2 extends keyof ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]> = never,
      K3 extends keyof ExtractGeneric<ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]> = never,
      K4 extends keyof ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]>[K3]> = never,
      K5 extends keyof ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]>[K3]>[K4]> = never,
      K6 extends keyof ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]>[K3]>[K4]>[K5]> = never>(
      path: [K1, K2?, K3?, K4?, K5?, K6?],
    ): this;

    removeIn<K1 extends keyof ExtractGeneric<Map<K, V>>,
      K2 extends keyof ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]> = never,
      K3 extends keyof ExtractGeneric<ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]> = never,
      K4 extends keyof ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]>[K3]> = never,
      K5 extends keyof ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]>[K3]>[K4]> = never,
      K6 extends keyof ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<ExtractGeneric<Map<K, V>>[K1]>[K2]>[K3]>[K4]>[K5]> = never>(
      path: [K1, K2?, K3?, K4?, K5?, K6?],
    ): this;
  }

  module Record {
    export interface Class<T> {
      new<T> (): Map<string, T>;
      new<T> (values: {[key in keyof T]: T[key]}): Map<string, T>;
      new<T> (values: Iterable<string, T>): Map<string, T>; // deprecated

      (): Map<string, T>;
      (values: {[key in keyof T]: T[key]}): Map<string, T>;
      (values: Iterable<string, T>): Map<string, T>; // deprecated
    }
  }

  export function Record<T>(
    defaultValues: {[key in keyof T]: T[key]}, name?: string
  ): Record.Class<T>;
}

with this it should actually work but it is finicky and may show “Type instantiation is excessively deep and possibly infinite” since it has more than 50 cycles.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

2 participants