Description
泛型
泛型:在定义函数,接口,类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
泛型和函数很类似,泛型接受的参数是类型,返回值也是类型
泛型要用尖括号(<>)
function createArray<T>(length: number, value: T): Array<T> {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
createArray<string>(3, 'x'); // ['x', 'x', 'x']
函数声明:在函数名后添加 <T>
,其中 <T>
用来代指任意输入的类型。在后面的输入 value: T 和输出 Array 中即可使用了。
函数调用:可以指定它具体的类型为 string,也可以让类型自动推论出来。
多个类型参数
function swap<T, U>(tuple: [T, U]): [U, T] {
return [tuple[1], tuple[0]];
}
swap([7, 'seven']); // ['seven', 7]
泛型约束
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
只允许传入那些包括 length
属性的变量
泛型的种类
- 函数泛型
function swap<T, U>(tuple: [T, U]): [U, T] {
return [tuple[1], tuple[0]];
}
swap([7, 'seven']); // ['seven', 7]
- 接口泛型
interface id<T, U> {
id1: T;
id2: U;
}
interface CreateArrayFunc {
<T>(length: number, value: T): Array<T>;
}
- 类泛型
class MyComponent extends React.Component<Props, State> {
...
}
总结:泛型的写法就是在标志符后面用 尖括号(<>), 然后在尖括号里写形参,并在 body(函数体,接口体或者类体)里用这些形参做一些逻辑处理
泛型参数的默认类型
function createArray<T = string>(length: number, value: T): Array<T> {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
从函数的角度来理解泛型
可以类比函数的默认参数理解:
泛型和函数很类似,泛型接受的参数是类型,返回值也是类型
定义:
应用:
- 从外表看只不过是 function 变成了 type,() 变成了 <>而已。
- 从语法规则上来看, 函数内部对标的是 ES 标准。而泛型对应的是 TS 实现的一套标准。
通过一些例子来学习一下泛型
1. React.FC
泛型类比到函数进行理解
- 首先定义了一个泛型类型
FC
,这个FC
就是我们平时用的React.FC
。它是通过另外一个泛型FunctionComponent
产生的。因此,实际上第一行代码的作用就是起了一个别名 - FunctionComponent 实际上是就是一个接口泛型,它定义了五个属性,其中四个是可选的,并且是静态类属性。
- displayName 比较简单,而 propTypes,contextTypes,defaultProps 又是通过其他泛型生成的类型。我们仍然可以采用我的这个分析方法继续分析。
- (props: PropsWithChildren
, context?: any): ReactElement<any, any> | null; 的含义是 FunctionComponent 是一个函数,接受两个参数(props 和 context )返回 ReactElement 或者 null。ReactElement 大家应该比较熟悉了。PropsWithChildren 实际上就是往 props 中插入 children,源码也很简单,代码如下:
type PropsWithChildren<P> = P & { children?: ReactNode };
使用
import React from 'react';
interface PropsType {
name: string;
}
const MyComponent: React.FC<PropsType> = ({name}) => <p>hello {name}</p>;
export default MyComponent;
2. React 中 useState的声明
重载允许一个函数接受不同数量或类型的参数时,作出不同的处理。
const [state, setState] = useState(initialState);
const [fileList, setFileList] = useState<UploadFile[]>([]);
// initialState 参数只会在组件的初始渲染中起作用
const [state, setState] = useState(() => {
const initialState = someExpensiveComputation(props);
return initialState;
});
3.axios中的使用
通常情况下,我们会把后端返回数据格式单独放入一个 interface 里:
// 请求接口数据
export interface ResponseData<T = any> {
code: number;
result: T;
message: string;
}
当我们把 API 单独抽离成单个模块时:
// 在 axios.ts 文件中对 axios 进行了处理,例如添加通用配置、拦截器等
import Ax from './axios';
import { ResponseData } from './interface.ts';
export function getUser<T>() {
return Ax.get<ResponseData<T>>('/somepath')
.then(res => res.data)
.catch(err => console.error(err));
}
接着我们写入返回的数据类型 User,这可以让 TypeScript 顺利推断出我们想要的类型:
interface User {
name: string;
age: number;
}
async function test() {
// user 被推断出为
// {
// code: number,
// result: { name: string, age: number },
// message: string
// }
const user = await getUser<User>();
}
axios 的 声明文件:https://github.com/axios/axios/blob/master/index.d.ts
选取其中的部分
export interface AxiosResponse<T = any> {
data: T;
status: number;
statusText: string;
headers: any;
config: AxiosRequestConfig;
request?: any;
}
4. TS 中使用带有泛型参数的 JSX
function Form() {
// ...
return (
<Select<string> options={targets} value={target} onChange={setTarget} />
);
}
什么时候用泛型
当你的函数,接口或者类:
- 当函数、接口、类是接受多类型参数的时候,可以用泛型提高可重用性。
- 当函数、接口、类需要在多个地方用到某个类型的时候。