2024年React项目中使用TypeScript(最新全方位指南)

一、函数组件

1. 外部声明

js 复制代码
//使用interface或type
interface AppProps {
  message: string;
}

const App = ({ message }: AppProps) => <div>{message}</div>;

2. 内联声明

js 复制代码
const App = ({ message }: { message: string }) => <div>{message}</div>;

3. 使用泛型

js 复制代码
// 定义泛型
interface Props<T> {
  prop1: T;
  prop2: T;
}

// 定义函数组件
const MyComponent: React.FC<Props<string>> = ({ prop1, prop2 }) => {
  return <div></div>;
};

//prop1,prop2被推断为string类型

二、hooks

1. useState

1. 类型推断

指定初始值,无需手动定义类型

js 复制代码
const [current, setCurrent] = useState(1);

current类型:number

setCurrent类型:Dispatch<React.SetStateAction<number>>

2. 传入泛型

显式指定state的类型

js 复制代码
interface User{
  userName:string,
  userId:number,
}

function UserInfo() {
  const [userInfo, setUserInfo] = useState<User | undefined>();
  //userInfo类型为 User 或 undefined
}

3. 不指定初始值取值问题

当泛型包含undefined时,例如useState<User | undefined>,对象取值时会遇到"对象可能未定义"的校验错误

解决方法1:使用可选链操作符 ?

js 复制代码
function UserInfo() {
  const [userInfo, setUserInfo] = useState<User | undefined>();

  return (
    <div>
      {userInfo?.userName}
    </div>
  );
}

解决方法2:初始值给个空对象,并使用类型断言

js 复制代码
const [userInfo, setUserInfo] = useState<User>({} as User);

2. useCallback

函数的类型根据第一个参数进行推断

js 复制代码
const memoizedCallback = useCallback((param1: string, param2: number) => {}, []);

memoizedCallback类型被推断为:

js 复制代码
(param1: string, param2: number) => void

3. useMemo

1. 类型推断

类型根据第一个参数中函数的返回值进行推断

js 复制代码
const str = useMemo(() => 1 + "2", []); //str类型被推断为string

2. 传入泛型

显式指定返回值的类型

js 复制代码
const str = useMemo<number>(() => 1 + "2", []); //报错:不能将类型"string"分配给类型"number"

4. useRef

1. 用来访问DOM节点

给一个初始值null

js 复制代码
const divRef = useRef<HTMLDivElement>(null);

2. 用来清除定时器

js 复制代码
const intervalRef = useRef<NodeJS.Timer>();

useEffect(() => {
    intervalRef.current = setInterval(() => {
      console.log("打印");
    }, 200);
    return () => clearInterval(intervalRef.current);
}, []);

5. useImperativeHandle

类型定义在forwardRef处,在forwardRef中指定props与ref的类型

注意:这里泛型参数传递的参数顺序,ref 在前,props在后

js 复制代码
import { useRef, forwardRef, useImperativeHandle, useState } from "react";

//定义ref类型
interface ForwardObject {
  reset: () => void;
}

const App = () => {
  //给一个初始值null
  const countRef = useRef<ForwardObject>(null);

  const handleClick = () => {
    countRef.current?.reset();
  };

  return (
    <div>
      <Count ref={countRef} />
      <button onClick={handleClick}>重置</button>
    </div>
  );
};

//注意:props,ref类型定义的顺序是反的
const Count = forwardRef<ForwardObject,unknown>((props, ref) => {
  const [count, setCount] = useState(0);

  useImperativeHandle(ref, () => ({
    reset: handleReset,
  }));

  const handleReset = () => {
    setCount(0);
  };

  return (
    <div>
      {count}
      <button onClick={() => setCount(count + 1)}>+1</button>
    </div>
  );
});

export default App;

6. useContext

在使用useContext时,会自动推断出提供的上下文对象的类型,因此类型主要定义在createContext处

