Reputation: 1393
I need to loop through an object which is of type union of two interfaces.
interface Family {
cat: string;
age: string;
family: string;
lastYearFamily: string;
}
interface Model {
cat: string;
age: string;
model: string;
lastYearModel: string;
}
interface Props {
attributionType: 'family' | 'model';
attributions?: Family[] | Model[];
}
const RowComponent = ({
attributionType,
attributions
}: props) =>{
return (
{attributions && attributions.map((attribution: Family | Model ) => (
<tr>
{
attributionType === 'family' && (
<React.Fragment>
<th>{attribution.family}</th>
<th>Family</th>
</React.Fragment>
)
}
{
attributionType === 'model' && (
<React.Fragment>
<th>{attribution.model}</th>
<th>Model</th>
</React.Fragment>
)
}
</tr>
))}
);
}
Since this is a union I am not able to access any other members of the object which are not common.
I can access cat
and age
but not family
, lastYearFamily
etc.
I want to keep the code common if possible and not have separate components for each attribution type.
Upvotes: 0
Views: 1409
Reputation: 62668
You would typically do this with custom type guards, which consists of casting a variable as a prospective type and checking if a field unique to that type exists, which permits you to infer that the value in the variable is indeed of that type.
interface Family {
cat: string;
age: string;
family: string;
lastYearFamily: string;
}
interface Model {
cat: string;
age: string;
model: string;
lastYearModel: string;
}
const isFamily = (f: Family|Model): f is Family => {
return (f as Family).family !== undefined
}
const isModel = (f: Family|Model): f is Model => {
return (f as Model).model !== undefined
}
const x : (Family | Model)[] = [
{cat: "x", age: "y", family: "z", lastYearFamily: "w"},
{cat: "x", age: "y", model: "z", lastYearModel: "w"}
]
x.map(e => {
if (isFamily(e)) {
return e.family;
} else {
return e.model;
}
})
Upvotes: 1
Reputation: 2656
You aren't hinting to the compiler that the string 'family'
in the Props
interface should infer to Family[]
on the attributions. You are instead stating that attributionType
has no correlation to attributions
.
// your code
interface Props {
attributionType: 'family' | 'model';
attributions?: Family[] | Model[];
}
To hint that you want this to the compiler, you can gather the types you need in different interfaces, and only then join them:
interface FamilyProp {
attributionType: "family";
attributions?: Family[];
}
interface ModelProp {
attributionType: "model";
attributions?: Model[];
}
type Props = ModelProp | FamilyProp;
function foo(bar: Props) {
if (bar.attributionType === "model") {
bar.attributions[0].lastYearModel; // compiles fine
}
if (bar.attributionType === "family") {
bar.attributions[0].lastYearFamily; // compiles fine
}
}
The compiler will infer the types inside the if
s thanks to Type Guard.
Upvotes: 1