Closed stwlam closed 51 minutes ago
hm… i'm not sure what the behavior should be here. the idea is that we want the second argument to extend
the first argument to prevent "accidental" comparisons, but i think this is a case where we should allow this.
Given the commutativeness of equality, maybe that ought to be given up?
i think we would sooner give up commutativity of equality than practical usefulness. in either case, i don't think it's useful for us to disallow b
if its type is the same as a
but nullable.
I guess the practical usefulness isn't clear to me. E.g., it seems perfectly sensible to expect the following to work.
declare const a3: { foo: boolean }
declare const b3: object;
isDeepEqual(a3, b3); // No overload matches this call
Our functions are designed with pipes in mind and to surface issues as compile-time (typing) issues instead of runtime bugs. One such concern is that we want to detect when assumptions about the shape and make of an object change during the lifetime of a project, but to do this, we need to sacrifice flexibility and some ergonomics of how our types work; mainly, this manifests as "forward" typing where the first param (usually the "data" param) defines further refinements on the other params in the function signature.
The best example of this (and a function we get feedback on often) is prop:
declare const MY_ARRAY: Array<{ a: number }>;
MY_ARRAY.map(R.prop("a"))
The way prop is typed, you'd get a nice typeahead from your IDE while you are typing the prop name, but more importantly, renaming the prop would cause a typescript error:
declare const MY_ARRAY: Array<{ b: number }>;
// @ts-expect-error [ts2345] ...
MY_ARRAY.map(R.prop("a"));
Similarly, if we want isDeepEqual
to surface a compile-time error when your types change, we need the second param to rely on the first:
// @ts-expect-error [ts2345] ...
MY_ARRAY.filter(isDeepEqual({ a: 123 }));
We believe this is the right approach to designing the API as it brings the most value to our users.
Playground link