当前位置: 首页 > news >正文

TS React 项目中使用TypeScript

在 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是typescript项目的配置文件,用于配置typescript
  • 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
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 串的存储实现方法(与链表相关)
  • Pybullet 安装过程
  • 面试干货|自动化测试中常见面试题
  • 摆脱困境并在iPhone手机上取回删除照片的所有解决方案
  • 神经网络面试题目
  • Get请求-LocalDateTime的转换问题
  • Python在数据科学与机器学习中的应用
  • 大话Python|基础语法(上)
  • 【监控】【Nginx】使用 Docker 部署 ELK Stack 监控 Nginx
  • Vite + Vue + TypeScript 项目搭建总结
  • 【ComfyUI】自定义节点ComfyUI_LayerStyle——模仿 Adob​​e Photoshop 的图层样式、图层混合、图文混合、添加不可见水印
  • 安卓13去掉下拉菜单的Dump SysUI 堆的选项 android13删除Dump SysUI 堆
  • C语言中数组和字符串的联系
  • OpenAI 刚刚推出 o1 大模型!!突破LLM极限
  • Ruby-SAML CVE-2024-45409 漏洞解决方案
  • ES6指北【2】—— 箭头函数
  • 【comparator, comparable】小总结
  • Bytom交易说明(账户管理模式)
  • chrome扩展demo1-小时钟
  • gitlab-ci配置详解(一)
  • JS 面试题总结
  • Sequelize 中文文档 v4 - Getting started - 入门
  • Spark VS Hadoop:两大大数据分析系统深度解读
  • unity如何实现一个固定宽度的orthagraphic相机
  • vue-router 实现分析
  • 不上全站https的网站你们就等着被恶心死吧
  • 动手做个聊天室,前端工程师百无聊赖的人生
  • 基于Android乐音识别(2)
  • 快速体验 Sentinel 集群限流功能,只需简单几步
  • 力扣(LeetCode)22
  • # 利刃出鞘_Tomcat 核心原理解析(七)
  • #gStore-weekly | gStore最新版本1.0之三角形计数函数的使用
  • #HarmonyOS:Web组件的使用
  • #java学习笔记(面向对象)----(未完结)
  • (10)Linux冯诺依曼结构操作系统的再次理解
  • (javaweb)Http协议
  • (补充)IDEA项目结构
  • (附源码)spring boot公选课在线选课系统 毕业设计 142011
  • (九十四)函数和二维数组
  • (深入.Net平台的软件系统分层开发).第一章.上机练习.20170424
  • (五)Python 垃圾回收机制
  • (转)Sublime Text3配置Lua运行环境
  • (转)真正的中国天气api接口xml,json(求加精) ...
  • ******IT公司面试题汇总+优秀技术博客汇总
  • .babyk勒索病毒解析:恶意更新如何威胁您的数据安全
  • .FileZilla的使用和主动模式被动模式介绍
  • .NET HttpWebRequest、WebClient、HttpClient
  • .NET 发展历程
  • .NET 将混合了多个不同平台(Windows Mac Linux)的文件 目录的路径格式化成同一个平台下的路径
  • .net6解除文件上传限制。Multipart body length limit 16384 exceeded
  • .net反编译的九款神器
  • .NET开发不可不知、不可不用的辅助类(三)(报表导出---终结版)
  • .net连接MySQL的方法
  • .net中我喜欢的两种验证码
  • @angular/cli项目构建--http(2)