UI 组件库的组件参数类型验证
对于 TypeScript 的 React 项目,编写一个类型安全的组件是非常自然的。这里以 UUI 为例。UUI 是一个用 TypeScript 打造的 React 组件库。
UUI 精简版的 TextField 组件:
export interface TextFieldFeatureProps {
name?: string;
type?: 'text' | 'tel' | 'url' | 'email' | 'password';
placeholder?: string;
loading?: boolean;
disabled?: boolean;
value?: string | null | undefined;
onChange?: (value: string, event: React.ChangeEvent<HTMLInputElement>) => void;
}
export funtion TextField(props: TextFieldFeatureProps) {
// implementation
}
如果是在实际开发写业务功能的组件时,直接用 TypeScript 就完全满足了对组件参数类型安全的保证。但是对于一个组件库来说,这个库可能会被一个 Pure JavaScript 项目所使用,这时候 TypeScript 编译期时面的类型安全保证就完全失效了。所以在编译时类型安全的同时,还要在运行时对传入的这些参数类型进行验证。
React 官方有提供一个工具 prop-types 用于运行时验证参数类型。可以使用这个工具对组件做一个运行时验证的补充,同时写一个工具用来创建 propTypes。
export function createComponentPropTypes<
T extends { [key in string]: any }
>(data: UUIComponentFeaturePropTypes<T>) {
return data
}
function RecursiveShapeType<S, A extends string>(
shape: S,
childAttr: A,
mapper = <X extends PropTypes.Requireable<PropTypes.InferProps<S>>>(shape: X) => PropTypes.arrayOf(shape),
) {
const children = mapper(PropTypes.shape(shape));
(shape as any)[childAttr] = children;
const tagPropTypes = PropTypes.shape(shape);
return tagPropTypes as PropTypes.Requireable<PropTypes.InferProps<S & { [key in A]: typeof children }>>
}
function NullableType(propType: any) {
return (props: any, propName: any, ...rest: any) => props[propName] === null ? null : propType(props, propName, ...rest);
}
const NullType = PropTypes.oneOf([null])
export const ExtraPropTypes = {
null: NullType,
nullable: NullableType,
recursiveShape: RecursiveShapeType,
}
export const TextFieldPropTypes = createComponentPropTypes<TextFieldFeatureProps>({
name: PropTypes.string,
type: PropTypes.oneOf(['text', 'tel', 'url', 'email', 'password']),
placeholder: PropTypes.string,
loading: PropTypes.bool,
disabled: PropTypes.bool,
value: PropTypes.string,
onChange: PropTypes.func,
})
TextField.propTypes = TextFieldPropTypes
顺便一说,现在做工具库一般有两种模式:
- JavaScript 为主,补写裸的 TypeScript *.d.ts 文件用于描述工具库API的类型
- TypeScript 为主,补充做一些运行时检查
大势所趋,同时我个人也认为,以 TypeScript 为主更好。从这几年的 TypeScript 开发经验来看,TypeScript 的确是对于开发规范齐整安全正确的项目有非常大的帮助。
createComponentPropTypes 这个工具也是在这样的思路下写出来的,只是对于 TypeScript 做一个补充,而且还利用 TypeScript 类型推断,确保开发者在新增 Props Interface 定义时,在编译器就告知开发者要补充 propTypes。