目录
类型兼容性
对象类型兼容性
接口类型兼容性
函数类型兼容性
交叉类型
类型兼容性
在TS中,类型采用的是结构化类型系统,也叫做 duck typing(鸭子类型),类型检查关注的是值所具有的形状。也就是说,在结构化系统中,如果两个对象具有相同形状,则认为他们属于同一类型。
class obj {x: number; y: number}
class obj1 {x: number; y: number}
// 因为TS的结构化类型,只检查obj与obj1的结构是否相同
const p: obj = new obj1()
注意:如果在 Nominal Type System中(比如:C#、java等),它们是不同的类,类型无法兼容。
对象类型兼容性
在结构化系统中,如果两个对象具有相同的形状,则认为他们属于同一类型。这种条件成立的前提在于成员多的可以赋予成员少的,反之则报错。如下:
class obj {x: number; y: number}
class obj1 {x: number; y: number; z: number}
// 成员多的 obj1 可以赋值给成员少的 obj
const p: obj = new obj1()
// const p1: obj1 = new obj() 成员少不能赋值给成员多的,报错
接口类型兼容性
接口类型兼容性,类似于class。并且class和interface之间也可以兼容。
interface person {
name: string
age: number
}
interface person1 {
name: string
age: number
}
interface person2 {
name: string
age: number
say: ()=> void
}
let p1: person = {name:'张三',age:18}
let p2: person1 = {name:'李四',age:17}
let p3: person2 = {name:'王五',age:16,say(){}}
p2=p1
p1=p2
// p3=p1 报错
// 类和接口之间也可以兼容
class person3 {
name: string
age: number
say: ()=> void
}
let p4: person3 = {name:'陈六',age:15,say(){}}
p2=p4
函数类型兼容性
函数类型的兼容性比较复杂,需要考虑以下三种情况:
参数个数:参数多的兼容参数少的,即参数少的可以赋值给参数多的。
type F1 = (a: number) => void
type F2 = (a: number,b: number) => void
let f1: F1 = (a=1)=>{}
let f2: F2
f2=f1
// f1=f2 参数多的不能赋值给参数少的 报错
参数类型:相同位置的参数类型要相同(原始类型)或兼容(对象类型)。
type F1 = (a: number) => void
type F2 = (a: number) => void
let f1: F1 = ()=>{}
let f2: F2 = ()=>{}
f1 = f2
在对象类型中,参数可能很多,这个时候就需要参数多的兼容参数少的,即参数少的能赋值给参数多的,如下:
interface point1 {
name:string
age:number
}
interface point2 {
name:string
age:number
say():void
}
type F1 = (P:point1) => void // 相当于两个参数
type F2 = (P:point2) => void // 相当于三个参数
let f1:F1=()=>{}
let f2:F2
f2=f1
返回值类型:只关注返回值类型本身即可。如果返回值类型是原始类型,类型之间要相同;如果返回值类型是对象类型,成员多的可以赋值给成员少的。
// 原始类型
type F1 = ()=> string
type F2 = ()=> string
let f1: F1 = ()=>{return '1'}
let f2: F2 = f1
// 对象类型
type F3 = {name:string}
type F4 = {name:string,age:number}
let f3: F3
let f4: F4 = {name:'张三',age:18}
f3=f4
交叉类型
交叉类型(&):功能类似于接口继承(extends),用于组合多个类型为一个类型(常用于对象类型),使用教程类型后,新的类型就具备被交叉的类型的所有属性类型。如下:
interface Cat {
name:string
}
interface Dog {
say():void
}
// Animals属性同时具备 接口 Cat和Dog 的所有属性
type Animals = Cat & Dog
let p: Animals = {
name:'毛',
say(){
console.log('汪汪');
}
}
交叉类型(&)与 接口继承(extends)的对比:
相同点:都可以实现对象类型的组合。
不同点:两种方式实现类型组合时,对于同名属性之间,处理类型冲突的方式不同。
当不同的接口类型出现同名属性不兼容的时候,接口继承处理的方法是报错:
interface Cat {
fn:(value:number)=>string
}
interface Dog extends Cat {
fn:(value:string)=>string
}
当不同的接口类型出现同名属性不兼容的时候,交叉类型处理的方法是处理成联合类型:
interface Cat {
fn:(value:number)=>string
}
interface Dog {
fn:(value:string)=>string
}
type Animals = Cat & Dog
// 交叉类型将fn的参数值类型变为联合类型 fn:(value:number|string) => string
// ! 作用强调这个值不为空
let p!: Animals
// 没有报错
p.fn(1)
p.fn('2')