Repository: TypeScript
Issue: Satisfies hinting for equality type overlap
Labels: Suggestion,Awaiting More Feedback

# Suggestion

<!--
  Please fill in each section completely. Thank you!
-->

## 🔍 Search Terms

<!--
  💡 Did you know? TypeScript has over 2,000 open suggestions!
  🔎 Please search thoroughly before logging new feature requests as most common ideas already have a proposal in progress.
  The "Common Feature Requests" section of the FAQ lists many popular requests: https://github.com/Microsoft/TypeScript/wiki/FAQ#common-feature-requests

  Replace the text below:
-->

comparison, satisfies, equality, overlap, equals

## ✅ Viability Checklist

<!--
   Suggestions that don't meet all these criteria are very, very unlikely to be accepted.
   We always recommend reviewing the TypeScript design goals before investing time writing
   a proposal for ideas outside the scope of the project.
-->
My suggestion meets these guidelines:

* [x] This wouldn't be a breaking change in existing TypeScript/JavaScript code
* [x] This wouldn't change the runtime behavior of existing JavaScript code
* [x] This could be implemented without emitting different JS based on the types of the expressions
* [x] This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
* [x] This feature would agree with the rest of [TypeScript's Design Goals](https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals).


## ⭐ Suggestion


Add the ability for satisfies to hint a possible overlapping type for equality. Eg given `x: X` and `y: Y` if `X extends S` and `Y extends S` then `x satisfies S === y` should type check even though `x == y` does not.

Right now, typescript has a check for type overlaps on equality. This catches cases where you compared the wrong values but is sometimes wrong if you have two interfaces, since classes can implement more than one interface. Often 

## 📃 Motivating Example

```ts
interface CommonParent {
  buz(): void;
}

interface Foo extends CommonParent {
  foo(): void;
}

interface Bar extends CommonParent {
  bar(): void;
}

class Both implements Foo, Bar {
  buz(): void {}
  bar(): void {}
  foo(): void {}
}

function compare(l: Foo, r: Bar): boolean {
  return l satisfies CommonParent == r;
}
```

## 💻 Use Cases


It's a bit hard to come up with a compact real-world example of this coming up because it often happens as a consequence of the structure of large codebases. I have mostly seen it come up when comparing an abstract class implementation of an interface with an instance of a more-specific interface, eg:

```ts
interface IThing {
  thingStuff(): void;
}

abstract class AThing implements IThing {
  thingStuff() {}
  abstract method(): void;
}

interface IMoreSpecificThing extends IThing {
  otherStuff(): void;
}

class MoreSpecificThingImpl extends AThing implements IMoreSpecificThing {
  method(): void {}
  otherStuff(): void {}
}


// deep inside some logic
function compare(l: AThing, r: IMoreSpecificThing): boolean {
  return l satisfies IThing == r;
}
```

In the meantime, using `as` casting as a workaround works, but in general it would be nice to avoid using type-unsafe casting in places where it isn't need. An alternative to this would be an explicit upcasting operator.