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

Inferring types from a variable in typescript

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

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"

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改成一个对象,就可以得到它的keys

如果您想保留 string literal types of the type properties of the elements of mapComponents, you'll need to tell the compiler not to widen them to string which is the default behavior. Since TS3.4 you can use a const assertion 以请求尽可能窄的推断类型:

const mapComponents = [
  { type: "Functional Component 1", element: FC1 },
  { type: "Functional Component 2", element: FC2 }
] as const

完成此操作后,您就可以从中构建所需的类型。这是我确定 ConstructComponentProps:

的方法
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;
    };
} */

这通过使用 distributive conditional type(这是 C extends any ? ... 语法,其中 C 是泛型类型参数)将 [=14= 的元素并集分开] 分成单独的部分并对每个部分进行操作。还有其他方法可以做到这一点,但它们都会以某种形式涉及条件类型。

然后你的 Types 是一个简单的 lookup:

type Types = ConstructComponentProps["type"];
// type Types = "Functional Component 1" | "Functional Component 2"

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

Link to code