Open
Description
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.
β Suggestion
π Motivating Example
Disjunction of interfaces can lead to an error
Here is example 1:
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:
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:
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
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:
function modifyObject(value: AnyOfType<A | B>){
value.id = 100; // this is OK
// ^? (property) id: string | number
}
but this seems impossible