I know this question is very old, but I was playing around with the same problem, as I was learning the difference between | and & when making Type Unions.
there are some options to solve this problem (that is also linter-friendly).
The best way is to use a discriminator in all your interfaces (a narrow interface).
//First create a super-interface that have the discriminator
interface B
{
kind:'b1'|'b2' //define discriminator with all the posible values as string-literals (this is where the magic is)
}
interface B1 extends B
{
kind: 'b1' //now narrow the inherited interfaces literals down to a single
//after that add your interface specific fields
data1: string;
data: string;
}
interface B2 extends B
{
kind:'b2' //now narrow the inherited interfaces literals down to a single
//after that add your interface specific fields
data2: string;
data: string;
}
//lets initialize 1 B1 type by using the literal value of a B1 interface 'b1'
var b1: B1|B2 = {
kind:'b1',
data: 'Hello From Data',
data1:'Hello From Data1'
//typescript will not allow you to set data2 as this present a B1 interface
}
//and a B2 Type with the kind 'b2'
var b2: B1|B2 = {
kind: 'b2',
data: 'Hello From Data',
//typescript will not allow you to set data1 as this present a B2 interface
data2: 'Hello From Data2'
}
another option is to check for fields on an object using the "in"-keyword, but this can cause a lot of boilerplate code, as you have to update it every time you change your interface.
interface B1
{
data1: string;
data: string;
}
interface B2
{
data2: string;
data: string;
}
var b3: B1|B2 = {
data: 'Hello From Data',
data1:'Hello From Data1',
data2:'Hello From Data2'
}
console.log(b3.data); //this field is common in both interfaces and does not need a check
if('data1' in b3) //check if 'data1' is on object
{
console.log(b3.data1);
}
if('data2' in b3){
console.log(b3.data2); //check if 'data2' is on object
}