Skip to content

TypeScript之泛型 #164

Open
Open
@funnycoderstar

Description

@funnycoderstar

泛型

泛型:在定义函数,接口,类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
泛型和函数很类似,泛型接受的参数是类型,返回值也是类型
泛型要用尖括号(<>)

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;
}

从函数的角度来理解泛型

可以类比函数的默认参数理解:
泛型和函数很类似,泛型接受的参数是类型,返回值也是类型

定义:

image

应用:

image

  • 从外表看只不过是 function 变成了 type,() 变成了 <>而已。
  • 从语法规则上来看, 函数内部对标的是 ES 标准。而泛型对应的是 TS 实现的一套标准。

image

通过一些例子来学习一下泛型

1. React.FC

image

泛型类比到函数进行理解

  • 首先定义了一个泛型类型 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的声明

image

重载允许一个函数接受不同数量或类型的参数时,作出不同的处理。

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
选取其中的部分
image

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} />
  );
}

Select 组件中的定义
image

什么时候用泛型

当你的函数,接口或者类:

  • 当函数、接口、类是接受多类型参数的时候,可以用泛型提高可重用性。
  • 当函数、接口、类需要在多个地方用到某个类型的时候。

参考文档

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions