TS React 项目中使用TypeScript

server/2024/9/23 3:56:00/

在 React 项目中使用 TS

  1. 创建新项目
  1. 在现有项目中添加 TS

创建新项目

  • 命令:npx create-react-app my-app --template typescript
  • 说明:在命令行中,添加 --template typescript 表示创建支持 TS 的项目
  • 项目目录的变化:
    1. 在项目根目录中多了一个文件:tsconfig.json
      • TS 的配置文件
    1. 在 src 目录中,文件的后缀有变化,由原来的 .js 变为 .ts.tsx
      • .ts ts 文件的后缀名
      • .tsx 是在 TS 中使用 React 组件时,需要使用该后缀
    1. 在 src 目录中,多了 react-app-env.d.ts 文件
      • .d.ts 类型声明文件,用来指定类型

基本使用

创建类组件

在vscode中通过tsrcc快速创建类组件

import React, { Component } from 'react'type Props = {}type State = {}export default class App extends Component<Props, State> {state = {}render() {return (<div>App</div>)}
}

其中泛型Props指外部数据的数据类型

State指内部数据的数据类型。

创建函数组件

定义函数组件第一种方式:

在vscode中通过tsrfc快速创建函数组件

import React from 'react'type Props = {}export default function Header({}: Props) {return (<div>Header</div>)
}

Props:指外部数据的数据类型

第二种方式:

import { FC } from 'react';type Props = {}// FC:函数组件
const Nav: FC<Props> = function() {return <div></div>
}

外部数据

简单的数据类型定义
import React, { Component } from 'react'type Props = {msg: string
}type State = {}export default class Footer extends Component<Props, State> {state = {}render() {return (<div>消息: {this.props.msg}</div>)}
}

复杂数据类型定义

定义复杂数据类型后,可以导出数据类型方便其他组件引入使用。

import React, { Component } from 'react'export interface User {name: string,age: number
}export type UserList = User[];type Props = {msg: string,user: User,userList: UserList
}type State = {}export default class Footer extends Component<Props, State> {state = {}render() {return (<div>消息: {this.props.msg}<br />姓名:{this.props.user.name}年龄:{this.props.user.age}</div>)}
}

在父组件引入数据类型使用

import React, { Component } from 'react'
import Footer, { User, UserList } from './components/Footer'type Props = {}type State = {}const user: User = {name: '张三', age: 20}
const userList: UserList = [{name: '李四', age: 30}];export default class App extends Component<Props, State> {state = {}render() {return (<div><Footer msg={'消息'} user={user} userList={userList} /></div>)}
}

内部数据

类组件的内部数据State

内部数据通过泛型传入State数据类型。后续使用中提示更加友好。

import React, { Component } from 'react'type Props = {
}type State = {address: string
}export default class Footer extends Component<Props, State> {state = {address: '红旗河沟'}changeAddr = () => {this.setState({address: '渝北区'})}render() {return (<div>地址:{this.state.address}<button onClick={this.changeAddr}>修改地址</button></div>)}
}

函数组件的内部数据State

在函数组件中通过useState创建内部数据

在创建某些复杂数据时,要注意显示去传入state的泛型数据类型,否则数据类型很容容易报错。

import React, { useEffect } from 'react'
import { useState } from 'react';type Props = {}interface User {name: string, age: number}export default function Header(props: Props) {let [count, setCount] = useState(0);let [user, setUser] = useState<User>({} as any);let [userList, setUserList] = useState<User[]>([]);function changeCount() {setCount(10);}function changeUserList() {setUserList([{name: '张三',age: 20}]);}return (<div>count:{count}<button onClick={changeCount}>修改count</button><br />姓名:{user.name}年龄:{user.age}<button onClick={changeUserList}>修改userList</button></div>)
}

对父子通信进行类型限定

首先让脚手架支持TypeScript,可以在安装脚手架的时候进行配置即可,命令如下。

npx create-react-app react-ts-study --template typescript

