我在React应用程序中有一个ModalContext来打开对话框,它需要一个带有组件和道具键的对象,并且我希望打字稿能够正确推断道具与组件道具相同,因此我可以摆脱ModalComponent 组件中的 switch 语句:
import { ComponentProps, createContext } from 'react';
import SelectYearModal from '@components/SelectYearModal';
import ChangeDefaultModal from '@components/ChangeDefaultModal';
const ModalComponents = {
SelectYearModal: SelectYearModal,
ChangeDefaultModal: ChangeDefaultModal,
};
export type ModalMap = typeof ModalComponents;
type ModalComponentProps<T extends keyof ModalMap> = ComponentProps<
ModalMap[T]
>;
interface ModalState<T extends keyof ModalMap> {
component: T;
props: ModalComponentProps<T>;
}
type AnyModalState = {
[K in keyof ModalMap]: ModalState<K>;
}[keyof ModalMap];
export type ModalType = AnyModalState | null;
export interface ModalContextValues {
Modal: ModalType;
setModal: (modal: ModalType) => void;
loading: boolean;
setLoading: (loading: boolean) => void;
}
export const ModalContext = createContext<ModalContextValues>({
Modal: null,
setModal: () => {},
loading: false,
setLoading: () => {},
});
export const ModalComponent = ({ modal }: { modal: ModalType }) => {
if (!modal) return null;
switch (modal.component) {
case 'SelectYearModal':
return <SelectYearModal {...modal.props} />;
case 'ChangeDefaultModal':
return <ChangeDefaultModal {...modal.props} />;
default:
return null;
}
};
所以我想使用这样的东西,这样我就不必使用 switch 语句:
export const ModalComponent = ({ modal }: { modal: ModalType }) => {
if (!modal) return null;
const Modal = ModalComponents[modal.component];
return <Modal {...modal.props} />;
};
但是 Typescript 会抛出错误,因为它认为 props 可以是 ModalComponents 中的任何组件:
Argument of type 'SelectYearModalProps | ModalConfirmProps' is not assignable to parameter of type 'SelectYearModalProps & ModalConfirmProps'.
Type 'ModalConfirmProps' is not assignable to type 'SelectYearModalProps & ModalConfirmProps'.
Type 'ModalConfirmProps' is missing the following properties from type 'SelectYearModalProps': years, selectedYearIdx, setSelectedYearIdx
我想知道是否有办法让 Typescript 正确推断出 props 将是对应组件的 props
不确定这是否是您正在寻找的,但这适合您的用例吗?
根据动态分配道具类型的附加要求,这是更新的逻辑
type ComponenProps<T extends (...args: any[]) => any> = T extends (arg: infer A) => any ? A : never;
type FunctionsMap = typeof ModalComponents;
type UnionOfProps = keyof FunctionsMap extends infer K ?
K extends keyof FunctionsMap ? ComponenProps<FunctionsMap[K]> : never
: never;
type UnionToIntersection<U> =
(U extends any ? (x: U)=>void : never) extends ((x: infer I)=>void) ? I : never
type ModalProps = UnionToIntersection<UnionOfProps>
export const ModalComponent = ({ modal }: { modal: ModalType }) => {
if (!modal) return null;
const Modal = ModalComponents[modal.component];
const props = modal.props as ModalProps;
return <Modal {...props} />;
};