如何定义文字对象以使用泛型存储不同的属性 [英] How to define a literal object to store different properties with generics
问题描述
我想在文字对象中存储一些反应组件.而且这些组件的props是不一样的.
interface ComponentMap{[key: string]: React.JSXElementConstructor<P>}const cMap: ComponentMap = {}接口 AProps {str:字符串}接口 BProps {n:数字}cMap['a'] = (props: AProps) =>{/* 代码 */}cMap['b'] = (props: BProps) =>{/* 代码 */}//可能有 c、d、e 等组件被赋值到 cMap
目前我使用 来避免类型错误.但是当我从
cMap
获取一个组件时,它的 props 类型也是 any
.
const CompA = cMap['a']const CompB = cMap['b']<CompA/><CompB/>//没有类型错误
有没有办法保持道具的CompA/B
类型?如何正确定义ComponentMap
?请帮忙,谢谢!
当你想强制一个变量扩展
一个特定类型而不扩展那个变量的类型时,然后您需要通过函数创建变量.
type BaseMap = Record;const createComponentMap = (map: M): M =>地图;const cMap = createComponentMap({a:(道具:AProps)=>{/* 代码 */返回空值;},b:(道具:BProps)=>{/* 代码 */返回空值;}})const ComponentA = cMap.a;<ComponentA str=a"/>//好的<组件A/>//错误:属性str"在类型{}"中缺失,但在AProps"类型中需要
<块引用>
更多未知组件会动态分配到cMap中
这部分让它变得更有趣.您可以在这里创建一些奇特的东西,其中地图具有添加新对象的方法.简单的解决方案就是在您添加任何内容时创建一个新的地图对象,因为您总是想更改地图的类型.下面是我们如何添加第三个组件 c
:
const nextMap = createComponentMap({...cMap,c:(道具:CProps)=>{/* 代码 */返回空值;}});
以下是链接方法的工作原理.add
函数采用像 {c: ComponentC}
这样的对象比单独的键和值参数更容易.
interface ComponentMap{地图:M;add(添加:T):ComponentMap(map: M): ComponentMap=>({地图,add(添加:T):ComponentMap{/* 代码 */返回空值;},b:(道具:BProps)=>{/* 代码 */返回空值;}})const ComponentA = cMap.get('a')//或 cMap.map.a;const nextMap = cMap.add({c:(道具:CProps)=>{/* 代码 */返回空值;}});
以下是它如何将 key
和 component
作为单独的参数使用:
interface ComponentMap{地图:M;add>(key: K, component: C): ComponentMap(map: M): ComponentMap=>({地图,add>(key: K, component: C): ComponentMap {/* code */return null; }).add("b", (props: BProps) => {/* code */return null; }).add("c", (props: CProps) => {/* code */return null; });
I want to store some react components in a literal object. And these components' props are different.
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. But when I get a component from cMap
, its props type is also 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
? How to correctly define 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.
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
This part makes it more interesting. You could create something fancy here where the map had methods to add new objects. 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. Here's how we can add a third component 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.
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:
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; });
这篇关于如何定义文字对象以使用泛型存储不同的属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!