# 类型兼容性

## 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，所以会不兼容
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://yes-1-am.gitbook.io/blog/typescript-yu-fa/lei-xing-jian-rong-xing.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
