类型兼容性就是看一个类型能赋值给其他类型。Typescript 的兼容性是基于结构类型的。如果x要兼容y,那么y至少具有与x相同的属性。
基本类型
// 基本类型兼容性
let num:string | number;
let str: string = 'str'
num = str
let num2:{ // 有个 toString方法,并且返回string
toString():string
}
let str2:string = 'll' // 字符串也有个toString 方法
num2=str2
str2 = num2 // 不能将类型“{ toString(): string; }”分配给类型“string” num2 至少要具有与str2 相同的的属性
接口兼容
interface Animal{
name: string;
age: number
}
interface Person1{
name: string
age: number;
gender: string
}
function getName(a: Animal):string{
return a.name
}
let a1: Animal = {
name: '',
age: 10
}
getName(a1)
let p: Person1 = {
name: '',
age: 10,
gender: 'man'
}
getName(p) // 因为Animal里的属性,Person里都有
let p2 = {
name: '',
}
getName(p2) // Property 'age' is missing in type '{ name: string; }' but required in type 'Animal'.ts(2345)
类类型兼容性
类类型的兼容性,只比较实例成员和方法,类的静态成员和构造函数不进行比较:
class Animal {
static age: number;
constructor(public name: string) {}
}
class People {
static age: string;
constructor(public name: string) {}
}
class Food {
constructor(public name: number) {}
}
let a: Animal;
let p: People;
let f: Food;
a = p; // ok
a = f; // error Type 'Food' is not assignable to type 'Animal'
函数类型兼容性
1.参数
(1)参数类型
let x = (a: number) => 0;
let y = (b: number, s: string) => 0;
y = x; // OK x是否能赋值给y, 就看x的每个参数必须能在y里找到对应类型的参数。
x = y; // Error y有个必需的第二个参数类型string,但是x并没有,所以不允许赋值。
(2)参数个数
参数个数只能少不能多
let x = (a: number) => 0;
let y = (b: number, s: string) => 0;
y = x; // OK x 赋值给y,y有两个参数,x的参数个数只能小于等于y的参数个数。
x = y; // Error y参数个数只能少于等于 x 的参数个数
为什么允许忽略参数,像例子y = x中那样。 原因是忽略额外的参数在JavaScript里是很常见的。 例如,Array#forEach给回调函数传3个参数:数组元素,索引和整个数组。 尽管如此,传入一个只使用第一个参数的回调函数也是很有用的。
(3)剩余参数和可选参数
const getNum = ( // 这里定义一个getNum函数,他有两个参数
arr: number[], // 第一个参数是一个数组
callback: (...args: number[]) => number // 第二个参数是一个函数,这个函数的类型要求可以传入任意多个参数,但是类型必须是数值类型,返回值必须是数值类型
): number => {
return callback(...arr); // 这个getNum函数直接返回调用传入的第二个参数这个函数,以第一个参数这个数组作为参数的函数返回值
};
getNum(
[1, 2],
(...args: number[]): number => args.length // 这里传入一个函数,逻辑是返回参数的个数
);
const getNum = (
arr: number[],
callback: (arg1: number, arg2?: number) => number // 这里指定第二个参数callback是一个函数,函数的第二个参数为可选参数
): number => {
return callback(...arr); // error 应有 1-2 个参数,但获得的数量大于等于 0
};
2.返回值
返回值个数只能大于等于不能少于。
let x = () => ({name: 'Alice'});
let y = () => ({name: 'Alice', location: 'Seattle'});
x = y; // OK y 赋值给x,y的返回值个数只能大于或等于x返回值的个数
y = x; // Error, because x() lacks a location property
3.函数重载
4.函数的协变与逆变
A ≼ B,意味着A是B的子类型
返回值类型时协变,参数类型时逆变。
返回值可以传子类,参数可以传父类。
class Animal3{}
class Dog3 extends Animal3{}
class BlackDog extends Dog3{}
class WhiteDog extends Dog3{}
let animal3: Animal3
let blackDog: BlackDog
let whiteDog: WhiteDog
type callback = (dog: Dog3) => Dog3 // 函数
function exec(callback: callback): void{ // 参数是callback
callback(whiteDog)
}
type ChildToChild = (blackDog: BlackDog) => BlackDog
let childToChild: ChildToChild
exec(childToChild) // error
type ChildToParent = (blackDog: BlackDog) => Animal3
let childToParent:ChildToParent
exec(childToParent) // error
type ParentToParent = (animal3: Animal3) => Animal3
let parentToParent: ParentToParent
exec(parentToParent) // error
type ParentToChild = (animal3: Animal3) => BlackDog
let parentToChild: ParentToChild
exec(parentToChild) //
四种情况:
参数是子类,返回值是子类
参数是子类,返回值是父类
参数是父类,返回值是父类
参数是父类,返回值是子类
只有最后一种情况才正确
参数可以传自己和自己的父类,返回值可以传自己和自己的子类。
泛型类型兼容性
class GrandFather1{
grandFather: string = ''
}
class Father1 extends GrandFather1{
father: string = ''
}
class Child1 extends Father{
child: string = ''
}
function get<T extends Father>(){
}
get<GrandFather1>; //类型“GrandFather1”不满足约束“Father”。类型 "GrandFather1" 中缺少属性 "father",但类型 "Father" 中需要该属性
get<Child>()