Equality
Equality is hard to check in TypeScript.
A perfect check is in fact impossible to implement in the language itself —
it would have to come from the compiler as a built-in.
What type-plus provides is a check that is exact for the cases you are likely to write,
with one documented limit.
IsEqual / IsNotEqual
Section titled “IsEqual / IsNotEqual”type IsEqual<A, B, $O extends $StrictOptions<$O, IsEqual.$Options> = {}>type IsNotEqual<A, B, $O extends $StrictOptions<$O, IsNotEqual.$Options> = {}>🎭 predicate — IsEqual resolves to true when A and B are the same type, otherwise false.
IsNotEqual is its inverse, with the same rules.
It is the type-level counterpart of ===,
and it handles the special types (any, unknown, never, void) correctly
rather than letting them swallow the comparison.
type R = IsEqual<1, 1> // truetype R = IsEqual<any, any> // truetype R = IsEqual<boolean, boolean> // truetype R = IsEqual<[1], [1]> // true
type R = IsEqual<boolean, true> // falsetype R = IsEqual<any, 1> // falsetype R = IsEqual<[any], [1]> // falsetype R = IsEqual<{ a: 1 }, { a: 1; b: 2 }> // false
type R = IsNotEqual<1, 1> // falsetype R = IsNotEqual<boolean, true> // truesymbol gets no special treatment: two distinct unique symbols are not equal, and a
unique symbol is not equal to symbol. Before 8.0.0, IsEqual (then a deprecated alias of the
removed Equal) reported both of those as equal.
declare const s1: unique symboldeclare const s2: unique symbol
type R = IsEqual<typeof s1, typeof s1> // truetype R = IsEqual<typeof s1, typeof s2> // falsetype R = IsEqual<typeof s1, symbol> // falseEqual and the old positional IsEqual/IsNotEqual/NotEqual were removed in 8.0.0; see
Migrating from Then/Else to $Options.
Intersections are only flattened one level
Section titled “Intersections are only flattened one level”An intersection is compared as though it were flattened, but only at the first level. The check does not recurse into properties to flatten intersections nested inside them.
// true — the intersection is at the top levelIsEqual<{ a: 1 } & { b: 2 }, { a: 1; b: 2 }>
// false — the intersection is nested one level downIsEqual<{ nested: { a: number; b: string } }, { nested: { a: number } & { b: string } }>This is deliberate. Flattening intersections recursively would not terminate on a recursive type, so the check trades exactness in the nested case for one that always halts.