> 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/lei-xing-jian-rong-xing.md).

# 类型兼容性

## 1. 介绍

TypeScript 里的类型兼容性是基于**结构子类型**的，它与**名义类型**形成对比。

即结构子类型只要求组成结构一致，而不需要完全的名称相同。

```javascript
interface Named {
    name: string;
}

class Person {
    name: string;
}

let p: Named;
p = new Person();  // 不会报错
```

因为 Named 类型和 Person 类型的结构`{ name: string }`是一致的，但是在基于**名义类型**的语言中，如 C# 或 Java 中，这段代码会报错，因为 Person 类没有明确说明其实现了 Named 接口。

### 1.1 对象兼容

如果 x 要兼容 y，那么 y 至少具有与 x 相同的属性

```javascript
interface Named {
    name: string;
}

let x: Named;

let y = { name:'Alice', location: 'Seattle'};

x = y;
```

将 `y` 赋值给 `x`，目标为 `x`，此时就会检验，是否 `x` 中的每个属性，在 `y` 中都可以找到。只要都能找到，那么赋值就是允许的。

检验函数参数也是同样的

```javascript
function greet(n: Named) {
    console.log(n);
}

greet(y)

// 只要 Named 中的所有属性，y中都能找到，就可以。
// 至于多余的 location  属性，不会引发错误
```

这个属性查找的过程是递归的，检查每个成员及子成员。

### 1.2 函数兼容

**1.2.1 参数兼容**

```javascript
let x = (a:number) => 0;
let y = (b:number,s:number) => 0;

x = y;  // 错误，y函数的第二个参数必选，而x不接受
y = x;  // 正确，x函数的每个参数，在y中都能找到对应类型的参数,且索引一致


let x = (a: number) => 0;
let y = (b: string, s: number) => 0;
y = x;  // 错误，x的第一个参数为number，而y函数为string
```

为什么会允许，将*少参数的函数*赋值给*多参数的函数*呢？

因为在JS中忽略额外的参数是很常见的，如 `Array#forEach`， 三个参数为元素，索引和整个数组:

```javascript
let items = [1,2,3];

// 符合要求
items.forEach((item,index,arr) => console.log(item));

// 但是最常见的是会省略不用的参数
items.forEach((item) => console.log(item));
```

**1.2.2 返回值兼容**

```javascript
let x = () => ({name: 'Alice'});
let y = () => ({name: 'Alice', location: 'Seattle'});

x = y; // OK

y = x; // Error, because x() lacks a location property
// 要求y中的返回值，x都要能返回. 这与参数兼容不一样
```

## 2. 枚举之间的兼容性

枚举类型与数字类型兼容，数字类型与枚举类型兼容. 但是枚举类型之间是不兼容的。

```
enum Status { Ready, Waiting }
enum Color { Red, Blue, Green };

let status1:number = Status.Ready;   // 数字类型与枚举类型兼容
status1 = Color.Red;    // 错误， 不同枚举类型不兼容
```

## 3. 类

类与对象字面量和接口差不多，除了：类有静态部分和实例部分的类型。

比较两个类类型的对象时，只有实例成员会被比较，静态成员和构造函数不在比较范围内。

```javascript
class Animal {
    feet: number;
    constructor(name: string) {}
}

class Size {
    feet: number;
    constructor(feetNum: number) {}
}

let a: Animal;
let s: Size;

a = s;  // OK 
s = a;  // OK

// 因此对于Animal和Size来说，都有feet属性，而constructor方法虽然不一样，但是不会被比较
```

## 4. 泛型

类型参数只影响使用其作为类型一部分的结果类型：

```
interface Empty<T> {
}
let x: Empty<number>;
let y: Empty<string>;

x = y;  // OK, because y matches structure of x
因为虽然T最终的类型不一样，但是最终的interface并没有使用到T

interface NotEmpty<T> {
    data: T;
}
let x: NotEmpty<number>;
let y: NotEmpty<string>;

x = y;  // Error, because x and y are not compatible
因为T最终的类型不一样，且最终的interface使用到了T，所以会不兼容
```
