Reputation: 41
I have a map storing all kinds of element. Defined as follow:
const map = new Map<string, IComponent<IBase>>();
I want to use this map to store instance of IComponent<IBase>
, IComponent<INumber>
, IComponent<IText>
and so on.
The base interface is defined as follow:
// base interface
interface IBase {}
// base component type
export type IComponent<T extends IBase> = React.FC<T>;
Now I want to add a NumberComponent
// derived interface
interface INumber extends IBase {
someNumber: number;
}
// some component
const NumberComponent: IComponent<INumber> = (props) => {
return (
<div>{props.someNumber}</div>
);
}
I use map.set('number', NumberComponent)
to store it to the map, but I got
Argument of type 'FC<INumber>' is not assignable to parameter of type 'FC<IBase>'.
Types of parameters 'props' and 'props' are incompatible.
Type 'PropsWithChildren<IBase>' is not assignable to type 'PropsWithChildren<INumber>'.
Property 'someNumber' is missing in type 'PropsWithChildren<IBase>' but required in type 'INumber'.
I know I could use map.set('number', NumberComponent as IComponent<IBase>)
to cast the type. However, I don't want to use as
. Is there another way to solve this?
Upvotes: 1
Views: 79
Reputation: 33041
It is possible to do. You just need to overload your Map
import React from 'react'
interface IBase { }
export type IComponent<T extends IBase> = React.FC<T>;
interface INumber extends IBase {
someNumber: number;
}
type MapOverloads = Map<string, IComponent<IBase>> & Map<string, IComponent<INumber>>
const map:MapOverloads = new Map<string, IComponent<IBase>>();
const NumberComponent: IComponent<INumber> = (props) => {
return (
<div>{props.someNumber}</div>
);
}
const BaseComponent: IComponent<IBase> = (props) => {
return null
}
map.set('number', NumberComponent)
map.set('base', BaseComponent)
Main idea was shamelesly stolen from this answer of @jcalz
However, because map
is mutable, and I assume you want it to be mutable, there is a problem with getter
.
const expectNumber = map.get('number') // IComponent<IBase> | undefined
P.S. You was not able to assign IComponent<INumber>
to IComponent<IBase>
because of contravariance
Upvotes: 2