从打字稿中的变量推断类型

时间:2019-12-03 17:38:38

标签: reactjs typescript typescript-typings typescript3.0

假设我有一个字典,将组件名称映射到实际组件,如下所示:-

const FC1 = ({prop} : {prop: number}) => <>{prop}</>;
const FC2 = ({prop} : {prop: string}) => <>{prop}</>;
const mapComponents =  [
  {type : "Functional Component 1", element : FC1},
  {type : "Functional Component 2", element : FC2}
]

我有一个辅助函数来构造所需的元素,如下所示:-

const ConstructComponent = ({type, props}: {type : string, props? : any}) => {
  for(const x of mapComponents){
    if(x.type === type){
      return <x.element {...props}/>
    }
  }
  return null
}

这样,我可以使用jsx表达式轻松地调用地图中的任何组件

<ConstructElement type="Functional Component 1" props={{prop1 : 123}}/>

我想使其尽可能地安全。我知道可以通过手动创建像这样的类型来实现:-

type ConstructComponentProps = ({type : "Functional Component 1", props : React.ComponentProps<typeof FC1>}) | ({type : "Functional Component 2", props : React.ComponentProps<typeof FC2>})

这是我的问题:-

1)是否有更简单的方法来实现这一目标?我在想能够从mapComponents常量自动推断类型的思路。我知道这行不通,但是类似:-

type ConstructComponentProps = mapComponents.map((obj) => {type : obj.type, props: React.ComponentProps<typeof obj.element>})

2)我可以这种格式获取'type'属性的所有可能值吗?这样的东西让我像:-

type types = "Functional Component 1" | "Functional Component 2"

2 个答案:

答案 0 :(得分:0)

1)

const FC1 = ({prop} : {prop: number}) => <>{prop}</>;
const FC2 = ({prop} : {prop: string}) => <>{prop}</>;
const mapComponents = {
  "Functional Component 1": FC1,
  "Functional Component 2": FC2
};

type MapComponents = typeof mapComponents;
type PropsOf<T> = T extends React.ComponentType<infer P> ? P : never;

function ConstructComponent <T extends keyof MapComponents, P extends PropsOf<MapComponents[T]>>({type, props}: {type: T, props?: P}) {
  for(const x in mapComponents){
    if(x === type){
      const Component = mapComponents[x];
      return <Component {...props} />;
    }
  }
  return null;
}

<ConstructComponent type="Functional Component 2" props={{prop: 123}} />会引发以下错误:The expected type comes from property 'prop' which is declared here on type '{ prop: string; }',因为类型Functional Component 2期望一个名为prop的字符串类型的道具。

2)通过将mapComponents更改为对象,可以获取其键

答案 1 :(得分:0)

如果要保留type元素的mapComponents属性的string literal类型,则需要告诉编译器不要将它们扩展为{{1} },这是默认行为。从TS3.4开始,您可以使用const assertion来请求尽可能窄的推断类型:

string

执行此操作后,便可以从中建立所需的类型。这是确定const mapComponents = [ { type: "Functional Component 1", element: FC1 }, { type: "Functional Component 2", element: FC2 } ] as const 的方法:

ConstructComponentProps

这可以通过使用distributive conditional type(这是type _ConstructComponentProps<C extends typeof mapComponents[number] = typeof mapComponents[number]> = C extends any ? { type: C["type"], props: React.ComponentProps<C["element"]> } : never; type ConstructComponentProps = _ConstructComponentProps /* type ConstructComponentProps = { type: "Functional Component 1"; props: { prop: number; }; } | { type: "Functional Component 2"; props: { prop: string; }; } */ 语法,其中C extends any ? ...是通用类型参数)来将C的元素联合分解为各个部分并对其进行操作。还有其他方法可以执行此操作,但是它们都将以某种形式涉及条件类型。

然后您的mapComponents是一个简单的lookup

Types

好的,希望能有所帮助;祝你好运!

Link to code