Table of contents
1. 背景:
回顾我们在第一篇列的一些问题:
1.TypeScript类型优化,我们可以考虑增加泛型,让我们的open/close方法的参数更加准确
2.open方法可以增加参数,用于拓展弹层组件和子组件的props
3.目前的实现仅局限于antd视图,可以实现兼容其他UI库的情况4.注入的props(injectProps)目前是写死的字段,可以根据实际要求进行转换5.schema的定义需要引入type,可以考虑提供createLayerSchemas方法,协助生成
其中,3和4两点我们在上一篇已经解决。我们这一篇可以优化TypeScript类型提示
我们可以实现的类型提示优化:
SchemaItem["children"]的类型提示(弹层的子组件我们注入了一些props,但需要开发者手动声明)
比如下面的例子,我们期望实际上使用的参数类型应为"modal" | "drawer"
const schemas: Schemas = [
{
key: "modal",
type: "Modal",
props: { title: "弹窗" },
childrenProps: { text: "Modal" },
children: ({text}) => <div>弹窗内容{text}</div>,
},
{
key: "drawer",
type: "Drawer",
props: { title: "抽屉" },
childrenProps: {},
children: () => <div>抽屉内容</div>,
},
];
export default () => {
const { views, actions } = useLayer(schemas);
const { modalView, drawerView } = views;
return (
<div>
{modalView}
{drawerView}
<Button
onClick={() => {
// 实际上这一步有bug,但是open的参数是string,所以没有类型校验提示
actions.open("modal1");
}}
>
打开弹窗
</Button>
<Button
onClick={() => {
actions.open("drawer");
}}
>
打开抽屉
</Button>
</div>
);
};
此外,除了上述的优化项,我们也需要考虑让弹层组件能够拿到action,确保子组件内部也能有开关弹层的方法,但这也需要对子组件的类型声明进行优化
可以提供辅助函数,协助用户生成Schema,避免用户需要手动引入type。
2. Actions的类型优化
我们先看useLayer hook现在的类型定义:
function useLayer(schemas: Schemas, options?: LayerContextType): {
actions: Actions;
views: Views;
}
我们也可以对useLayer传入泛型,从而约束Actions和SchemaItem的类型:
function useLayer<LayerKeys extends string = any>(schemas: SchemaItem<LayerKeys>[], options?: LayerContextType): {
actions: Actions<LayerKeys>;
views: Views;
}
/**单个弹层的约定数据
* @param type 弹层类型 "Modal" | "Drawer"
* @param key 弹层的唯一标识
* @param defaultOpen 是否默认打开弹层,默认不打开弹层
* @param props 弹层的props ModalProps | DrawerProps
* @param children 弹层的内容 ComponentType<any>
* @param childrenProps 弹层的内容props
* @example
* {
type: "Modal",
key: "modalKey",
props: {title: "弹窗"},
children: () => <div>弹窗内容</div>,
childrenProps:{text:"Modal"}
}
*/
export type SchemaItem<LabelKey extends string = any> = {
/**弹层类型 */
type: LayerType;
/**弹层的唯一标识 */
key: LabelKey;
/**是否默认打开弹层,默认不打开弹层 */
defaultOpen?: boolean;
/**弹层的props */
props: ModalProps | DrawerProps;
/**弹层的内容 */
children: ComponentType<any>;
/**弹层的内容props */
childrenProps?: Record<string, any>;
};
/**弹层的操作方法 */
export type Actions<LabelKey extends string = any> = {
/**关闭对应key的弹层 */
close: (layerKey: LabelKey) => void;
/**关闭所有弹层 */
closeAll: () => void;
/**打开对应key的弹层 */
open: (layerKey: LabelKey) => void;
};
通过这个方式,可以实现actions方法的类型提示
import { useLayer } from "./implements/index";
import { type SchemaItem } from "./implements/typing";
import { Button } from "antd";
type LayerKeys = "modal" | "drawer";
const schemas: SchemaItem<LayerKeys>[] = [
{
key: "modal",
type: "Modal",
props: { title: "弹窗" },
childrenProps: { text: "Modal" },
children: ({ text }) => <div>弹窗内容{text}</div>,
},
{
key: "drawer",
type: "Drawer",
props: { title: "抽屉" },
childrenProps: {},
children: () => <div>抽屉内容</div>,
},
];
3.createLayerSchemas辅助方法
实现一个辅助方法,协助用户生成相应类型的Schema。
实现的方式,其实类似vite的defineConfig方法
import { type SchemaItem } from "./typing";
export function createLayerSchemas<LabelKeys extends string = any>(
schemas: SchemaItem<LabelKeys>[]
): SchemaItem<LabelKeys>[] {
return schemas;
}
调用:
import { createLayerSchemas } from "./implements/helper";
const schemas = createLayerSchemas([
{
key: "modal",
type: "Modal",
props: { title: "弹窗" },
childrenProps: { text: "Modal" },
children: ({ text }) => <div>弹窗内容{text}</div>,
},
{
key: "drawer",
type: "Drawer",
props: { title: "抽屉" },
childrenProps: {},
children: () => <div>抽屉内容</div>,
},
]);
对应的类型定义:
createLayerSchemas<"modal" | "drawer">(schemas: SchemaItem<"modal" | "drawer">[]): SchemaItem<"modal" | "drawer">[]
4. 总结:
我们可以通过泛型去约束Actions以及SchemaItem的类型,实现更准确的类型定义