[英]How to define a literal object to store different properties with generics
I want to store some react components in a literal object.我想将一些反应组件存储在文字 object 中。 And these components' props are different.
而且这些组件的 props 是不同的。
interface ComponentMap<P = any> {
[key: string]: React.JSXElementConstructor<P>
}
const cMap: ComponentMap = {}
interface AProps {
str: string
}
interface BProps {
n: number
}
cMap['a'] = (props: AProps) => { /* code */ }
cMap['b'] = (props: BProps) => { /* code */ }
// there may be c, d, e and other components assigned into cMap
Currently I use <P = any>
to avoid type errors.目前我使用
<P = any>
来避免类型错误。 But when I get a component from cMap
, its props type is also any
.但是当我从
cMap
获取一个组件时,它的 props 类型也是any
。
const CompA = cMap['a']
const CompB = cMap['b']
<CompA />
<CompB />
// no type errors
Is there any way to keep the props' type of CompA/B
?有什么办法可以保持道具的
CompA/B
类型? How to correctly define ComponentMap
?如何正确定义
ComponentMap
? Please help, thanks!请帮忙,谢谢!
When you have a situation where you want to enforce that a variable extends
a particular type without widening the type of that variable, then you need to create the variable through a function.当您想要强制变量
extends
特定类型而不扩大该变量的类型时,您需要通过 function 创建变量。
type BaseMap = Record<string, ComponentType<any>;
const createComponentMap = <M extends BaseMap>(map: M): M => map;
const cMap = createComponentMap({
a: (props: AProps) => { /* code */ return null; },
b: (props: BProps) => { /* code */ return null; }
})
const ComponentA = cMap.a;
<ComponentA str="a"/> // ok
<ComponentA/> // error: Property 'str' is missing in type '{}' but required in type 'AProps'
more unknown components will be assigned into cMap dynamically
更多未知组件将被动态分配到 cMap
This part makes it more interesting.这部分使它更有趣。 You could create something fancy here where the map had methods to add new objects.
您可以在这里创建一些花哨的东西,其中 map 具有添加新对象的方法。 The simple solution is just to create a new map object whenever you add anything because you always want to change the type of the map.
简单的解决方案是在添加任何内容时创建一个新的 map object,因为您总是想更改 map 的类型。 Here's how we can add a third component
c
:下面是我们如何添加第三个组件
c
:
const nextMap = createComponentMap({
...cMap,
c: (props: CProps) => { /* code */ return null; }
});
Here's how it works with the chaining approach.以下是它如何与链接方法一起使用。 It's easier for the
add
function to take an object like {c: ComponentC}
than separate key and value arguments.对于
add
function 来说,像{c: ComponentC}
这样的 object 比单独的键和值 arguments 更容易。
interface ComponentMap<M extends BaseMap> {
map: M;
add<T extends BaseMap>(added: T): ComponentMap<M & T>;
get<K extends keyof M>(key: K): M[K];
}
const createComponentMap = <M extends BaseMap>(map: M): ComponentMap<M> => ({
map,
add<T extends BaseMap>(added: T): ComponentMap<M & T> {
return createComponentMap({
...map,
...added
})
},
get<K extends keyof M>(key: K): M[K] {
return map[key];
}
});
const cMap = createComponentMap({
a: (props: AProps) => { /* code */ return null; },
b: (props: BProps) => { /* code */ return null; }
})
const ComponentA = cMap.get('a') // or cMap.map.a;
const nextMap = cMap.add({
c: (props: CProps) => { /* code */ return null; }
});
Here's how it works with the key
and component
as separate arguments:以下是它如何将
key
和component
作为单独的 arguments 工作:
interface ComponentMap<M extends BaseMap> {
map: M;
add<K extends string, C extends ComponentType<any>>(key: K, component: C): ComponentMap<M & Record<K, C>>;
get<K extends keyof M>(key: K): M[K];
}
const createComponentMap = <M extends BaseMap>(map: M): ComponentMap<M> => ({
map,
add<K extends string, C extends ComponentType<any>>(key: K, component: C): ComponentMap<M & Record<K, C>> {
return createComponentMap({
...map,
[key]: component
})
},
get<K extends keyof M>(key: K): M[K] {
return map[key];
}
});
const cMap = createComponentMap({})
.add("a", (props: AProps) => { /* code */ return null; })
.add("b", (props: BProps) => { /* code */ return null; })
.add("c", (props: CProps) => { /* code */ return null; });
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.