然后就是创建两个组件,并且完成props通信。

import React from 'react'
interface WelcomeProps {msg?: stringcount?: numberlist: string[]info: { username: string; age: number }status?: 'loading' | 'success' | 'error'
}function Welcome(props: WelcomeProps) {const { count = 0 } = props;return (<div><h2>hello Welcome, {count}</h2></div>)
}
export default function App() {return (<div><h2>01_react-ts</h2><Welcome msg="hello" count={123} list={['a', 'b', 'c']} info={{username: 'xiaoming', age: 20}} /><Welcome list={['a', 'b', 'c']} info={{username: 'xiaoming', age: 20}} /><Welcome status="loading" list={['a', 'b', 'c']} info={{username: 'xiaoming', age: 20}} /></div>)
}

下面来看一下函数表达式写法的情况下,如何指定props的类型,可通过内置的FC类型来进行实现。

const Welcome: React.FC<WelcomeProps> = (props) => {return (<div><h2>hello Welcome</h2></div>)
}

children与event限制

children的类型限制

父子通信时候的内容分发进行限制。

import React from 'react'
interface WelcomeProps {children?: React.ReactNode
}
function Welcome(props: WelcomeProps) {return (<div><h2>hello Welcome, {props.children}</h2></div>)
}
export default function App() {return (<div><h2>02_react-ts</h2><Welcome /><Welcome>aaaaa</Welcome></div>)
}

我们把children属性作为可选参数,这样当<Welcome>组件进行内容分发和不进行内容分发都是可以的。

event限制

event在React中主要通过内置的ev: React.MouseEvent<HTMLButtonElement>来进行限定。

import React from 'react'
interface WelcomeProps {children?: React.ReactNodehandleMsg?: (ev: React.MouseEvent<HTMLButtonElement>)=> void
}
function Welcome(props: WelcomeProps) {const handleChange = (ev: React.ChangeEvent<HTMLInputElement>) => {console.log(ev.target.value)}return (<div><h2>hello Welcome, {props.children}</h2><button onClick={props.handleMsg}>点击</button><input type="text" onChange={handleChange} /></div>)
}
export default function App() {return (<div><h2>02_react-ts</h2><Welcome /><Welcome handleMsg={(ev)=>{}}>aaaaa</Welcome></div>)
}

props.children问题

在tsx中props中要访问children,那么应该使用PropsWithChildren去定义props数据类型

import React, { useEffect } from 'react'
import { useState, PropsWithChildren } from 'react';type Props = {}export default function Header(props: PropsWithChildren<Props>) {return (<div{props.children}</div>)
}

PropsWithChildren是一个数据类型,接口泛型Props数据类型,然后得到一个注入了children数据类型的Props数据类型。

通过FC创建的函数组件的props也没有children属性,也需要使用PropsWithChildren去定义

import { FC, PropsWithChildren } from 'react';type Props = {}// FC:函数组件
const Nav: FC<PropsWithChildren<Props>> = function(props) {return <div>{props.children}</div>
}

style与component限制

style限制

当我们进行style样式通信的时候,也是可以指定类型,防止样式传递的时候不复合规范。

import React from 'react'
interface HeaderProps {username: string
}
interface WelcomeProps {style: React.CSSProperties
}
function Welcome(props: WelcomeProps) {return (<div><h2>hello Welcome</h2></div>)
}
export default function App() {return (<div><h2>03_react-ts</h2><Welcome style={{'border': '1px red solid', display: 'none'}} /></div>)
}

主要通过React.CSSProperties来指定样式的类型,这样当传递的样式属性或者值不符合规范的时候,就不会产生TS的提示。

component限制

如果组件进行通信的时候,也可以进行类型的限制。

