> For the complete documentation index, see [llms.txt](https://yes-1-am.gitbook.io/blog/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://yes-1-am.gitbook.io/blog/typescript-yu-fa/fan-xing.md).

# 泛型

## 1. 泛型函数

定义一个 identity 函数

```
function identity<T>(arg: T): T {
    return arg;
}
```

给 identity 函数添加了 类型变量`T`. T 会捕获用户传入的类型，(比如: number)，之后 `T` 就代表这个类型了。

这个版本的 identity函数 叫做泛型函数,因为 T 可以是任意的类型。

泛型函数的两种调用方法:

```
1. 传入所有的参数，包括类型参数，其中类型参数用 <> 括起来
const output = identity<string>('123');
即明确指定 T 就是 string, 因此会限制 arg 必须为 string 类型。
因此下面的代码会报错:  
const output = identity<string>(123);  因此 123 不是 string 类型，所以会报错。


2. 可以不传类型参数，编译器自己查看 '123' 的类型，自动推算出 T 为 string
const output = identity('123');
```

## 2. 泛型变量

想象下以下代码，因为 return 的值必然是个字符串，而 T 不一定是 string, 所以会报错。

```
function identity<T>(arg: T): T {
    return arg+'123';
}
```

又或者：

```
function identity<T>(arg: T): T {
    console.log(arg.length);  // T 类型不一定有 length 属性(T 为 string 时有)，所以会报错
    return arg;
}
```

又因为数组具有 `.length` 属性，因此可以像下面这样：

```
function logIdentity<T>(arg: T[]): T[] {
    console.log(arg.length);    // 数组中确定是有length属性的
    return arg;
}
```

即我们可以把泛型变量`T`作为类型的一部分，实际类型 `T[]` 由 `T` 构成，增加了灵活性。

## 3. 泛型类型

```
function identity<T>(arg: T): T {
    return arg;
}

const myIdentity: <T>(arg: T) => T = identity
这段代码其实就是 const myIdentity = identity; 
<T>(arg: T) => T 只是类型描述
```

`const myIdentity: <T>(arg: T) => T = identity`\
又会等同于\
`const myIdentity: { <T>(arg: T): T } = identity`

因此我们可以定义一个泛型接口:

```
interface GenericIdentityFn {
    <T>(arg: T): T;
}

function identity<T>(arg: T): T{
    return arg;
}

const myIdentity: GenericIdentityFn = identity
```

接受参数的泛型接口:

```
// 接受一个类型参数，作为T
// 这样接口里的其他成员也能知道这个参数的类型
interface GenericIdentityFn<T> {
    (arg: T): T;
}

function identity<T>(arg: T) :T {
    return arg;
}

const myIdentity: GenericIdentityFn<number> = identity;
```

## 4. 泛型类

与泛型接口类似

```
class GenericNumber<T> {
    zeroValue: T;
    add: (x:T, y:T) => T;
}

const myGenericNumber = new GenericNumber<number>();  // 必须传入类型 number
myGenericNumber.zeroValue = 1;
myGenericNumber.add = function(x,y) = { return x + y };
```

**类有静态部分和实例部分，泛型类只能用于实例部分的类型，类的静态属性不能使用这个泛型类型。**

## 5. 泛型约束

首先以下代码会报错:

```
function logIdentity<T>(arg: T):T {
    console.log(arg.length); // 不一定会有 .length 属性
    return arg;
}
```

因此我们需要约束类型 `T` 必须要有 `length` 属性, 我们可以定义一个接口来描述约束条件，使用接口和`extends`关键字来实现约束：

```
interface LengthWise {
    length: number;
}

function identity<T extends LengthWise>(arg: T) :T {
    console.log(arg.length);  // 不会报错，接口限制了类型 T 会有 length 属性
    return arg;
}
```

此时，要调用 `identity`， 那么参数必须有 `length` 属性

### 5.1 泛型约束中使用类型参数

声明一个类型参数，被另一个类型参数所约束。

```
function getProperty<T, K extends keyof T>(obj: T, key:K) {
    return obj[key];
}

const x = {a: 1, b :2};

getProperty(x,'a')
getProperty(x,'m')    // m 不是对象 x 的key，因此会报错
```

关于 `keyof`:

```
interface Person {
    name: string;
    age: number;
    location: string;
}

type k1 = keyof Person;    // "name" | "age" | "location"   对象的属性名
type k1 = keyof Person[];    // "length" | "push" | "pop"  数组的属性名
```

因此 `K extends keyof T`，则 `K` 必须是对象 `T` 的某个属性名

### 5.2 泛型中使用类类型
