Closed
Description
TypeScript Version: 2.8.1
Search Terms:
union complex types
Code
interface IOne{
stringOne: string;
numberOne: number;
}
interface ITwo{
stringTwo: string;
numberTwo: number;
}
type IEitherOr = IOne | ITwo;
var itemThree: IEitherOr = {
numberOne: 42343,
stringOne: "",
numberTwo: "sdfsd"
}
Expected behavior:
This should throw an error.
IMHO I think that there are 3 issues here.
- First off I was surprised that
numberTwo
was allowed AT ALL. I think that the type should be eitherIOne
ORITwo
, not a mix of the two. - If we do allowed mixed types (which I don't think we should) then surely if there are any properties for
ITwo
we should require all of them. This should throw an error unless we have specified bothnumberTwo
andstringTwo
- lastly it's not even type checking
numberTwo
. It is typed as a number but it's fine with a string.
Actual behavior:
The compiler is fine with this!
Playground Link:
Link
Work Around:
You can enforce proper type checking on the secondary mixed type like this:
type IFixedEitherOr =
( IOne & Partial<ITwo> )
|
( ITwo & Partial<IOne> );
var itemFour: IFixedEitherOr = {
numberOne: 42343,
stringOne: "",
numberTwo: "dfgdf" // this errors
}
The code above correctly fails to compile.
Metadata
Metadata
Assignees
Type
Projects
Milestone
Relationships
Development
No branches or pull requests
Activity
jack-williams commentedon Apr 5, 2018
Related: #20863
There is nothing unsound about this interaction but it can be undesirable which is why there is excess property checking at initialisation, however I believe this does not work for unions without a discriminant (see related issue).
Roaders commentedon Apr 5, 2018
I really can't see how you can say there is nothing unsound when I have a number property assigned to a string!
jack-williams commentedon Apr 5, 2018
The type
IEitherOr
does not promise that propertynumberTwo
will be a type number. It promises that either:stringOne
will bestring
andnumberOne
will benumber
, OR,stringTwo
will bestring
andnumberTwo
will benumber
.If you state
IEitherOr
using intersection and union (which is roughly equivalent), and consider them like logical and, and logic or. Then you have:({stringOne: string} & {numberOne: number}) | ({stringTwo: string} & {numberTwo: number})
which can be satisfied with only
{stringOne: string}
and{numberOne: number}
: you never need to claim anything aboutnumberTwo
.Note: you can get unsound behaviour from this object:
however this is from narrowing rules for union, not assignment compatibility.