import React from 'react'
interface HeaderProps {username: string
}
interface WelcomeProps {style: React.CSSPropertiescomponent: React.ComponentType<HeaderProps>
}
function Welcome(props: WelcomeProps) {return (<div><h2>hello Welcome</h2><props.component username="xiaoming"></props.component></div>)
}
function Header(props: HeaderProps) {return (<div>hello Header</div>)
}
export default function App() {return (<div><h2>03_react-ts</h2><Welcome style={{'border': '1px red solid', display: 'none'}} component={Header} /></div>)
}

主要通过React.ComponentType<>来指定组件的类型,那么一旦不符合指定的接口类型,就会报错。
 

use函数限制

在React函数组件中,主要就是对use函数进行类型的注解。常见的注解use函数如下:

  • useState -> 联合类型、对象字面量类型
  • useEffect -> 自动类型推断
  • useRef -> 泛型标签类型
import React, { useEffect, useState, useRef } from 'react'
interface WelcomeProps {
}
function Welcome(props: WelcomeProps) {return (<div><h2>hello Welcome</h2></div>)
}
type Info = {username: string; age: number}
export default function App() {//const [count, setCount] = useState(0)const [count, setCount] = useState<number|string>(0)const [list, setList] = useState<string[]>([])//const [info, setInfo] = useState<{username: string; age: number}|null>(null)const [info, setInfo] = useState<Info>({} as Info)const myRef = useRef<HTMLButtonElement>(null)useEffect(()=>{console.log( myRef.current?.innerHTML )  // 可选链(类型保护)//console.log( myRef.current!.innerHTML )  // 非空断言(慎用)   return ()=>{}}, [])const handleClick = () => {setCount(1)setList(['a', 'b'])}return (<div><h2>04_react-ts</h2><button onClick={handleClick} ref={myRef}>点击</button>{ info.username }, { info.age }<Welcome />      </div>)
}

useState和useRef都是通过泛型的方式进行类型注解,useEffect主要利用自动类型推断来完成。

类组件类型限制

类组件在React中并不是重点,但是也要了解怎么对类组件进行类型的限制。

import React, { Component } from 'react'
interface WelcomeProps {msg: stringcount: number
}
interface WelcomeState {username: string
}
class Welcome extends Component<WelcomeProps, WelcomeState> {state = {username: 'xiaoming'}render() {return (<div>hello Welcome {this.state.username}</div>)}
}
export default function App() {return (<div><h2>05_react-ts</h2><Welcome msg="hello" count={123} />      </div>)
}

主要就是给继承的类Component传递泛型,Props和State,这样可以实现父子通信的数据进行类型限制,又可以对内部的state进行类型限制。
 

路由如何使用TS进行开发

react-router-dom类型限制

React路由与TS配合常见的使用为以下这些操作:

  • RouteObject 内置类型,限制路由表
  • React.createElement() 进行组件编写
  • 扩展 meta 元信息
// /router/index.ts
import { createBrowserRouter } from 'react-router-dom'
import type { RouteObject } from 'react-router-dom'
import App from '../App';
import Index from '../views/Index/Index';
import User from '../views/User/User';
import Login from '../views/Login/Login';
import React from 'react';
declare module 'react-router' {interface NonIndexRouteObject {meta?: { title: string }}interface IndexRouteObject {meta?: { title: string }}
}
export const routes: RouteObject[] = [{path: '/',element: React.createElement(App),meta: { title: '/' },children: [{path: 'index',element: React.createElement(Index),meta: { title: 'index' }},{path: 'user',element: React.createElement(User),meta: { title: 'user' }},{path: 'login',element: React.createElement(Login)}]}
];
const router = createBrowserRouter(routes);
export default router;

状态管理如何使用TS进行开发

Redux Toolkit限制类型

Redux状态管理与TS配合常见的使用为以下这些操作:

  • 得到全局state类型: ReturnType<typeof store.getState>
  • 限定payload类型: PayloadAction
