什么是类型兼容? 当一个类型 Y 可以被赋值给另一个类型 X 时 , 我们就可以说类型 X 兼容 类型 Y X 兼容 Y : X (目标类型) = Y (源类型)
let a : string = "1"; a = null;//正常这样书写会报错, 那么可以把ts的配置strictNullChecks 设置为false即可 //这样就可以说, 字符型 是兼容 null类型的,也就是说null是字符类型的子类型 //这里讨论类型的兼容性问题, 是因为ts在某种情况下,允许我们将不同类型的值进行赋值, 虽然 //有一些不可靠性,但是也增加了灵活性1. 接口的兼容性
interface X{ a : any ; b : any ; } interface Y { a : any ; b : any ; c : any ; } let x : X = { a : 1 , b : 2 }; let y : Y = { a : 1 , b : 2 , c : 3} //这里x可以赋值给y,但是y不能赋值为x ,那是因为源类型必须具备目标类型的必要属性 //接口相互兼容性问题,成员少的可兼容成员多的 x = y; // y = x;报错2… 函数的兼容性 通常发生在两个函数相互赋值的情况下
2.1 函数作为参数的情况下
//1)参数个数 let handler1 = (a : number) => {}; hor(handler1);//可以兼容,参数少的兼容参数多的,且源类型具备了目标类型的必要属性 let handler2 = (a : number , b : number , c : number) => {}; // hor(handler2);//不可兼容,因为参数比源类型多 //可选参数和剩余参数 let a1 = (p1 : number , p2 : number) => {}; let a2 = (p1 ? : number , p2 ? : number) => {}; let a3 = (...args : number[]) => {}; //固定参数可以兼容可选参数和剩余参数 a1 = a2; a1 = a3; //可选参数不可兼容固定参数和剩余参数,但是可以通过配置,让其兼容;strictFunctionTypes:false a2 = a1; a2 = a3; a3 = a1; a3 = a2; //2)参数类型 interface Point3D { X : number ; y : number ; z : number ; } interface Point2D { X : number ; y : number ; } let p3d = ( point : Point3D) => {}; let p2d = ( point : Point2D) => {}; //这里会发现,有违背了之前的定义,应该是参数少的兼容参数多的,但是这里不一样,记特殊情况 //当一个函数的参数是一个对象类型时, 参数多的兼容参数少的 p3d = p2d; //反而参数少的不能兼容参数多的,如果想兼容,可以做ts配置中进行设置,strictFunctionTypes : false p2d = p3d; //小结:这种情况叫做函数的参数双向斜变,可以把一个精确的类型允许赋值给一个不那么精确的类型,这样做很方便 //这样我们可以不用把一个不精确的类型,断言成一个精确的类型 //3)返回值类型 //目标函数的返回类型,必须和源函数类型的返回类型相同 let f = () => ({name : 'Alice'}); let g = () => ({name : 'Alice' , location : "Beijing"}); f = g; // g = f;//返回值类型遵循鸭试变形法, 参数少的兼容参数多的 //函数重载兼容,第一行参数是目标类型,以外的都是源类型,源类型的参数必须和目标类型一致 function overload(a:number , b : number) : number; function overload(a:string , b : string) : string; function overload(a:any , b : any) : any{}; //枚举兼容性 enum Fruit {Apple , Bamama}; enum Color { Red , Yellow}; let fruit : Fruit.Apple = 3; let no : number = Fruit.Apple; //两个枚举之间,是不可兼容的 // let color : Color.Red = Fruit.Apple; //类兼容性 class A { constructor( p : number , q : number) {} id : number = 1; private a = 1; } class B { static s = 1; constructor(p : number) {} id : number = 1; } let aa = new A(1,2); let bb = new B(1); //类的兼容, 不管构造器的参数,和静态成员参数, 只要id参数相同, 即可兼容,注意,将私有属性private去掉即可相互兼容 // aa = bb; // bb = aa; //如果类中有private属性,将不可互相兼容, 只可子类继承父类后,子类实例化可以兼容父类的实例化 class C extends A {}; let cc = new C(1,2); cc = aa; aa = cc; //泛型的兼容性 interface Empty<T>{ value : T }; // let object1 : Empty<string> = {}; // let object2 : Empty<number> = {}; //当泛型接口中,没有参数定义时,是可以相互兼容的 // object1 = object2; //当泛型接口中,有参数定义时,是不可相互兼容的 let log1 = <T>(value : T) : T => { return value; } let log2 = <U>(y : U) : U =>{ return y; } log1 = log2; }总结 口诀: 结构之间的兼容 : 成员少的兼容成员多的: 函数之间的兼容 : 参数多的兼容参数少的