类型兼容性用于确定一个类型是否能赋值给其他类型。
X兼容Y 即 X(目标类型)= Y(源类型)
1.接口兼容
只要Y具备X接口的属性,则X = Y成立。
这里遵循鸭子类型规则:“如果它走路是鸭子,叫起来也是鸭子,那么它就是鸭子”。鸭子类型指导我们只关注对象的行为,而不关注对象的本身。
interface X{
a:any;
b:any;
}
interface Y{
a:any;
b:any;
c:any;
}
let x:X = {a:1,b:2}
let y:Y = {a:1,b:2,c:3}
x = y;
y = x;//类型 "X" 中缺少属性 "c",但类型 "Y" 中需要该属性。
2.函数兼容
2.1 参数个数
目标函数的参数个数要必须要多于源类型参数的个数,参数名不需要相同,参数类型必须一致。
let x = (a: number) => 0;
let y = (b: number, s: string) => 0;
y = x; // OK
x = y; // Error
2.2 固定参数、可选参数、剩余参数
固定参数可以兼容可选参数和剩余参数
可选参数不兼容剩余参数和固定参数
剩余参数可以兼容固定参数和可选参数
let foo = (x: number, y: number) => {};
let bar = (x?: number, y?: number) => {};
let bas = (...args: number[]) => {};
foo = bar;
foo = bas;
bar = foo;//Error
bar = bas;//Error
bas = foo;
bas = bar;
2.3 返回值
目标函数的返回值类型必须与源函数的返回值类型相同,或为其子类型。不准确的理解即,源函数≥目标函数,源函数返回参数类型要包含目标函数返回值类型。
interface Point2D {
x: number;
y: number;
}
interface Point3D {
x: number;
y: number;
z: number;
}
let iMakePoint2D = (): Point2D => ({ x: 0, y: 0 });
let iMakePoint3D = (): Point3D => ({ x: 0, y: 0, z: 0 });
iMakePoint2D = iMakePoint3D;
iMakePoint3D = iMakePoint2D; // ERROR: Point2D 不能赋值给 Point3D
其中函数重载兼容性
function overload(a:number,b:number):number;
function overload(a:string,b:string):string;
//前面两条为重载列表,程序运行时会查找第一个匹配的定义去执行函数,后面一条为实现方式
//前为目标函数,后为源类型函数。目标函数的参数要多于源函数的参数
function overload(a:any,b:any):any{};
3.枚举类型
枚举和数字类型相互兼容,不同的枚举类型相互不兼容。
enum Fruit {Apple,Banana}
enum Color {Red , Yellow}
//数字兼容
let fruit :Fruit.Apple = 3;
let no :number = Fruit.Apple ;
//不同枚举类型不兼容
let apple = Fruit.Apple;
apple = Fruit.Banana;
apple = Color.Red;//Error
4.类兼容
4.1 静态成员和构造函数不参与比较,如果两个类具有完全相同的实例成员,则他们完全兼容。
class A{
static a = 1;//静态成员
constructor(p:number,q:number){};//构造函数
id:number = 1;//实例成员
}
class B{
constructor(p:number){};
id:number = 2;
}
let aa = new A(1,2);
let bb = new B(1);
aa = bb;
bb = aa;
4.2 类的私有成员和受保护成员
类的私有成员和受保护成员会影响兼容性。 当检查类实例的兼容时,如果目标类型包含一个私有成员,那么源类型必须包含来自同一个类的这个私有成员。 同样地,这条规则也适用于包含受保护成员实例的类型检查。 这允许子类赋值给父类,但是不能赋值给其它有同样类型的类。
class Animal {
protected feet: number;
}
class Cat extends Animal {}
let animal: Animal;
let cat: Cat;
animal = cat; // ok
cat = animal; // ok
class Size {
protected feet: number;
}
let size: Size;
animal = size; // ERROR
size = animal; // ERROR
5.泛型兼容
5.1 如果两个泛型定义相同,但是没有指定泛型函数参数,它们之间也是相互兼容的。
interface Empty<T>{}
let obj1:Empty<number> = {};
let obj2:Empty<string> = {};
obj1 = obj2;
obj2 = obj1;//Error
5.2 如果泛型中指定了类型参数,会按照结果类型进行比较。
interface Empty<T>{
value:T//它将在实例化泛型后影响兼容性
}
let obj1:Empty<number> = {value:1};
let obj2:Empty<string> = {value:"你好"};
obj1 = obj2;//Error,不能将类型“Empty<string>”分配给类“Empty<number>”。不能能将类型“string”分配给类型“number”。
obj2 = obj1;//Error