TypeScript 高级类型-类型兼容性

学习目标:

2.类型兼容性
简介理解为: 对象-多赋值少, 函数-少赋值多

类型兼容性

类-兼容性

两种类型系统:1 Structural Type System(结构化类型系统) 2 Nominal Type System(标明类型系统)。
TS 采用的是结构化类型系统 ,也叫做 duck typing(鸭子类型), 类型检查关注的是值所具有的形状
也就是说,在结构类型系统中,如果两个对象具有相同的形状,则认为它们属于同一类型。
//两个类的兼容性演示:

class Point {
  x: number
  y: number
}

class Point2D {
  x: number
  y: number
}

//如果两个对象具有相同的形状,则认为它们属于同一类型。
const p:Point = new Point2D();  //不报错

// 解释:

// 1. Point 和 Point2D 是两个名称不同的类。

// 2. 变量 p 的类型被显示标注为 Point 类型,但是,它的值却是 Point2D 的实例,并且没有类型错误。

// 3. 因为 TS 是结构化类型系统,只检查 Point 和 Point2D 的结构是否相同(相同,都具有 x 和 y 两个属性,属性类型也相同)。

// 4. 但是,如果在 Nominal Type System 中(比如,C#、Java 等),它们是不同的类,类型无法兼容。(简单理解为类名相同才兼容)


对象-兼容性

注意:在结构化类型系统中,如果两个对象具有相同的形状,则认为它们属于同一类型,这种说法并不准确。
更准确的说法:对于对象类型来说,y 的成员至少与 x 相同,则 x 兼容 y( 成员多的可以赋值给少的

//对象-兼容性


class Point {
  x: number
  y: number
}
class Point3D{
  x: number
  y: number
  z: number
}

const pp:Point = new Point3D()

//错误演示
//const p2:Point3D = new Point() //报错
// 类型 "Point" 中缺少属性 "z",但类型 "Point3D" 中需要该属性。

// 解释:

// 1. Point3D 的成员至少与 Point 相同,则 Point 兼容 Point3D。

// 2. 所以,成员多的 Point3D 可以赋值给成员少的 Point。


接口-兼容性

除了 class 之外,TS 中的其他类型也存在相互兼容的情况,包括:1 接口兼容性 2 函数兼容性 等。
接口之间的兼容性,类似于 class 。并且, class 和 interface 之间也可以兼容

// 成员多的可以赋值给少的。
//接口-兼容性
interface Point {
  x: number
  y: number
}

interface Point2D {
  x: number
  y: number
}

interface Point3D {
  x: number
  y: number
  z: number
}

//初始化
let p1: Point 
let p2: Point2D = {x: 1, y: 2}
let p3: Point3D = {x: 3, y: 4, z: 5}

p1 = p2;
p2 = p3;
p1 = p3;

//错误演示:
// p3 = p1;
//类型 "Point" 中缺少属性 "z",但类型 "Point3D" 中需要该属性
// p3 = p2;
//类型 "Point2D" 中缺少属性 "z",但类型 "Point3D" 中需要该属性。

接口之间的兼容性,class 和 interface 之间也可以兼容

//在class中

//在class中

// l. 接口之间的兼容性,类似于 class。并且,class 和 interface 之间也可以兼容。
class  Add {
  x: number
  y: number
  z: number
}
//类和接口之间也是兼容的
let add:Point = new Add()

p2 = new Add()


函数-兼容性

函数之间兼容性比较复杂 ,需要考虑:1. 参数个数 2. 参数类型 3. 返回值类型。

//参数少的可以赋值给多的

参数个数

1. 参数个数 ,参数多的兼容参数少的(或者说, 参数少的可以赋值给多的 )。

// 函数-兼容性

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

// 1. 参数少的可以赋值给参数多的

//初始化
let f1: F1 = function(){}
let f2: F2
//参数少的可以赋值给多的,所以,f1 可以赋值给 f2。
f2 = f1;

//错误示范:
//f1 = f2;

// 2. 数组 forEach 方法的第一个参数是回调函数,该示例中类型为:(value: string, index: number, array: string[]) => void。

const arr = ['a', 'b', 'c']
//传一个或者二个,三个,甚至可以一个都不传
arr.forEach(() => {})
arr.forEach((item,index,array) => {})

// 解释:

// 1. 参数少的可以赋值给参数多的

// 2. 数组 forEach 方法的第一个参数是回调函数,该示例中类型为:(value: string, index: number, array: string[]) => void。

// 3. 在 JS 中省略用不到的函数参数实际上是很常见的,这样的使用方式,促成了 TS 中函数类型之间的兼容性。

// 4. 并且因为回调函数是有类型的,所以,TS 会自动推导出参数 item、index、array 的类型。

参数类型

//参数类型可以少,不能多

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

原始类型

//原始类型:
type Num1 = (a: number) => void
type Num2 = (a: number) => void

//初始化
let sum1:  Num1 = function() {};
let sum2:  Num2 = function() {};

sum1 = sum2;
sum2 = sum1;

// 解释:函数类型 Num2 兼容函数类型 Num1,因为 Num1 和 Num2 的第一个参数类型相同。

对象类型

我不不理解

 参数类型,相同位置的参数类型要相同或兼容。

// 解释:

// 1. 注意,此处与前面讲到的接口兼容性冲突。

// 2. 技巧:将对象拆开,把每个属性看做一个个参数,则,参数少的(f2)可以赋值给参数多的(f3)。

返回值类型

//返回值类型,可以多,不能少

原始类型


//  返回值类型,只需要关注返回值类型本身即可

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

let f5: F5 
let f6: F6 

f6 = f5
f5 = f6

对象类型

// 对象类型:
type F7 = () => { name: string }
type F8 = () => { name: string; age: number }

let f7: F7
let f8: F8

f7 = f8

// 错误演示
// f8 = f7


 

// 解释:

// 1. 如果返回值类型是原始类型,此时两个类型要相同,类型 F5 和 F6。

// 2. 如果返回值类型是对象类型,此时成员多的可以赋值给成员少的,比如,类型 F7 和 F8。


猜你喜欢

转载自blog.csdn.net/zysskt/article/details/128318536