I have few several types in my apps which has few attributes in common. I abstracted common attributes into separated type. Now I would like to write a function that accept any type which has the base type in common.
This few lines explains the problem much better then the words:
type Box = {
width: number,
height: number,
}
// is like Box + it has position
type PositionedBox = Box & {
type: 'positionedBox',
left: number,
top: number,
}
// is like Box + it has colo
type ColorBox = Box & {
type: 'colorBox',
color: string,
}
const logSize = (obj: Box) => {
console.log({ w: obj.width, h: obj.height });
};
const logPosition = (obj: PositionedBox) => {
console.log({ l: obj.left, t: obj.top });
};
const logColor = (obj: ColorBox) => {
console.log({color: obj.color});
};
// this function should accept any Box like type
const logBox = (obj: Box) => {
logBox(obj);
// $ERROR - obj Box has no type property
// (make sense at some point, but how to avoid it?)
if (obj.type === 'colorBox') logColor(obj);
if (obj.type === 'positionedBox') logPosition(obj);
}
The question is: How the logBox()
function declaration should looks like in order to pass flowtype check.
These errors are legitimate since there is nothing stopping me from passing {width: 5, height: 5, type: 'colorBox'}
to the logBox
function, since that is a subtype of Box
. If you really want to accept any subtype of Box
you will have to deal with the consequences, which are that checking the type
field gives you no information about any other properties.
If you want to only allow specific subtypes of Box
, then you want a disjoint union. Below I have renamed Box
to BaseBox
and added a separate Box
type that is a union of the two specialized boxes. This example passes.
type BaseBox = {
width: number,
height: number,
}
// is like Box + it has position
type PositionedBox = BaseBox & {
type: 'positionedBox',
left: number,
top: number,
}
// is like Box + it has colo
type ColorBox = BaseBox & {
type: 'colorBox',
color: string,
}
type Box = PositionedBox | ColorBox;
const logSize = (obj: Box) => {
console.log({ w: obj.width, h: obj.height });
};
const logPosition = (obj: PositionedBox) => {
console.log({ l: obj.left, t: obj.top });
};
const logColor = (obj: ColorBox) => {
console.log({color: obj.color});
};
// this function should accept any Box like type
const logBox = (obj: Box) => {
logBox(obj);
// $ERROR - obj Box has no type property
// (make sense at some point, but how to avoid it?)
if (obj.type === 'colorBox') logColor(obj);
if (obj.type === 'positionedBox') logPosition(obj);
}