typescript中的类型兼容性
函数兼容性
函数参数个数
比如有如下两个函数:
let x = (a: number) => 0
let y = (b: number, c: string) => 0
函数参数个数如果要兼容,需要满足条件:如果对函数 y 进行赋值,则 x 中的每个参数都应在 y 中有对应,也就是 x 的参数个数小于 y 的参数个数。所以有如下结果:
y = x // 没问题
x = y // error,不能将类型“(b: number, c: string) => number”分配给类型“(a: number) => number”。
所以在函数兼容性中,参数个数少的可以赋值给参数个数多的。
函数参数类型和返回值类型
除了参数个数,参数的类型也需要对应,有如下三个函数:
let x = (a: number) => 0 let y = (b: string) => 0 let z = (c: string) => false
x 和 y 两个函数的参数个数和返回值都相同,但是参数类型对应不上,所以互相不兼容:
x = y // error,不能将类型“(b: string) => number”分配给类型“(a: number) => number”。参数“b”和“a” 的类型不兼容。
y 和 z 两个函数的参数个数和参数类型都相同,但是返回值类型对应不上,所以互相不兼容:
y = z // error,不能将类型“(c: string) => boolean”分配给类型“(b: string) => number”。不能将类型“boolean”分配给类型“number”。
我们来改造下 y 函数,如下:
let y = (b: string): number | boolean => 0 let z = (c: string) => false y = z // 没问题
y 函数改造后返回联合类型 number | boolean ,所以可以将 z 赋值给 y。
剩余参数和可选参数
当要被赋值的函数参数中包含剩余参数(...args)时,赋值的函数可以用任意个数的参数代替,但类型要对应,如下:
let x = (...args: number[]):number => 0 let y = (a: number, b: number) => a + b let z = () => 0 x = y // 没问题 x = z // 没问题
x 函数中参数是 ...args,表示可以有 0 个参数,也可以有无限多个参数,所以 y 和 z 函数都可以赋值给 x,前提是参数类型是一样的。
剩余参数可以看成无数个可选参数,来看个可选参数和剩余参数结合的例子:
const getNum = ( arr: number[], // 函数第一个参数是一个各项 number 类型的数组 callback: (arg1: number, arg2?: number) => number // 第二个参数是函数,它有一个或者两个参数 ): number => { return callback(...arr) // 应有 1-2 个参数,但获得的数量大于等于 0 }
例子中函数实体里 callback(...arr) 实现了参数中的 callback,callback 参数个数应为 1 个或者 2 个,但是 ...arr 表示参数个数可能是 0 个或者是无限个,当为 0 个时就会不兼容报错。改成下面这样就没问题了:
return callback(arr[0], ...arr)
函数参数双向协变
函数参数双向协变即参数类型无需绝对相同,比如:
let A = function(arg: number|string): void{} let B = function(arg: number): void{} A = B // 可以 B = A // 可以
这个例子中,A 和 B 的参数类型并不是完全一样的,A 的参数类型是一个联合类型 number | string,而 B 的参数类型是 number | string 中的 number,这两个函数兼容。但需要注意的是在严格模式下,A = B 不通过,B = A 通过。严格模式的开启是在 tsconfig.json 文件中去掉 "strict": true 的注释。
函数重载
function funcA(arg1: number, arg2: number): number; function funcA(arg1: string, arg2: string): string; function funcA(arg1: any, arg2: any): any{ return arg1 + arg2 } function funcB(arg1: number, arg2: number): number; function funcB(arg1: any, arg2: any): any{ return arg1 + arg2 } let fn = funcA fn = funcB // error,不能将类型“(arg1: number, arg2: number) => number”分配给类型“{ (arg1: number, arg2: number): number; (arg1: string, arg2: string): string; }”
例子中 funcB 的重载缺少参数都为 string 类型,返回值为 string 类型的情况,与函数 funcA 不兼容。
枚举
数字枚举成员类型与数字类型兼容,比如:
enum Status{ On, Off } let s = Status.On s = 2 s = 3
Status.On 的值为 0,数字枚举成员类型和数值类型互相兼容,所以 s 可以赋值为数字类型。
但是不同枚举类型之间是不兼容的:
enum Status{ On, Off } enum Color{ White, Black } let s = Status.On s = Color.White // error,不能将类型“Color.White”分配给类型“Status”
虽然 Status.On 和 Color.White 的值都为 0,但是它们不兼容。
字符串枚举成员类型与字符串类型不兼容,比如:
enum Status{ On = 'on', Off = 'off' } let s = Status.On s = 'hi' // error,不能将类型“"hi"”分配给类型“Status”
类
比较两个类类型值的兼容性时,只比较实例的成员,类的静态成员和构造函数不进行比较:
class Animal{ static age: number // 静态属性 age 是 number 类型 constructor(public name: string){} } class People{ static age: string // 静态属性 age 是 string 类型 constructor(public name: string){} } class Food{ constructor(public name: number){} // name 是 number 类型 } let a: Animal let p: People let f: Food a = p // 通过 a = f // error,不能将类型“Food”分配给类型“Animal”。属性“name”的类型不兼容。
Animal 和 People 的静态属性 age 的类型虽然不相同,但是比较兼容性时不比较静态属性,而它们都有实例属性 name,且都为 string 类型,所以 a = p 可行。Food 的实例属性 name 是 number 类型,所以 a = f 错误。
类的私有成员和受保护成员会影响兼容性。比较两个类的兼容性时,当发现有私有成员或者受保护成员时,只有这些成员来自同一个类时才能兼容,即允许子类赋值给父类:
class Parent{ private age: number constructor(){} } class Children extends Parent{ constructor(){ super() } } class Other{ private age: number constructor(){} } const children: Parent = new Children() const other: Parent = new Other() // error,不能将类型“Other”分配给类型“Parent”。类型具有私有属性“age”的单独声明。
泛型
因为TypeScript是结构性的类型系统,类型参数只影响使用其做为类型一部分的结果类型。比如:
interface Data<T>{} let data1: Data<string> let data2: Data<number> data1 = data2 // 通过
虽然两次类型参数 T 的类型不一样,但是因为接口里并没有用到参数 T,所以不管传入什么类型都不影响,两者兼容。相反如果接口中用到参数 T,则结果就相反:
interface Data<T>{ data: T } let data1: Data<string> let data2: Data<number> data1 = data2 // error,不能将类型“Data<number>”分配给类型“Data<string>”。不能将类型“number”分配给类型“string”。
以上就是类型兼容性的相关知识点。