介绍

TS中在没有明确指定出类型的地方,类型推论会帮助提供类型

  • 推断是发生在初始化成员的时候,设置默认参数和决定函数返回值时
  • 如果没有找到最佳通用类型的话,类型推断的结果为联合数组类型(Rhino | Elephant | Snake)[]
// x被推断为数字,下面修改时也必须是数字类型
let x = 3;
x = false; // error

上下文类型

TS类型推论也可以按照相反的方向进行,叫做:按上下文归类,发生在表达式的类型与所处的位置相关时

window.onmousedown = function(mouseEvent) {
  console.log(mouseEvent.button);
}

类型兼容性

介绍:TS里的类型兼容性是基于结构子类型的
结构类型:是一种只使用其成员来描述类型的方式,正好与名义类型成对比

  • 名义类型的类型系统:数据类型的兼容性或等价性是通过明确的声明和类型的名称来决定的
  • 结构类型的类型系统:他是基于类型的组成结构,不要求明确的声明
  • 因为JS广泛使用匿名对象,例如:函数表达式和对象字面量,所以使用结构类型系统来描述这些类型比使用名义类型系统更好
// 使用基于名义类型定义,因为Person类没有明确说明其实现了Named接口
interface Named {
  name: string;
}
class Person {
  name: string;
}
let p: Named;
p = new Person();

TS结构化类型系统的基本规则:如果x要兼容y,那么y至少具有与x相同的属性,检查的过程是递归进行的

// 要检查y能否赋值给xx,编译器会检查xx中的每个属性,看是否能在y中也找到对应的属性
interface NamesD {
  name: string;
}
let xx: NamesD;
// y包含名字是name的string类型成员,所以满足赋值条件
let y = { name: 'Alice', location: 'Seattle' };
xx = y;

// 检查参数时使用相同的规则
function greet(n: NamesD){
  console.log('Hello, ' + n.name);
}
greet(y); // OK
// 比较两个函数:
// 要检查x是否能赋值给y,首先需要查看参数列表,x的每个参数必须能在y里找到对应的参数。
// 注:参数的名字是否相同无所谓,只看类型
let x1 = (a: number) => 0;
let y1 = (b: number, s: string) => 0;
// y1中包含x1中的类型,所以可以将x1赋值给y1
y1 = x1; // OK
// x1中没有包含y1中的所有类型,所以无法赋值
// x1 = y1; // error

// 处理返回值类型:类型系统强制源函数的返回值类型必须是目标函数返回值类型的子类型
let x2 = () => ({name: 'Alice'});
let y2 = () => ({name: 'Alice', location: 'Seattle'});

x2 = y2; // OK
// y2 = x2; // error

枚举类型与数字类型兼容,并且数字类型与枚举类型兼容。不同枚举类型之间是不兼容的

// 枚举类型与数字类型兼容,并且数字类型与枚举类型兼容。不同枚举类型之间是不兼容的
enum Status { Ready, Watting };
enum Color { Red, Blue, Green };

let statuss = Status.Ready;
// statuss = Color.Green; // error

类:类与对象字面量和接口差不多,但是:类有静态部分和实例部分的类型
比较两个类类型的对象时,只有实例的成员会被比较,静态成员和构造函数不在比较范围内

class Animal {
  feet: number;
  constructor(name: string, numFeet: number) {}
}
class Size {
  feet: number;
  constructor(numFeet: number){}
}
let animal: Animal;
let size: Size;

animal = size; // OK
size = animal; // OK

泛型:因为TS是结构性的类型系统,类型参数只影响使用其作为类型一部分的结果类型

interface Empty {};
let x3: Empty;
let y3: Empty;
// x跟y是兼容的,因为他们的结构使用类型参数时并没有什么不同
x3 = y3; // OK

interface NotEmpty{
  data: T;
}
let x4: NotEmpty;
let y4: NotEmpty;

// x4 = y4; // error

// 子类型与赋值:在TS里,有两种兼容性:子类型和赋值
// 他们不同点在于:赋值扩展了子类型兼容性,增加了一些规则,允许和any来回赋值,以及enum和对应数字值之间的来回赋值