1. 类型推断

指定初始值,无需手动定义类型

js 复制代码
const UserContext = createContext({
  name: "张三",
  age: 20,
});

UserContext推断为

js 复制代码
const UserContext: React.Context<{
  name: string;
  age: number;
}>

2. 传入泛型

将空对象作为默认值,使用类型断言转换为预期的上下文类型

js 复制代码
interface UserContextType {
  name: string;
  age: number;
}

const UserContext = createContext<UserContextType>({} as UserContextType);

7. useReducer

两处类型定义:

  1. 使用 typeof 推断初始值state类型

  2. 使用联合类型定义reducer的action类型

js 复制代码
import { useReducer } from "react";

const initialCount = {
  count: 0,
};

//1.定义初始值类型
type State = typeof initialCount;

//2.定义action类型
type ActionType = { type: "increment" } | { type: "decrement" };

function reducer(state: State, action: ActionType) {
  switch (action.type) {
    case "increment":
      return { count: state.count + 1 };
    case "decrement":
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
}

const App = () => {
  const [state, dispatch] = useReducer(reducer, initialCount);
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({ type: "decrement" })}>-</button>
      <button onClick={() => dispatch({ type: "increment" })}>+</button>
    </>
  );
};

export default App;

8. useEffect / useLayoutEffect

useEffect与useLayoutEffect都用于执行副作用,不需要手动定义类型

三、内置类型

1. 样式属性类型

在React中,当想给子组件传递一个style内联样式时,可使用React.CSSProperties

js 复制代码
interface Props{
  style?:React.CSSProperties
}

2. 子元素类型

1. React.ReactNode

在 JSX 中作为子元素传递的所有可能类型的并集,可以是string,number,也可以是ReactElement,ReactNodeArray

js 复制代码
interface Props{
  children?: React.ReactNode;
}

2. React.ReactElement

只包括 JSX 元素,不包括 JavaScript 原始类型,如 string 或 number。常用来定义函数组件返回值类型

js 复制代码
interface Props{
  children?: React.ReactElement;
}

3. 获取组件props类型

如果子组件使用内联类型定义props,而父组件需要子组件props类型时,可使用React.ComponentProps推断子组件的props类型

js 复制代码
const Children = ({ message }: { message: string }) => <div>{message}</div>;

type PropsType = React.ComponentProps<typeof Children>;

PropsType类型为

js 复制代码
type PropsType = {
  message: string;
}

四、事件处理

1. event 事件类型

1. onclick事件

将HTMLButtonElement作为泛型参数传入React.MouseEvent,表示button标签的鼠标事件

js 复制代码
interface Props{
  onClick:(event:React.MouseEvent<HTMLButtonElement>)=>void
}

2. onchange事件

将HTMLInputElement作为泛型参数传入React.ChangeEvent,表示input标签的表单域值变更事件

js 复制代码
function App() {
  const [value, setValue] = useState("");

  const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setValue(event.target.value);
  };

  return <input value={value} onChange={handleChange} />;
}

3. 常用event事件类型

情况 类别
React.MouseEvent 鼠标事件
React.ChangeEvent change事件
React.keyboardEvent 键盘事件
React.DragEvent 拖拽事件
React.FocusEvent 焦点事件
React.FormEvent 表单事件
React.WheelEvent 鼠标滚动事件
React.TouchEvent 触摸事件
React.ClipboardEvent 剪贴板事件
React.AnimationEvent 动画事件
React.TransitionEvent 过渡事件

4. HTML标签与类型映射关系

常见的标签及其类型如下:

