antd@5 源码:TypeScript类型工具 (GetProps、GetProp、GetRef...)

376 阅读3分钟

antd5 提供了一系列实用的TypeScript类型工具,本文将详细介绍这些工具的使用方法和实现原理。 欢迎各位大佬留言指正~

antd5 文档说明

antd 源码路径

核心类型工具

  • GetProps
  • GetProp
  • GetRef
  • GetContextProps
  • GetContextProp

1. GetProps - 获取组件的Props类型

GetProps工具可以从组件类型中提取完整的Props类型定义。这对于组件封装和类型继承特别有用。

实现原理

export type GetProps<T extends React.ComponentType<any> | object> = T extends React.ComponentType<infer P>
  ? P
  : T extends object
  ? T
  : never;

该工具利用条件类型和infer关键字推断组件的Props类型:

  • 如果T是React组件类型,使用infer P推断并返回其Props类型
  • 如果T已经是对象类型,则直接返回T
  • 否则返回never

使用示例

import { Button } from 'my-ui';
import type { GetProps } from 'my-ui';

// 获取Button组件的Props类型
type ButtonProps = GetProps<typeof Button>;

// 使用提取的类型
const onClick: ButtonProps['onClick'] = (e) => {
  console.log('Button clicked', e);
};

// 获取子组件的Props类型
type CheckboxGroupProps = GetProps<typeof Checkbox.Group>;

2. GetProp - 获取组件的单个Prop类型

GetProp工具可以提取组件特定属性的类型,并自动处理非空情况,确保类型安全。

实现原理

export type GetProp<
  T extends React.ComponentType<any> | object,
  PropName extends keyof GetProps<T>
> = NonNullable<GetProps<T>[PropName]>;

该工具的实现过程:

  1. 首先使用GetProps<T>获取组件的所有props类型
  2. 然后使用索引访问GetProps<T>[PropName]获取特定属性的类型
  3. 最后用NonNullable<T>处理掉null和undefined类型,确保类型安全

使用示例

import { Select } from 'my-ui';
import type { GetProp, SelectProps } from 'my-ui';

// 提取数组选项类型
type SelectOption = GetProp<typeof Select, 'options'>[number];
const option: SelectOption = { label: '选项1', value: '1' };

// 提取事件处理函数类型
const handleChange: GetProp<typeof Select, 'onChange'> = (value) => {
  console.log(value); // 类型安全,不用担心undefined
};

// 以下两种方式均可获取options类型
type SelectOption1 = GetProp<SelectProps, 'options'>[number];
type SelectOption2 = GetProp<typeof Select, 'options'>[number];

实际示例中的应用:

// 在Checkbox组件示例中使用
const onChange: GetProp<typeof Checkbox.Group, 'onChange'> = checkedValues => {
  setCheckedList(checkedValues as string[]);
  console.log('选中的值: ', checkedValues);
};

const onCheckAllChange: GetProp<typeof Checkbox, 'onChange'> = e => {
  setCheckedList(e.target.checked ? plainOptions : []);
  setIndeterminate(false);
  setCheckAll(e.target.checked);
};

3. GetRef - 获取组件的Ref类型

GetRef工具可以提取组件的Ref类型,便于正确地使用ref引用组件实例。

实现原理

type ReactRefComponent<Props extends { ref?: React.Ref<any> | string }> = (props: Props) => React.ReactNode;
type ExtractRefAttributesRef<T> = T extends React.RefAttributes<infer P> ? P : never;

export type GetRef<T extends ReactRefComponent<any> | React.Component<any>> = T extends React.Component<any>
  ? T
  : T extends React.ComponentType<infer P>
  ? ExtractRefAttributesRef<P>
  : never;

该工具的实现过程:

  1. 定义辅助类型ReactRefComponent用于描述函数组件
  2. 定义辅助类型ExtractRefAttributesRef用于从RefAttributes中提取具体的Ref类型
  3. 根据组件类型分别处理类组件和函数组件的ref类型

使用示例

import { Input } from 'my-ui';
import type { GetRef } from 'my-ui';
import { useRef } from 'react';

// 获取Input组件的Ref类型
type InputRef = GetRef<typeof Input>;

// 使用提取的类型
const inputRef = useRef<InputRef>(null);

// 可以安全地访问ref上的方法
const focusInput = () => {
  inputRef.current?.focus();
};

4. GetContextProps/GetContextProp - 获取Context类型

这两个工具用于从React Context中提取类型信息。

实现原理

export type GetContextProps<T> = T extends React.Context<infer P> ? P : never;

export type GetContextProp<
  T extends React.Context<any>,
  PropName extends keyof GetContextProps<T>
> = NonNullable<GetContextProps<T>[PropName]>;

使用示例

import { ThemeContext } from 'my-ui';
import type { GetContextProps, GetContextProp } from 'my-ui';

// 获取Context的Props类型
type ThemeContextProps = GetContextProps<typeof ThemeContext>;

// 获取Theme上下文中的color属性类型
type ThemeColor = GetContextProp<typeof ThemeContext, 'color'>;

应用场景

1. 组件封装与扩展

当需要基于现有组件进行封装或扩展时,可以使用GetProps获取原组件的完整Props类型:

interface MyButtonProps extends GetProps<typeof Button> {
  extraProp?: string;
}

const MyButton: React.FC<MyButtonProps> = (props) => {
  // 实现逻辑
};

2. 类型安全的事件处理

使用GetProp确保事件处理函数具有正确的类型:

// 确保onChange函数的参数类型与组件期望的一致
const handleChange: GetProp<typeof Input, 'onChange'> = (e) => {
  // 类型安全的事件处理
};

3. Ref引用管理

使用GetRef确保正确地使用组件提供的ref方法:

const formRef = useRef<GetRef<typeof Form>>(null);

// 安全地调用ref方法
const validateForm = () => {
  formRef.current?.validate();
};

4. 数组选项类型

从选择类组件中提取选项类型:

// 从Select组件的options属性提取单个选项的类型
type Option = GetProp<typeof Select, 'options'>[number];

// 创建类型安全的选项数组
const options: Option[] = [
  { label: '选项1', value: '1' },
  { label: '选项2', value: '2' }
];