React和TypeScript集合使用的重点集中在 存储数据/状态有关的Hook函数以及组件接口的位置,这些地方最需要数据类型校验
Vite是前端工具链工具,可以帮助我们快速创建一个 react+ts
的工程化环境出来
Vite官网:https://cn.vitejs.dev/
npm create vite@latest react-typescript -- --template react-ts
项目名称 模版模式
# 安装依赖
npm i
# 运行项目
npm run dev
简单场景下,可以使用TS的自动推断机制,不用特殊编写类型注解,运行良好
const [val, toggle] = React.useState(false)
// `val` 会被自动推断为布尔类型
// `toggle` 方法调用时只能传入布尔类型
// react + ts
// 根据初始值自动推断
// 场景:明确的初始值
import { useState } from 'react'
function App() {
const [value, toggle] = useState(false)
const [list, setList] = useState([1, 2, 3])
const changeValue = () => {
toggle(true)
}
const changeList = () => {
setList([4])
}
return <>this is app {list}</>
}
export default App
useState本身是一个泛型函数,可以传入具体的自定义类型
// react + ts
type User = {
name: string
age: number
}
const [user, setUser] = useState<User>({
name: 'jack',
age: 18,
})
说明:
1.限制useState函数参数的初始值必须满足类型为:user | ()=>user
2.限制setUser函数的参数必须满足类型为: User | ()=> User | underfined
3.user状态数据具备User类型相关的类型提示
// react + ts
import { useState } from 'react'
type User = {
name: string
age: number
}
function App() {
// 1. 限制初始值的类型
// const [user, setUser] = useState({
// name: 'jack',
// age: 18,
// })
// const [user, setUser] = useState(() => {
// return {
// name: 'jack',
// age: 18,
// }
// })
const [user, setUser] = useState<User>({
name: 'jack',
age: 18,
})
const changeUser = () => {
setUser(() => ({
name: 'john',
age: 28,
}))
}
return <>this is app {user.name}</>
}
export default App
实际开发时,有些时候useState的初始值可能为null或者undefined,按照泛型的写法是不能通过类型校验的,此时可以通过完整的类型联合null或者undefined类型即可
type User = {
name: String
age: Number
}
const [user, setUser] = React.useState<User>(null)
// 上面会类型错误,因为null并不能分配给User类型
const [user, setUser] = React.useState<User | null>(null)
// 上面既可以在初始值设置为null,同时满足setter函数setUser的参数可以是具体的User类型
说明:
1.限制useState函数参数的初始值可以是User | null
2.限制setUser函数的参数类型可以是User | null
// react + ts
import { useState } from 'react'
type User = {
name: string
age: number
}
function App() {
const [user, setUser] = useState<User | null>(null)
const changeUser = () => {
setUser(null)
setUser({
name: 'jack',
age: 18,
})
}
// 为了类型安全 可选链做类型守卫
// 只有user不为null(不为空值)的时候才进行点运算
return <>this is app {user?.age}</>
}
export default App
为组件prop添加类型,本质是给函数的参数做类型注解,可以使用type对象类型或者interface接口来做注解
说明:button组件只能传入名称为className的prop参数,类型为string,且为必填
// props + ts
//写法一
// type Props = {
// className: string
// }
//写法二
interface Props {
className: string
title?: string
}
function Button(props: Props) {
const { className } = props
return <button className={className}>click me </button>
}
function App() {
return (
<>
<Button className="test" title="this is title" />
</>
)
}
export default App
children是一个比较特殊的prop,支持多种不同类型数据的传入,需要通过一个内置的ReactNode类型来做注解
说明:注解之后,children可以是多种类型,包括:react.reactElement、string、number、react.reactFragment、react.reactPortal、boolean、null、undefined
// props + ts
type Props = {
className: string
children: React.ReactNode
}
function Button(props: Props) {
const { className, children } = props
return <button className={className}>{children} </button>
}
function App() {
return (
<>
<Button className="test">click me!</Button>
<Button className="test">
<span>this is span</span>
</Button>
</>
)
}
export default App
组件经常执行类型为函数的prop实现子传父,这类prop重点在于函数参数类型的注解
说明:
1.组件内部调用时需要遵守类型的约束,参数传递需要满足要求
2.绑定prop时如果绑定内联函数直接可以推断出参考类型,否则需要单独注解匹配的参数类型
// props + ts
type Props = {
onGetMsg?: (msg: string) => void
}
function Son(props: Props) {
const { onGetMsg } = props
const clickHandler = () => {
onGetMsg?.('this is msg')
}
return <button onClick={clickHandler}>sendMsg</button>
}
function App() {
const getMsgHandler = (msg: string) => {
console.log(msg)
}
return (
<>
<Son onGetMsg={(msg) => console.log(msg)} />
<Son onGetMsg={getMsgHandler} />
</>
)
}
export default App
获取dom的场景,可以直接把要获取的dom元素的类型当成泛型参数传递给useRef,可以推导出.current属性的类型
// useRef + ts
import { useEffect, useRef } from 'react'
// 1. 获取dom
// 2. 稳定引用的存储器(定时器管理)
function App() {
const domRef = useRef<HTMLInputElement>(null)
const timerId = useRef<number | undefined>(undefined)
useEffect(() => {
// 可选链 前面不为空值(null / undefined)执行点运算
// 类型守卫 防止出现空值点运算错误
domRef.current?.focus()
timerId.current = setInterval(() => {
console.log('123')
}, 1000)
return () => clearInterval(timerId.current)
}, [])
return (
<>
<input ref={domRef} />
</>
)
}
export default App