js 复制代码
interface HTMLElementTagNameMap {
  "a": HTMLAnchorElement;
  "article": HTMLElement;
  "aside": HTMLElement;
  "audio": HTMLAudioElement;
  "body": HTMLBodyElement;
  "br": HTMLBRElement;
  "button": HTMLButtonElement;
  "canvas": HTMLCanvasElement;
  "caption": HTMLTableCaptionElement;
  "code": HTMLElement;
  "col": HTMLTableColElement;
  "colgroup": HTMLTableColElement;
  "dd": HTMLElement;
  "del": HTMLModElement;
  "dialog": HTMLDialogElement;
  "div": HTMLDivElement;
  "dl": HTMLDListElement;
  "dt": HTMLElement;
  "em": HTMLElement;
  "footer": HTMLElement;
  "form": HTMLFormElement;
  "h1": HTMLHeadingElement;
  "h2": HTMLHeadingElement;
  "h3": HTMLHeadingElement;
  "h4": HTMLHeadingElement;
  "h5": HTMLHeadingElement;
  "h6": HTMLHeadingElement;
  "head": HTMLHeadElement;
  "header": HTMLElement;
  "html": HTMLHtmlElement;
  "i": HTMLElement;
  "iframe": HTMLIFrameElement;
  "img": HTMLImageElement;
  "input": HTMLInputElement;
  "label": HTMLLabelElement;
  "legend": HTMLLegendElement;
  "li": HTMLLIElement;
  "link": HTMLLinkElement;
  "main": HTMLElement;
  "map": HTMLMapElement;
  "mark": HTMLElement;
  "menu": HTMLMenuElement;
  "meta": HTMLMetaElement;
  "meter": HTMLMeterElement;
  "nav": HTMLElement;
  "ol": HTMLOListElement;
  "option": HTMLOptionElement;
  "p": HTMLParagraphElement;
  "picture": HTMLPictureElement;
  "progress": HTMLProgressElement;
  "script": HTMLScriptElement;
  "section": HTMLElement;
  "select": HTMLSelectElement;
  "small": HTMLElement;
  "source": HTMLSourceElement;
  "span": HTMLSpanElement;
  "strong": HTMLElement;
  "style": HTMLStyleElement;
  "sub": HTMLElement;
  "table": HTMLTableElement;
  "tbody": HTMLTableSectionElement;
  "template": HTMLTemplateElement;
  "textarea": HTMLTextAreaElement;
  "tfoot": HTMLTableSectionElement;
  "thead": HTMLTableSectionElement;
  "time": HTMLTimeElement;
  "title": HTMLTitleElement;
  "tr": HTMLTableRowElement;
  "track": HTMLTrackElement;
  "u": HTMLElement;
  "ul": HTMLUListElement;
  "video": HTMLVideoElement;
}

2. 事件处理函数类型

1. onchange事件

除了使用Event事件类型,还可以使用React提供的事件处理函数类型,例如上面input的change事件

js 复制代码
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
  setValue(event.target.value);
};

可以写成

js 复制代码
const handleChange: React.ChangeEventHandler<HTMLInputElement> = (event) => {
  setValue(event.target.value);
};

event事件对象被自动推断为:React.ChangeEvent<HTMLInputElement>类型

2. 常用事件处理函数类型

情况 类别
React.MouseEventHandler 鼠标事件
React.ChangeEventHandler change事件
React.KeyboardEventHandler 键盘事件
React.DragEventHandler 拖拽事件
React.FocusEventHandler 焦点事件
React.FormEventHandler 表单事件
React.WheelEventHandler 鼠标滚动事件
React.TouchEventHandler 触摸事件
React.ClipboardEventHandler 剪贴板事件
React.AnimationEventHandler 动画事件
React.TransitionEventHandler 过渡事件

五、工具泛型

Typescript提供的工具泛型能提高类型的复用率,减少很多类型定义,这里列举一些常用的

1. Partial<T>

将所有属性都变为可选属性

js 复制代码
interface Person{
  name: string;
  age: number;
}

type PersonPartial = Partial<Person>;

/**
* type PersonPartial = {
*  name?: string;
*  age?: number;
* }
*/    

2. Required<T>

将所有可选属性都变为必选属性

