简体   繁体   English

如何定义文字 object 以使用 generics 存储不同的属性

[英]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:以下是它如何将keycomponent作为单独的 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.

相关问题 如何检查提供的属性是否属于泛型对象 - How to check if provided properties belongs to generics object 如何根据对象内的值定义文字类型 - how to define a literal type based on a value inside an object 如何在使用 Typescript 时将样式化组件添加为 object 文字的属性? - How to add styled components as properties of an object literal while using Typescript? 如何为 React Action Hook Store 定义 Typescript 属性和类型 - How to define Typescript properties and types for a React Action Hook Store 如何在Redux中存储具有单独属性的对象 - How to store an object with separate properties in Redux 无法从其他文件定义导入的 object,存储在 state 中并将其传播到 React 中的另一个变量中 - Unable to define an imported object from a different file , store in a state and spread it in another variable in React 为 Redux Store 定义 initialState 的不同方法 - different ways to define initialState for Redux Store 为什么我可以将未知属性分配给 typescript 中的文字 object? - Why can I assign unknown properties to literal object in typescript? 在反应中,如何获取某些 object 属性并将其存储在不同的 object 中? - In react, how to take certain object property and store it in a different object? 在严格模式下,对象文字不能具有多个具有相同名称的属性 - An object literal cannot have multiple properties with the same name in strict mode
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM