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

参考

相关推荐
鑫~阳1 小时前
html + css 淘宝网实战
前端·css·html
Catherinemin1 小时前
CSS|14 z-index
前端·css
2401_882727572 小时前
低代码配置式组态软件-BY组态
前端·后端·物联网·低代码·前端框架
NoneCoder3 小时前
CSS系列(36)-- Containment详解
前端·css
anyup_前端梦工厂3 小时前
初始 ShellJS:一个 Node.js 命令行工具集合
前端·javascript·node.js
5hand3 小时前
Element-ui的使用教程 基于HBuilder X
前端·javascript·vue.js·elementui
GDAL3 小时前
vue3入门教程:ref能否完全替代reactive?
前端·javascript·vue.js
六卿3 小时前
react防止页面崩溃
前端·react.js·前端框架
z千鑫4 小时前
【前端】详解前端三大主流框架:React、Vue与Angular的比较与选择
前端·vue.js·react.js
m0_748256144 小时前
前端 MYTED单篇TED词汇学习功能优化
前端·学习