复习ts泛型+深度实例解析

直接上需求

假设你的团队都需要使用一个你写出来的utils,就比如转化一些元素元素为元素数组,这个方法称之为convertToArray

如果写js 我们当然可以洋洋洒洒这样就完事

js 复制代码
function convertToArray(...input) {
	return [...input]
}

const output = convertToArray('hello', 'world')
const output2 = convertToArray(2)
const output3 = convertToArray({ name: 'herry' })

如果代码在ts文件中,显然是会报警告的

如果要应付一下这个报错当然也很简单,把prettier的ts any直接ignore也行

也可以直接添加类型

ts 复制代码
const convertToArray = (...input: (string | number | object)[]): (string | number | object)[] => [
	...input,
]

但是是硬编码不够灵活,这个时候就比较适合上泛型。

泛型出场

泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性

在函数名之后 参数之前加上一对尖括号<Type1, Type2, ...>,一般简写为T,当然这都只是一个参数名称而已

ts 复制代码
function convertToArray<T>(...input : T[]): T[] {
	return [...input]
}
const output = convertToArray('hello' ,'world')
const output2 = convertToArray(2, 1, 3, 7)
const output3 = convertToArray({ name: 'herry' })

或者写成箭头函数,在等号之后 参数之前加尖括号<Type1, Type2, ...>

ts 复制代码
const convertToArray = <T>(...input: T[]): T[] => [...input]

添加类型

你当然可以显式给参数添加类型

ts 复制代码
const output = convertToArray<string>('hello', 'world')

一般情况下ts可以直接隐式推断出参数类型,就没有必要再添加

但是如果传递的参数类型不同也会报错

加深巩固

趁热再来个需求,返回数组元素的下标(不考虑边界情况)

  • 一般来说数组中的元素都一样,所以我们可以共用一个类型参数T
  • 数组下标肯定是number类型,返回值可以写死,当然ts其实会做隐式推断
js 复制代码
function getIndexOfItem<T>(array: T[], arrayItem: T): number {
	return array.findIndex(item => item === arrayItem)
}
const getIndexOfItem = <T>(array: T[], arrayItem: T) => array.findIndex(item => item === arrayItem)

多个类型参数

多个类型参数只需要在<>中添,和函数参数类似

ts 复制代码
const createArrayPair = <T, S>(input: T, input2: S): [T, S] => {
	return [input, input2]
}
const output = createArrayPair('hello', 1024)

在tsx中实操

TODO:假设一个简单需求, 实现一个主题选择器

tsx 复制代码
import { useState } from 'react'
import './theme.css'
export default function Theme() {
	const [selectedTheme, setTheme] = useState('light')
    const themesOptions = ['light', 'dark', 'system']
	return (
		<div className="container">
			<h2>主题设置</h2>
			<ul>
				{themesOptions.map(theme => (
					<li
						key={theme}
						onClick={() => setTheme(theme)}
						className={theme === selectedTheme ? 'selected' : ''}
					>
						{theme}
					</li>
				))}
			</ul>
			<div>
				我选择主题的是 <strong>{selectedTheme}</strong>
			</div>
		</div>
	)
}

代码很简单就不啰嗦了,虽然没有一点类型注解没有一点ts的痕迹但是在tsx运行良好,其原因是ts会隐式推断

子组件进阶

TODO:你需要将ul用子组件包裹(也就是上面的第9行到19行)

将第9行到19行替换为

tsx 复制代码
<ThemeOptions themesOptions={themesOptions} selectedTheme={selectedTheme} setTheme={setTheme}/>

ThemeOptions就定义为

tsx 复制代码
function ThemeOptions({
	selectedTheme,
    themesOptions,
    setTheme,
}: {
	selectedTheme: string
    themesOptions: string[]
	setTheme: (theme: string) => void
}) {
	return (
		<ul>
			{themesOptions.map(theme => (
				<li
					key={theme}
					onClick={() => setTheme(theme)}
					className={theme === selectedTheme ? 'selected' : ''}
				>
					{theme}
				</li>
			))}
		</ul>
	)
}

其实就写了硬参数类型,不具备通用型

泛型改造

tsx 复制代码
type ThemeOptionsProps<T> = {
	selectedTheme: T
	themesOptions: T[]
	setTheme: (theme: T) => void
}
function ThemeOptions<T>({ selectedTheme, setTheme, themesOptions }: ThemeOptionsProps<T>) {
	return {}
}

显示报错,根据提示加上类型约束就可以了ThemeOptions<T extends React.ReactNode> 表示ThemeOptions组件的参数必须是React.ReactNode类型的 当然,如果图简便直接将theme转化成字符串{String(theme)}也没问题, 这样可以确保theme是ReactNode的一种有效形式。但ReactNode是什么类型呢

ReactNode类型

通过查看react声明文件,得出 ReactNode 的类型如下:

js 复制代码
type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined; 
type ReactChild = ReactElement | ReactText;
interface ReactNodeArray extends Array<ReactNode> {}
type ReactFragment = {} | ReactNodeArray;
type ReactText = string | number;

感觉像是在套娃。。 分析一下

  • 联合类型ReactNode包括6个,其中ReactChild也是联合类型,包含ReactElement 和 ReactText

  • 而页面上的JSX.Element 又通过执行 React.createElement 或是转译 JSX 而来

    • JSX.Element 是一个 ReactElement, react类型定义如下
    jsx 复制代码
    declare global {
        namespace JSX {
            interface Element extends React.ReactElement<any, any> { }
        }
    }
    • 分析大半天居然是any。。但这个过程还是要有的
  • 而从type ReactText = string | number;可以看出string是符合ReactText类型当然也就符合ReactNode类型

所以,图简便直接将theme转化成字符串{String(theme)}也没问题

extends类型约束

那么接下来我们来研究一下 ThemeOptions<T extends React.ReactNode>

<T extends React.ReactNode>将ThemeOptions的参数类型限定为React.ReactNode,传参时ts将theme推导为string类型 而string符合ReactNode类型

关于extends用法网上有很多文章,这里就不再赘述辽

参考

相关推荐
hongkid8 分钟前
React Native 如何打包正式apk
javascript·react native·react.js
李少兄11 分钟前
简单讲讲 SVG:前端开发中的矢量图形
前端·svg
前端小万12 分钟前
告别 CJS 库加载兼容坑
前端·前端工程化
恋猫de小郭12 分钟前
Flutter 3.38.1 之后,因为某些框架低级错误导致提交 Store 被拒
android·前端·flutter
JarvanMo16 分钟前
Flutter 需要 Hooks 吗?
前端
光影少年26 分钟前
前端如何虚拟列表优化?
前端·react native·react.js
Moment28 分钟前
一杯茶时间带你基于 Yjs 和 reactflow 构建协同流程图编辑器 😍😍😍
前端·后端·面试
invicinble1 小时前
对于前端数据的生命周期的认识
前端
PieroPc1 小时前
用FastAPI 后端 和 HTML/CSS/JavaScript 前端写一个博客系统 例
前端·html·fastapi
hunter14501 小时前
2026.1.4 html简单制作
java·前端·笔记·html