接口的兼容性
原理是 Duck-Check,就是说只要目标类型中声明的属性变量在源类型中都存在就是兼容的
interface Animal {
name: string;
age: number
}
interface Person {
name: string;
age: number;
gender: number
}
// 测试
const getName = (a: Animal): string => a.name;
let a: Animal = { name: '', age: 10 }
getName(a);
// 兼容 Person:因为 Animal 中的属性 Person 里面都有
let p: Person = { name: "", age: 10, gender: 0 };
getName(p);
基本类型的兼容性
// 示例 1
let num: string | number;
let str: string = '张三';
num = str; // 兼容:str 的类型是 num 的子类型
// 示例 2ÏÏ
let num2: { toString(): string }
let str2: string = '李四';
num2 = str2; // 兼容:str2 的类型是 num2 的子类型
str2 = num2; // 不能将类型“{ toString(): string; }”分配给类型“string”
类的兼容性
class Animal { name: string }
class Bird extends Animal { age: number }
let animal: Animal;
let bird: Bird;
animal = bird; // 兼容:animal 中有的 bird 中都有
bird = animal; // 类型 "Animal" 中缺少属性 "age",但类型 "Bird" 中需要该属性
函数的兼容性
// 1.比较参数(参数可以少,但是不能多)参数逆变传父类
type Func = (a: number, b: number) => void;
let sum: Func;
function f4(a: number, b: number, c: number): void { }
sum = f4; // 不能将类型“(a: number, b: number, c: number) => void”分配给类型“Func”
function f1(a: number, b: number): void { }
sum = f1; // 兼容
function f2(a: number): void { }
sum = f2; // 兼容
function f3(): void { }
sum = f3; // 兼容
// 2.比较返回值(对象返回值可以多,但是不能少)返回值协变返子类
type GetPerson = () => { name: string, age: number }
let getPerson: GetPerson;
const g1 = () => ({ name: '张三', age: 10 });
getPerson = g1; // 兼容
const g2 = () => ({ name: '张三', age: 10, gender: 0 });
getPerson = g2; // 兼容
const g3 = () => ({ name: '张三' });
getPerson = g3; // 类型 "{ name: string; }" 中缺少属性 "age",但类型 "{ name: string; age: number; }" 中需要该属性
函数的协变与逆变
参数逆变传父类,返回值协变返子类
class Animal { }
class Dog extends Animal {
public name: string = "Dog";
}
class BlackDog extends Dog {
public age: number = 10;
}
type Callback = (dog: Dog) => Dog;
function exec(callback: Callback): void { }
// 1.参数传子类返回值子类
type ChildToChild = (blackDog: BlackDog) => BlackDog;
let childToChild: ChildToChild;
exec(childToChild); // n:参数“blackDog”和“dog” 的类型不兼容
//ts中其实函数参数是双向协变的,"strictFunctionTypes": false,也是正确的,这是个bug,不严谨
// 2.参数是子类返回值是父类
type ChildToParent = (blackDog: BlackDog) => Animal;
let childToParent: ChildToParent;
exec(childToParent); // n:参数“blackDog”和“dog” 的类型不兼容
// 3.参数是父类返回值是父类
type ParentToParent = (animal: Animal) => Animal;
let parentToParent: ParentToParent;
exec(parentToParent); // n:类型 "Animal" 中缺少属性 "name",但类型 "Dog" 中需要该属性
// 4.参数是父类返值是子类
type ParentToChild = (animal: Animal) => BlackDog;
let parentToChild: ParentToChild;
exec(parentToChild); // y
// 结论:参数可以传自己和自己的父类,返回值可以传自己和自己的子类
// 扩展:
type Callback = (a: string | number) => string | number;
function exec(callback: Callback) { }
// 1.参数传子类返回值子类
type ChildToChild = (a: string) => string;
let childToChild: ChildToChild;
exec(childToChild);// n:不能将类型“string | number”分配给类型“string”
// 2.参数是子类返回值是父类
type ChildToParent = (a: string) => string | number | boolean;
let childToParent: ChildToParent;
exec(childToParent);// n:不能将类型“string | number”分配给类型“string”
// 3.参数是父类返回值是父类
type ParentToParent = (a: string | number | boolean) => string | number | boolean;
let parentToParent: ParentToParent;
exec(parentToParent);// n:不能将类型“string | number | boolean”分配给类型“string | number”
// 4.参数是父类返值是子类
type ParentToChild = (a: string | number | boolean) => string;
let parentToChild: ParentToChild;
exec(parentToChild); // y
// 结论:参数可以传自己和自己的父类,返回值可以传自己和自己的子类
泛型的兼容性
interface Empty<T> {
data: T
}
let x: Empty<string>; // {data:string}
let y: Empty<number>; // {data:number}
x = y; // 不能将类型“Empty<number>”分配给类型“Empty<string>”
枚举的兼容性(数字和枚举 是兼容的)
// 例子 1
enum Colors { Red, Yellow }
let c: Colors;
c = Colors.Red;
c = 1;
// 例子 2
let n: number;
n = 1;
n = Colors.Red;
泛型
上一篇