// /store/index.ts
import { configureStore } from '@reduxjs/toolkit'
import userReducer from './modules/user';
import { useDispatch } from 'react-redux'
const store = configureStore({reducer: {user: userReducer}
})
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
export const useAppDispatch: () => AppDispatch = useDispatch
export default store;
// /store/modules/user.ts
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
import type { PayloadAction } from '@reduxjs/toolkit'
export const loginAction = createAsyncThunk('users/loginAction',async (userId: number) => {const response = await new Promise((resolve)=>{resolve('response data')})return response}
)
const userSlice = createSlice({name: 'user',initialState: {name: 'xiaoming'},reducers: {change(state, action: PayloadAction<string>){state.name = action.payload}}
})
export const { change } = userSlice.actions
export default userSlice.reducer

tsconfig的介绍

  • tsconfig.json配置文件可以通过 tsc --init 生成
  • 说明:所有的配置项都可以通过鼠标移入的方式,来查看配置项的解释说明。
  • tsconfig 文档链接
{// 编译选项"compilerOptions": {// 生成代码的语言版本:将我们写的 TS 代码编译成哪个版本的 JS 代码// 命令行: tsc --target es5 11-测试TS配置文件.ts"target": "es5",// 指定要包含在编译中的 library"lib": ["dom", "dom.iterable", "esnext"],// 允许 ts 编译器编译 js 文件"allowJs": true,// 跳过类型声明文件的类型检查"skipLibCheck": true,// es 模块 互操作,屏蔽 ESModule 和 CommonJS 之间的差异"esModuleInterop": true,// 允许通过 import x from 'y' 即使模块没有显式指定 default 导出"allowSyntheticDefaultImports": true,// 开启严格模式"strict": true,// 对文件名称强制区分大小写"forceConsistentCasingInFileNames": true,// 为 switch 语句启用错误报告"noFallthroughCasesInSwitch": true,// 生成代码的模块化标准"module": "esnext",// 模块解析(查找)策略"moduleResolution": "node",// 允许导入扩展名为.json的模块"resolveJsonModule": true,// 是否将没有 import/export 的文件视为旧(全局而非模块化)脚本文件"isolatedModules": true,// 编译时不生成任何文件(只进行类型检查)"noEmit": true,// 指定将 JSX 编译成什么形式"jsx": "react-jsx"},// 指定允许 ts 处理的目录"include": ["src"]
}

typescript声明文件

今天几乎所有的 JavaScript 应用都会引入许多第三方库来完成任务需求。

这些第三方库不管是否是用 TS 编写的,最终都要编译成 JS 代码,才能发布给开发者使用。

我们知道是 TS 提供了类型,才有了代码提示和类型保护等机制。

但在项目开发中使用第三方库时,你会发现它们几乎都有相应的 TS 类型,这些类型是怎么来的呢? 类型声明文件

  • 类型声明文件:用来为已存在的 JS 库提供类型信息

这样在 TS 项目中使用这些库时,就像用 TS 一样,都会有代码提示、类型保护等机制了。

  1. TS 的两种文件类型
  1. 类型声明文件的使用说明

TS 中的两种文件类型

  • TS 中有两种文件类型:1 .ts 文件 2 .d.ts 文件
  • .ts 文件:
    1. 既包含类型信息又可执行代码
    2. 可以被编译为 .js 文件,然后,执行代码
    3. 用途:编写程序代码的地方
  • .d.ts 文件:
    1. 只包含类型信息的类型声明文件
    2. 不会生成 .js 文件,仅用于提供类型信息,在.d.ts文件中不允许出现可执行的代码,只用于提供类型
    3. 用途:为 JS 提供类型信息
  • 总结:.ts 是 implementation(代码实现文件);.d.ts 是 declaration(类型声明文件)
  • 如果要为 JS 库提供类型信息,要使用 .d.ts 文件

类型声明文件的使用说明

  • 在使用 TS 开发项目时,类型声明文件的使用包括以下两种方式:
    1. 使用已有的类型声明文件
    2. 创建自己的类型声明文件

使用已有的类型声明文件

  1. 内置类型声明文件
  1. 第三方库的类型声明文件
  1. 自己提供的

