接口的兼容性
- 如果传入的变量和声明的类型不匹配,TS就会进行兼容性检查
- 原理是Duck-Check,就是说只要目标类型中声明的属性变量在源类型中都存在就是兼容的 ```typescript interface Animal { name: string, age: number }
interface Person { name: string, age: number, gender: number }
function getName(animal: Animal): void { }
const person: Person = { name: ‘f’, age: 18, gender: 0 };
const animal: Animal = { name: ‘dog’, age: 2 };
getName(animal); // 之所以传递 person 也没有报错是因为 person 当做参数的时候满足 animal 的一切属性,只多不少 getName(person);
// 这样赋值是可以的 const animal1: Animal = person; console.log(animal1); // {name: ‘f’, age: 18, gender: 0}
// 但是下面这样是不行的 // const animal2: Animal = { // name: ‘dog’, // age: 18, // gender: 0 // };
<a name="ljWLh"></a># 基本类型的兼容性```typescriptlet a: string | number;let b: string = '';let c: number = 1;a = b; // a -> stringa = c; // a -> number// semantic error TS2322: Type 'number' is not assignable to type 'string'.// b = a;
let num: {toString():string}let str: string = '';num = str; // 可以赋值的原因是因为字符串具有 toString() 属性
类的兼容性
在 TS 中是结构类型系统,只会对比结构而不在意类型
class Anmial {}class Dog extends Anmial {}let animal: Anmial = new Anmial;let dog: Dog = new Dog;animal = dog;dog = animal;
class Anmial { name!:string }class Dog extends Anmial { dark() { } }let animal: Anmial = new Anmial;let dog: Dog = new Dog;animal = dog;// semantic error TS2741: Property 'dark' is missing in type 'Anmial' but required in type 'Dog'.// dog = animal;
函数的兼容性
参数
type Sum = (a: number, b: number) => void;let sum: Sum;function fn1(a: number, b: number): void { }sum = fn1;function fn2(a: number): void { }sum = fn2;function fn3(a: number, b: number, c: number): void { }// semantic error TS2322: Type '(a: number, b: number, c: number) => void' is not assignable to type 'Sum'.// sum = fn3;
返回值
type Person = () => { name: string, age: number }let person: Person;function person1() {return {name:'f', age:18}}person = person1;function person2() {return {name:'f'}}// semantic error TS2322: Type '() => { name: string; }' is not assignable to type 'Person'.// person = person2;function person3() {return { name: 'f', age: 18, gender: 1 }}person = person3;
返回值的参数可以多,但不能少
函数的协变与逆变
- 协变(Covariant):只在同一个方向;
- 逆变(Contravariant):只在相反的方向;
- 双向协变(Bivariant):包括同一个方向和不同方向;
- 不变(Invariant):如果类型不完全相同,则它们是不兼容的。
- A ≼ B 意味着 A 是 B 的子类型。
- A → B 指的是以 A 为参数类型,以 B 为返回值类型的函数类型。
- x : A 意味着 x 的类型为 A
- 返回值类型是协变的,而参数类型是逆变的
- 返回值类型可以传子类,参数可以传父类
- 参数逆变父类 返回值协变子类 搀你父,返鞋子 ```typescript class Animal { } class Cat extends Animal { name!:string } class WhiteCat extends Cat { age!:number } class BlackCat extends Cat { home!:string }
let animal: Animal; let whiteCat: WhiteCat; let blackCat: BlackCat;
type CallBack = (cat: Cat) => Cat;
function exec(callback: CallBack) { callback(whiteCat); }
type ChildToChild = (blackCat: BlackCat) => BlackCat; const childToChild: ChildToChild = (blackCat: BlackCat): BlackCat => blackCat; // exec(childToChild);
type ChildToParent = (blackCat: BlackCat) => Animal; const childToParent: ChildToParent = (blackCat: BlackCat): Animal => animal // exec(childToParent);
type ParentToParent = (animal: Animal) => Animal; const parentToParent: ParentToParent = (animal: Animal): Animal => animal // exec(parentToParent);
type ParentToChild = (animal: Animal) => BlackCat; const parentToChild: ParentToChild = (animal: Animal): BlackCat => blackCat exec(parentToChild);
- 在 TypeScript 中, 参数类型是双向协变的 ,也就是说既是协变又是逆变的,而这并不安全。但是现在你可以在 TypeScript 2.6 版本中通过 --strictFunctionTypes 或 --strict 标记来修复这个问题```typescripttype Callback = (a: string | number) => string | number;function exec(callback: Callback):void{callback('');}type ParentToChild = (a: string | number | boolean) => string;const parentToChild: ParentToChild = (a: string | number | boolean): string => ''exec(parentToChild);
上面的代码运行无误。
type Callback = (a: string | number) => string | number;function exec(callback: Callback): void {callback('');}type ParentToParent = (a: string) => string;const parentToParent: ParentToParent = (a: string): string => ''// semantic error TS2345: Argument of type 'ParentToParent' is not assignable to parameter of type 'Callback'.exec(parentToParent);
上述代码报错,原因是 Callback 的参数可以是 string 或者 number 类型,就说明在函数内部可能调用了两种类型的方法,parentToParent 的参数只传递了 string 类型,存在 callback 执行报错的可能,所以 TS 会检测出错误。
泛型的兼容性
泛型在判断兼容性的时候会先判断具体的类型,然后再进行兼容性判断
interface Empty<T> { }let a!: Empty<string>;let b!: Empty<number>;a = b;b = a;
上述代码运行正常
interface Empty<T> { data: T }let a!: Empty<string>;let b!: Empty<number>;// semantic error TS2322: Type 'Empty<number>' is not assignable to type 'Empty<string>'a = b;
枚举的兼容性
- 枚举类型与数字类型兼容,并且数字类型与枚举类型兼容
- 不同枚举类型之间是不兼容的 ```typescript enum Colors {Red,Yellow} let c:Colors; c = Colors.Red; // 数字值可以赋值给枚举 c = 1;
// 枚举值可以赋值给数字 let n:number; n = 1; n = Colors.Red; ```
