Repository: TypeScript
Issue: Stricter disjunction of interfaces
Labels: Suggestion,Awaiting More Feedback

# Suggestion

## 🔍 Search Terms

List of keywords you searched for before creating this issue. Write them down here so that others can find this suggestion more easily and help provide feedback.

## ✅ Viability Checklist

My suggestion meets these guidelines:

* !!! This can be a breaking change
* This wouldn't change the runtime behavior of existing JavaScript code
* This could be implemented without emitting different JS based on the types of the expressions
* This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
* This feature would agree with the rest of [TypeScript's Design Goals](https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals).


## ⭐ Suggestion

<!-- A summary of what you'd like to see added or changed -->

## 📃 Motivating Example
Disjunction of interfaces can lead to an error 

Here is example 1:
```ts
interface A { id: string }
interface B { id: number }

const a: A = {id: "xxxxx"};

function modifyObject(value: A | B){
    value.id = 100; // this is OK
    //    ^? (property) id: string | number
}

modifyObject(a);

console.log(a.id.toUpperCase());
//             ^? A.id: string
// [ERR]: a.id.toUpperCase is not a function
```
Problem:  Typescript didn't find the error of type mismatch.

Example 2:
```ts
interface A { id: string }
interface B { id: number }

const v = {id: "xxxxx"} as A | B;

function modifyObject(value: A | B){
    value.id = 100; // this is OK
    //    ^? (property) id: string | number
}

modifyObject(v);

console.log(v.id.toUpperCase());
//             ^? (property) id: string | number
// TS: Property 'toUpperCase' does not exist on type 'number' --- OK!
```
Signature of `modifyObject` is same, but now code is correct.

Example 3:
```typescript
interface A { id: string, readonly dataType: "A" }
interface B { id: number, readonly dataType: "B" }

const v = {id: "xxxxx", dataType: "A"} as A | B;

function modifyObject(value: A | B){
    value.id = 100; // this is NOT OK, but works
}

modifyObject(v);

console.log(v); // {id: 100, dataType: "A"} // type mismatch
```

It would be better if interface disjunction handled getters and setters
```ts
interface A { id: string, readonly dataType: "A" }
interface B { id: number, readonly dataType: "B" }

const v: A = {id: "xxxxx", dataType: "A"};

function modifyObject(
    value: /* automatically calculates from (A | B) */ {
        get id(): string | number;
        set id(value: string & number);
        get dataType(): "A" | "B";
    }
){
    console.log(value.id) // OK! This is number | string
    value.id = 100; // TS ERROR: 'number' is not assignable to type 'never'.
}

modifyObject(v); // OK
```

In this form, it will lead to breaking changes, so there can be 2 options:
- make this behavior dependent on the flag
- make additional type like:
```ts
function modifyObject(value: AnyOfType<A | B>){
    value.id = 100; // this is OK
    //    ^? (property) id: string | number
}
```
but this seems impossible