复习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用法网上有很多文章,这里就不再赘述辽

参考

相关推荐
wordbaby几秒前
搞不懂 px、dpi 和 dp?看这一篇就够了:图解 RN 屏幕适配逻辑
前端
程序员爱钓鱼3 分钟前
使用 Node.js 批量导入多语言标签到 Strapi
前端·node.js·trae
鱼樱前端4 分钟前
uni-app开发app之前提须知(IOS/安卓)
前端·uni-app
V***u4534 分钟前
【学术会议论文投稿】Spring Boot实战:零基础打造你的Web应用新纪元
前端·spring boot·后端
i听风逝夜43 分钟前
Web 3D地球实时统计访问来源
前端·后端
iMonster1 小时前
React 组件的组合模式之道 (Composition Pattern)
前端
呐呐呐呐呢1 小时前
antd渐变色边框按钮
前端
元直数字电路验证1 小时前
Jakarta EE Web 聊天室技术梳理
前端
wadesir1 小时前
Nginx配置文件CPU优化(从零开始提升Web服务器性能)
服务器·前端·nginx
牧码岛1 小时前
Web前端之canvas实现图片融合与清晰度介绍、合并
前端·javascript·css·html·web·canvas·web前端