接口的兼容性

原理是 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;

打赏作者
您的打赏是我前进的动力
微信
支付宝
评论

中午好👏🏻,我是 ✍🏻   疯狂 codding 中...

粽子

这有关于产品、设计、开发的问题和看法,还有技术文档和你分享。

相信你可以在这里找到对你有用的知识和教程

了解更多

目录

  1. 1. 接口的兼容性
  2. 2. 基本类型的兼容性
  3. 3. 类的兼容性
  4. 4. 函数的兼容性
  5. 5. 函数的协变与逆变
  6. 6. 泛型的兼容性
  7. 7. 枚举的兼容性(数字和枚举 是兼容的)