直接上需求
假设你的团队都需要使用一个你写出来的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类型定义如下
 jsxdeclare 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类型
 而string符合ReactNode类型
关于extends用法网上有很多文章,这里就不再赘述辽