-
Notifications
You must be signed in to change notification settings - Fork 13k
Open
Labels
Awaiting More FeedbackThis means we'd like to hear from more people who would be helped by this featureThis means we'd like to hear from more people who would be helped by this featureSuggestionAn idea for TypeScriptAn idea for TypeScript
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
Metadata
Metadata
Assignees
Labels
Awaiting More FeedbackThis means we'd like to hear from more people who would be helped by this featureThis means we'd like to hear from more people who would be helped by this featureSuggestionAn idea for TypeScriptAn idea for TypeScript