UI 组件库的组件参数类型验证

React Apr 8, 2021

对于 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。

Tags

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.