【TS】TypeScript 类型兼容性

文章目录

对象的类型兼容性

类的类型兼容性

接口的类型兼容性

函数的类型兼容性

【函数参数个数】

【函数参数类型】

【函数返回值类型】


TS 采用的是结构化类型系统,类型检查关注的是值的类型所具有的形状。在结构类型系统中,如果两个对象具有相同的形状,则认为它们属于同一类型。

子类型:子类型继承了父类型所有的特性,并添加了父类型没有的特性。

对象的类型兼容性

下面实例中,p1 与 p2 具有相同的类型结构,因此二者之间可以相互赋值(在标明类型系统中(C#、Java 等)类型无法兼容);Point3D 是子类型,具有父类型(Point)的所有特性(x , y),且拥有父类型所不具有的特性(z)。因此子类型(Point3D)变量 p3 可以赋值给父类型(Point)变量 p1,反过来则不可以。

// 定义对象
type Point = { x: number, y: number };
type Point2D = { x: number, y: number };
type Point3D = { x: number, y: number, z: number };

// 两个对象的类型具有相同的形状
let p1: Point = { x: 1, y: 1 };
let p2: Point2D = { x: 2, y: 2 };
p1 = p2;
p2 = p1;
// 两个对象的类型形状不相同--子类型可以传给父类型
let p3: Point3D = { x: 3, y: 3, z: 3 };
p1 = p3;
p3 = p1; // 父类型赋值给子类型  --报错

类的类型兼容性

类也可以理解为是对对象的约束,因此类之间的类型兼容性,类似于对象。并且,class 和 type 之间也可以兼容。

// 定义类
class Point {
    x!: number
    y!: number
}
class Point2D {
    x!: number
    y!: number
}
class Point3D {
    x!: number
    y!: number
    z!: number
}
// 两个类的类型形状相同
let p1: Point = new Point2D();
let p2: Point2D = new Point();
// 子类型可以赋值给父类型
let p3: Point = new Point3D();
let p4: Point3D = new Point(); // 父类型赋值给子类型  --报错

class 与 type 之间的兼容

// 定义类
class Point2D {
    x: number = 2;
    y: number = 2;
}
class Point3D {
    x: number = 3;
    y: number = 3;
    z: number = 3;
}
// 定义对象类型
type tPoint = { x: number, y: number };
let t0: tPoint = { x: 0, y: 0 };
// 类型形状相同
let t1: tPoint = new Point2D();
let t2: Point2D = t0;
// 子类型可以赋值给父类型
let t3: tPoint = new Point3D();
let t4: Point3D = t0; // 父类型赋值给子类型  --报错

接口的类型兼容性

接口也可以表示为对对象的约束, 因此接口之间的类型兼容性,也类似于对象,类似于类。并且,interface 和 class 和 type 之间也可以兼容。

// 定义接口
interface Point {
    x: number
    y: number
}
interface Point2D {
    x: number
    y: number
}
interface Point3D {
    x: number
    y: number
    z: number
}
// 两个变量类型具有相同的形状
let p1: Point = { x: 1, y: 1 };
let p2: Point2D = { x: 2, y: 2 };
p1 = p2;
p2 = p1;
// 子类型可以传给父类型
let p3: Point3D = { x: 3, y: 3, z: 3 };
p1 = p3;
p3 = p1; // 父类型赋值给子类型  --报错

interface 和 class 和 type 之间的兼容

// 定义类型
type Point = { x: number, y: number };
// 定义类
class Point2D {
    x!: number
    y!: number
}
// 定义接口
interface Point3D {
    x: number
    y: number
    z: number
}

let p1: Point = { x: 0, y: 0 };
let p2: Point2D = new Point2D();
let p3: Point3D = { x: 0, y: 0, z: 0 };
// 两个变量类型具有相同的形状
p2 = p1;
p1 = p2;
// 子类型可以传给父类型
p2 = p3;
p3 = p2; // 父类型赋值给子类型  --报错

函数的类型兼容性

函数之间兼容性需要考虑三个因素:1 参数个数 2 参数类型 3 返回值类型

【函数参数个数】

排除参数类型和返回值类型对兼容性的影响,参数多的兼容参数少的(参数少的可以赋值给参数多的

type F1 = (a: number) => void
type F2 = (a: number, b: number) => void

// 参数少的可以赋值给参数多的
let f2: F2 = (a: number) => { };

let f1: F1 = (a: number, b: number) => { }; // --报错

数组的 forEach 方法就是使用的这个特性

【函数参数类型】

相同位置的参数类型要相同(原始类型)或兼容(对象类型)

下面示例中,函数类型 F2 兼容函数类型 F1,因为 F1 和 F2 的第一个参数类型相同。

type F1 = (a: number) => void
type F2 = (a: number) => void
// 相同位置参数类型相同
let f1: F1 = (a: number) => { };
let f2: F2 = f1;

如果参数是对象类型的,可以将对象拆开,每一个属性视为一个参数。(参数多的兼容参数少的)

// 对象类型
type Point2D = {
    x: number
    y: number
}
type Point3D = {
    x: number
    y: number
    z: number
}

type F3 = (p: Point2D) => void // 相当于有 2 个参数
type F4 = (p: Point3D) => void // 相当于有 3 个参数

let f3: F3 = ({ x, y }) => { }
let f4: F4 = f3; // 参数少的赋值给参数多的

【函数返回值类型】

函数的兼容性从返回值考虑,只关注返回值类型本身即可。如果返回值类型是原始类型,此时两个类型要相同;如果返回值类型是对象类型,此时子类型可以赋值给父类型。

// 原始类型:
type F5 = () => string
type F6 = () => string

let f5: F5 = () => { return "a" }
let f6: F6 = f5;

// 对象类型:
type F7 = () => { name: string }
type F8 = () => { name: string; age: number }
// 子类型赋给父类型
let f7: F7 = () => { return { name: "a", age: 0 } };
let f8: F8 = () => { return { name: "a" } }; // 父类型赋给子类型  --报错

TypeScript 教程——阮一峰·著

TypeScript 中文网

TypeScript 官网

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值