KiaraGrouwstra / typical

playground for type-level primitives in TypeScript
MIT License
173 stars 5 forks source link

StrictEqual #24

Open SimonMeskens opened 6 years ago

SimonMeskens commented 6 years ago

I need a type that is able to distinguish Promise<any> from Promise<number>.

This is as far as I've gotten:

type ObjectEqual<T, U> = And<
    | StrictEqual<T, U>
    | (T extends ArrayLike<infer TIndex>
          ? U extends ArrayLike<infer UIndex>
              ? StrictEqual<TIndex, UIndex>
              : false
          : true)
    | {
          [K in keyof (T & U)]: K extends keyof T
              ? K extends keyof U ? StrictEqual<T[K], U[K]> : false
              : false
      }[keyof (T & U)]
>;

type StrictEqual<T, U> = And<Equal<T, U> | Xnor<IsAny<T>, IsAny<U>>>;

type Equal<T, U> = And<
    ([U] extends [T] ? true : false) | ([T] extends [U] ? true : false)
>;

type IsAny<T> = boolean extends (T extends never ? true : false) ? true : false;

type And<T extends boolean, U extends boolean = never> =
    // AND of union
    (T | U) extends true ? true : false;

type Xnor<T extends boolean, U extends boolean = never> =
    // XNOR of union
    boolean extends (T | U) ? false : true;

It works for ObjectEqual<Array<any>, Array<number>>, but not for ObjectEqual<Array<Array<any>>, Array<Array<number>>, nor ObjectEqual<Promise<any>, Promise<number>>.

As far as I can tell, there's nothing you can do in the language where Promise<any> reacts differently from Promise<number>, same for deeply nested arrays, without going recursive.

I'm hoping there's some sort of trick that applies here where you can get a type into weird state where the two are distinguishable (in the vein of NoInfer).

KiaraGrouwstra commented 6 years ago

your Equal approach was what I'd try as well.

I thought Partial<[1,2,3]> yielded Array prototype methods (fwiw) but I never managed for other prototypes.

SimonMeskens commented 6 years ago

Alright, no problem. I figured as much, seeing as Typical doesn't have it :)

I'm trying to use the Equal to set up semantic tests. I tried using type-checker, but it doesn't like my project somehow. Is Tsst ready for light production use? I haven't heard about it in a while.

KiaraGrouwstra commented 6 years ago

It kinda worked for me, yeah. Small target audience though. So I forked it to e.g. let the consuming lib decide on TS version. I had trouble using TS off git tho (npm worked okay).