前言
TS为了类型的安全性考量,如果传入的类型和声明的类型不匹配TS就会进行兼容性检查。
原理:原理是 Duck-Check。就是说只要目标类型中声明的属性变量再原类型中都存在就是兼容的。
提示:以下是本篇文章正文内容,下面案例可供参考
一、使用示例
1.接口的兼容性
具有同一性,接口里已定义的属性或方法,必须要传入
namespace a {
interface Animal {
name: string,
age: number
}
interface Person {
name: string,
age: number
}
function getName(a: Animal): string {
return `${a.name}${a.age}`;
}
let a: Animal = {
name: '张三',
age: 10
}
console.log(getName(a)); // 张三10
let p: Person = {
// 此处传的参数如果与接口的参数数量或类型不一致,那么会报错
name: '李四',
age: 22
}
console.log(getName(p)); // 李四22
}
2.基本数据类型的兼容性
num1中的类型满足str1中使用,但是str1中的类型不满足num1中定义的类型
namespace b {
let num1: string | number;
let str1: string = '张三';
// 这种写法可以
num1 = str1;
// 报错:不能将类型“string | number”分配给类型“string”。不能将类型“number”分配给类型“string”。ts(2322)
// str1 = num1;
}
3.类的兼容性
namespace c {
class Person { name: string };
class Man extends Person { age: number };
let a: Person = new Person();
let b: Man = new Man();
a = b;
// 如果父类和子类中属性相同,那么此处不会报错,
// 如果属性不同会报错类型 "Person" 中缺少属性 "age",但类型 "Man" 中需要该属性。ts(2741)
// b = a;
}
4.函数的兼容性
- 比较参数:参数只能少不能多
- 比较返回值:返回值只能多不能少
namespace d {
// 参数比较
type Fn = (a: number, b: number) => void;
let sum: Fn;
// 参数可以全部传
function fn1(a: number, b: number) { };
sum = fn1;
// 参数可以少
function fn2(a: number) { };
sum = fn2;
function fn3() { };
sum = fn3;
// 参数不能多
function fn4(a: number, b: number, c: number) { };
// 报错:不能将类型“(a: number, b: number, c: number) => void”分配给类型“Fn”。ts(2322)
// sum = fn4;
// 返回值比较
type Person = () => { name: string, age: number };
let person: Person;
// 返回值可以一致
function p1() {
return { name: '张三', age: 10 }
}
person = p1;
// 返回值可以多
function p2() {
return { name: '李四', age: 20, sex: '男' }
}
person = p2;
// 返回值不能少
function p3() {
return { name: '王五' }
}
// 不能将类型“() => { name: string; }”分配给类型“Person”。
// 类型 "{ name: string; }" 中缺少属性 "age",但类型 "{ name: string; age: number; }" 中需要该属性。ts(2322)
// person = p3;
}
5.泛型的兼容性
namespace e {
interface Empty1<T> {}
let x1: Empty1<string>;
let y1: Empty1<number>;
// 此时不会报错,可正常运行
x1 = y1;
interface Empty2<T> {
data: T;
}
let x2: Empty2<string>; // { data: string }
let y2: Empty2<number>; // { data: number }
// 报错:不能将类型“Empty2<number>”分配给类型“Empty2<string>”。不能将类型“number”分配给类型“string”。ts(2322)
// x2 = y2;
}
6.补充:函数的协变与逆变
- 参数是逆变的, 可以传给父类
- 返回值的类型是协变的, 可以传给子类
namespace a {
class Person { };
class Man extends Person {
public name: string = '张三';
};
class Boy extends Man {
public age: number = 10;
};
class OldMan extends Man {
public address: string = '北京';
};
let person: Person;
let man: Man;
let boy: Boy;
let oldMan: OldMan;
type CallBack = (man: Man) => Man;
function exec(callBack: CallBack): void { };
/**
* 参数可以传自己和自己的父类
* 返回值可以传自己和自己的子类
* 四种情况:
* - 参数传子类,返回值传子类
* - 参数传子类,返回值传父类
* - 参数传父类,返回值传父类
* - 参数传父类,返回值传子类
*/
type ChildToChild = (boy: Boy) => Boy;
let childToChild: ChildToChild;
// 类型“ChildToChild”的参数不能赋给类型“CallBack”的参数。
// 参数“boy”和“man” 的类型不兼容。
// 类型 "Man" 中缺少属性 "age",但类型 "Boy" 中需要该属性。ts(2345)
// exec(childToChild);
type ChildToParent = (boy: Boy) => Person;
let childToParent: ChildToParent;
// 类型“ChildToParent”的参数不能赋给类型“CallBack”的参数。
// 参数“boy”和“man” 的类型不兼容。
// 类型 "Man" 中缺少属性 "age",但类型 "Boy" 中需要该属性。ts(2345)
// exec(childToParent);
type ParentToParent = (person: Person) => Person;
let parentToParent: ParentToParent;
// 类型“ParentToParent”的参数不能赋给类型“CallBack”的参数。
// 类型 "Person" 中缺少属性 "name",但类型 "Man" 中需要该属性。ts(2345)
// exec(parentToParent);
type ParentToChild = (person: Person) => Boy;
let parentToChild: ParentToChild;
exec(parentToChild);
}
总结
本文记录了ts中参数及返回值的兼容性,以及函数的协变与逆变。下次再见