js 复制代码
interface Props {
  a?: number;
  b?: string;
};

const obj: Props = { a: 5 }; // OK

const obj2: Required<Props> = { a: 5 }; // Error: property 'b' missing

3. Readonly<T>

将所有属性都变为只读属性

js 复制代码
type ReadonlyPerson = Readonly<Person>;

4. Pick<T,K>

选取部分属性

js 复制代码
interface Todo {
  title: string;
  description: string;
  done: boolean;
}

type TodoBase = Pick<Todo, "title" | "done">;

// =
type TodoBase = {
  title: string;
  done: boolean;
};

5. Omit<T,K>

去除部分属性,适合接口

js 复制代码
interface User {
  id: string;
  name: string;
  email: string;
};
type UserWithoutEmail = Omit<User, "email">;
// UserWithoutEmail ={id: string;name: string;}

6. Record<T,K>

定义一个对象的key和value类型

js 复制代码
Record<string,any>

//等价于

{
  [key: string]: any;
}
js 复制代码
interface PageInfo {
  title: string;
}

type Page = 'home' | 'about' | 'contact';

const x: Record<Page, PageInfo> = {
  about: { title: 'about' },
  contact: { title: 'contact' },
  home: { title: 'home' },
};

7. Exclude<T,U>

剔除某些类型成员,适用于字面量类型

js 复制代码
type A = 'get' | 'put' | 'post';

type B = Exclude<A, 'get' | 'post'>; //type B = "put"

8. Extract<T,U>

提取某些属性,适用于联合类型

js 复制代码
type MyUnion = 'a' | 'b' | 'c' | 'd';
type Extracted = Extract<MyUnion, 'a' | 'c'>;

// 提取出 'a' 和 'c',得到类型 'a' | 'c'

9. ReturnType<T>

获取函数的返回值类型

js 复制代码
function test(name:string,idx:number){
  return {
    name,idx
  }
}

type A = typeof test;
//type A = (name: string, idx: number) => {
//  name: string;
//  idx: number;
// }

type TestReturnType = ReturnType<typeof test>
// type TestReturnType = {
//    name: string;
//   idx: number;
// }

10. Parameters<T>

获取函数类型的参数类型

js 复制代码
function test(name:string,idx:number){
  return {
    name,idx
  }
}

type TestArgsType = Parameters<typeof test>
//type TestArgsType = [name: string, idx: number]

11. NonNullable<T>

剔除 null 和 undefined 类型

js 复制代码
type NullableString = string | null | undefined;

type NonNullableString = NonNullable<NullableString>;

// NonNullableString 的类型为 string
相关推荐
昨天;明天。今天。2 分钟前
案例-任务清单
前端·javascript·css
zqx_71 小时前
随记 前端框架React的初步认识
前端·react.js·前端框架
惜.己1 小时前
javaScript基础(8个案例+代码+效果图)
开发语言·前端·javascript·vscode·css3·html5
什么鬼昵称2 小时前
Pikachu-csrf-CSRF(get)
前端·csrf
长天一色2 小时前
【ECMAScript 从入门到进阶教程】第三部分:高级主题(高级函数与范式,元编程,正则表达式,性能优化)
服务器·开发语言·前端·javascript·性能优化·ecmascript
NiNg_1_2342 小时前
npm、yarn、pnpm之间的区别
前端·npm·node.js
秋殇与星河2 小时前
CSS总结
前端·css
BigYe程普3 小时前
我开发了一个出海全栈SaaS工具,还写了一套全栈开发教程
开发语言·前端·chrome·chatgpt·reactjs·个人开发
余生H3 小时前
前端的全栈混合之路Meteor篇:关于前后端分离及与各框架的对比
前端·javascript·node.js·全栈
程序员-珍3 小时前
使用openapi生成前端请求文件报错 ‘Token “Integer“ does not exist.‘
java·前端·spring boot·后端·restful·个人开发