内置类型声明文件

  • TS 为 JS 运行时可用的所有标准化内置 API 都提供了声明文件
  • 比如,在使用数组时,数组所有方法都会有相应的代码提示以及类型信息:
const strs = ['a', 'b', 'c']
// 鼠标放在 forEach 上查看类型
strs.forEach
  • 实际上这都是 TS 提供的内置类型声明文件
  • 可以通过 Ctrl + 鼠标左键(Mac:Command + 鼠标左键)来查看内置类型声明文件内容
  • 比如,查看 forEach 方法的类型声明,在 VSCode 中会自动跳转到 lib.es5.d.ts 类型声明文件中
  • 当然,像 window、document 等 BOM、DOM API 也都有相应的类型声明(lib.dom.d.ts)

第三方库的类型声明文件

  • 目前,几乎所有常用的第三方库都有相应的类型声明文件
  • 第三方库的类型声明文件有两种存在形式:1 库自带类型声明文件 2 由 DefinitelyTyped 提供。

  1. 库自带类型声明文件:比如,axios
    • 查看 node_modules/axios 目录

解释:这种情况下,正常导入该库,TS 就会自动加载库自己的类型声明文件,以提供该库的类型声明。

  1. 由 DefinitelyTyped 提供
  • DefinitelyTyped 是一个 github 仓库,用来提供高质量 TypeScript 类型声明
  • DefinitelyTyped 链接
  • 可以通过 npm/yarn 来下载该仓库提供的 TS 类型声明包,这些包的名称格式为:@types/*
  • 比如,@types/react、@types/lodash 等
  • 说明:在实际项目开发时,如果你使用的第三方库没有自带的声明文件,VSCode 会给出明确的提示

import _ from 'lodash'// 在 VSCode 中,查看 'lodash' 前面的提示
  • 解释:当安装 @types/* 类型声明包后,TS 也会自动加载该类声明包,以提供该库的类型声明
  • 补充:TS 官方文档提供了一个页面,可以来查询 @types/* 库
  • @types/* 库

创建自己的类型声明文件

  1. 项目内共享类型
  1. 为已有 JS 文件提供类型声明

项目内共享类型

  • 如果多个 .ts 文件中都用到同一个类型,此时可以创建 .d.ts 文件提供该类型,实现类型共享。
  • 操作步骤:
    1. 创建 index.d.ts 类型声明文件。
    2. 创建需要共享的类型,并使用 export 导出(TS 中的类型也可以使用 import/export 实现模块化功能)。
    3. 在需要使用共享类型的 .ts 文件中,通过 import 导入即可(.d.ts 后缀导入时,直接省略)。

为已有 JS 文件提供类型声明

  1. 在将 JS 项目迁移到 TS 项目时,为了让已有的 .js 文件有类型声明。
  1. 成为库作者,创建库给其他人使用。

  • 注意:类型声明文件的编写与模块化方式相关,不同的模块化方式有不同的写法。但由于历史原因,JS 模块化的发展 经历过多种变化(AMD、CommonJS、UMD、ESModule 等),而 TS 支持各种模块化形式的类型声明。这就导致 ,类型声明文件相关内容又多又杂。
  • 演示:基于最新的 ESModule(import/export)来为已有 .js 文件,创建类型声明文件。

类型声明文件的使用说明

  • 说明:TS 项目中也可以使用 .js 文件。
  • 说明:在导入 .js 文件时,TS 会自动加载与 .js 同名的 .d.ts 文件,以提供类型声明。
  • declare 关键字:用于类型声明,为其他地方(比如,.js 文件)已存在的变量声明类型,而不是创建一个新的变量。
    1. 对于 type、interface 等这些明确就是 TS 类型的(只能在 TS 中使用的),可以省略 declare 关键字。
    2. 对于 let、function 等具有双重含义(在 JS、TS 中都能用),应该使用 declare 关键字,明确指定此处用于类型声明。
let count = 10
let songName = '痴心绝对'
let position = {x: 0,y: 0
}function add(x, y) {return x + y
}function changeDirection(direction) {console.log(direction)
}const fomartPoint = point => {console.log('当前坐标:', point)
}export { count, songName, position, add, changeDirection, fomartPoint }

定义类型声明文件

declare let count:numberdeclare let songName: stringinterface Position {x: number,y: number
}declare let position: Positiondeclare function add (x :number, y: number) : numbertype Direction = 'left' | 'right' | 'top' | 'bottom'declare function changeDirection (direction: Direction): voidtype FomartPoint = (point: Position) => voiddeclare const fomartPoint: FomartPointexport {count, songName, position, add, changeDirection, FomartPoint, fomartPoint
}

http://www.ppmy.cn/server/120610.html

相关文章

C语言:冒泡排序的注意事项及具体实现

一、注意事项 1、函数声明为&#xff1a;void bubble_sort(void* base, size_t num, size_t width, int (*cmp)(const void* e1, const void* e2)); 2、base 指向所要排序的数组 3、num 为数组的元素个数 4、width 为一个元素占多少个字节的空间 5、cmp 为函数指针&#xff0c;…

Java中stream流及Collectors的常见用法详细汇总!!!

目录 1. Stream流的优势 2. 常用用法 2.1 中间操作 2.1.1filter() 2.1.2 map() 2.1.3 sorted() 2.1.4 distinct() 2.1.5 limit() 2.1.6 skip() 2.2 终端操作 2.2.1 foreach() 2.2.2 collect() 2.2.3 reduce() 2.2.4 count() 2.2.5 anyMatch() 2.3 查找和匹配…

动态IP是如何实现的?

相信很多人都知道动态IP和静态IP的区别&#xff0c;但对于动态IP的实现过程或许还有些疑惑。那么接下来就为大家介绍一下动态IP的实现过程&#xff01; 动态IP的实现离不开一个关键协议&#xff0c;那就是DHCP。DHCP协议的主要任务就是在网络中自动分配IP地址&#xff0c;让设…

使用cmd命令窗口操作mongodb

一、效果显示 二、下载MongoDB 1. 在官网下载安装MongoDB 官网网址&#xff1a;Download MongoDB Community Server | MongoDB 我安装的版本是7.0.14(注意安装到空闲磁盘) 三、启动MongoDB服务 1. 配置环境变量 注意替换为你的路径。 2. 在MongoDB的data下创建db文件夹 在…

运维工程师面试整理-操作系统

在运维工程师的面试中,操作系统相关的知识通常是重中之重,尤其是Linux/Unix系统。以下是针对操作系统部分的一些详细内容,帮助你更好地准备面试。 1. Linux/Unix 基础 ● 常用命令 ○ 文件和目录管理:ls, cd, cp, mv, rm, mkdir, rmdir, find, grep, awk, sed

CentOS入门宝典:从零到一构建你的Linux服务器帝国

目录 引言 一、CentOS简介与版本选择 1.1 CentOS是什么&#xff1f; 1.2 版本选择 二、安装CentOS 2.1 准备安装介质 2.2 安装过程 三、基础配置与优化 3.1 更新系统 3.2 配置防火墙 3.3 配置SELinux 3.4 系统监控与日志 四、网络配置与管理 4.1 配置静态IP 4.…

华南理工大学信息工程高频电子线路课程设计——基于锁相环的调试解调器设计

完整报告链接如下&#xff1a; 通过网盘分享的文件&#xff1a;高频课设报告.docx 链接: https://pan.baidu.com/s/1J83UCDSU0UHcv4ONYxfyhg?pwdzqyr 提取码: zqyr --来自百度网盘超级会员v5的分享 懒得贴上来了&#xff0c;放一下截图。

SQL进阶技巧:火车票相邻座位预定一起可能情况查询算法 ?

目录 0 场景描述 1 数据准备 2 问题分析 2.1 分析函数法 2.2 自关联求解 3 小结