类型推论
当我们在没有明确声明类型去初始化变量、成员、函数默认参数和函数返回值的时候,TS的类型推论就会自动推导,例如:
let x = 10; //此处会被推导为 number。
最佳通用类型
当我们需要从几个表达式中推断类型的时候,计算通用类型算法会考虑所有候选类型,并得出一个兼容所有类型的类型。例如:
//这里的候选类型有 number 和 null,null是number的子类型,null 可以赋值给number,所以最终结果是 number[]
let arr = [0,1,null];
由于最终通用类型是所有候选类型中的通用类型,但是候选类型中没有兼容所有类型的类型时,有时候我们需要手动指出类型,例如:
//但是这个对象中是没有Animal类型的,因此不能推断出是Animal类型
class Animal{}
class Bird extends Animal{}
class Pig extends Animal{}
//zoo的正确类型应该是 Animal[],但是由于候选类型中只有 Bird 和 Pig,无法推导出通用类型,所以会得到候选类型的组合 ( Bird | Pig )[]
let zoo = [new Bird(), new Pig()];
//为了得到正确的类型,这里只能明确的使用类型声明 zooTwo:Animal
let zooTwo:Animal[] = [new Bird(), new Pig()];
上下文类型
之前小节讲的是Typescript类型推论可以根据赋值表达式右边的来推断变量的类型,Typescript类型推论还可以根据赋值表达式的左边来推论赋值表达式右边的类型,叫做上下文类型,如果赋值表达式右边已经有明确的参数类型时,上下文类型会被忽略。
类型兼容性
Typescript里的兼容性是属于结构子类型的,成员只要都包含即可,俗称"鸭式变型法",例如:
interface Named {
name: string;
}
class Person {
name: string;
}
let p: Named;
//虽然Person没有明确的表明实现了Named接口,但是由于Person的成员包含了 Named的成员,就是类型兼容的,赋值不会报错。
p = new Person();
Typescript 基于结构子类型是根据javascript的典型写法来设计的,javascript可以广泛的使用匿名对象和函数。
对象类型比较
TypeScript结构化类型系统的基本规则是,如果x要兼容y,那么y至少具有与x相同的属性,比如:
interface Named {
name: string;
}
let x: Named;
//y有一个额外的属性location,这不会引起错误,只有目标类型的成员会被递归一一检查是否兼容。这个比较的过程是递归的
let y = { name: 'Alice', location: 'Seattle' };
x = y;
函数比较
比较原始类型和比较对象类型的时候是比较容易理解的,接下来介绍如何判断两个函数是否兼容
- 比较参数列表,x函数的参数列表的类型可以依次在 y 函数的参数列表中找到相同位置对应的类型,那么 x函数可以赋值给 y 函数;
let x= (a: number) => 0;
let y = (b: number, s: string) => 0;
x = y; //报错
y = x; //正确
- 跟比较对象类型不同,对象类型是 b对象至少要包含a中需要的成员,有额外的也无所谓,但是函数是相反的,只要 b 函数的参数列表的类型可以再 a 中找到对应的即可,你可能会疑惑为什么可以忽略参数,原因是忽略额外的参数在JavaScript里是很常见的。
// Array.foreach((item,index,array)=>{}) 是有三个参数的,但我们平时使用是可以只传前几个的
let array = [1,2,3].foreach(item => item);
下面我们来创建两个返回值类型不同的函数进行对比
//返回值的对比就是对象类型的比较了,赋值表达式左边的成员要包含左边的所有成员,除了额外属性和可选属性。
let x = () => ({name: 'Alice'});
let y = () => ({name: 'Alice', location: 'Seattle'});
x = y; // OK
y = x; // Error, because x() lacks a location property
函数重载
对于函数重载,源函数的每个重载都要在目标函数上找到对应的签名。
枚举
枚举类型的number类型成员与数字类型互相兼容,不同枚举类型互不兼容。
类
类与对象等差不多,但有一点不同的是:类只会去比较实例部分,静态部分不在比较范围内,并且私有成员和受保护成员会影响兼容性,当检查实例类型时,目标类型包含一个私有成员,源类型必须包含来自同一个类的私有成员。