TypeScript 类型兼容性运用
发布时间:2023-03-30 14:00:18 所属栏目:教程 来源:
导读:我们学习类型兼容性,就是在学习 TypeScript 在一个类型能否赋值给其他类型的规则。本节将会详细介绍 TypeScript 在函数、枚举、类和泛型中的类型兼容性规则。
类型兼容性用于确定一个类型是否能赋值给其他类型。
类型兼容性用于确定一个类型是否能赋值给其他类型。
我们学习类型兼容性,就是在学习 TypeScript 在一个类型能否赋值给其他类型的规则。本节将会详细介绍 TypeScript 在函数、枚举、类和泛型中的类型兼容性规则。 类型兼容性用于确定一个类型是否能赋值给其他类型。 TypeScript 的类型检查机制都是为了让开发者在编译阶段就可以直观的发现代码书写问题,养成良好的代码规范从而避免很多低级错误。 let address: string = 'Baker Street 221B' let year: number = address = year // Error 代码解释: 第 3 行,类型 ‘number’ 不能赋值给类型 ‘string’。 TypeScript 类型兼容性是基于结构类型的;结构类型只使用其成员来描述类型。 TypeScript 结构化类型系统的基本规则是,如果 x 要兼容 y,那么 y 至少具有与 x 相同的属性。比如: interface User { name: string, year: number } let protagonist = { name: 'Sherlock·Holmes', year: , address: 'Baker Street 221B' } let user: User = protagonist // OK 代码解释: 接口 User 中的每一个属性在 protagonist 对象中都能找到对应的属性,且类型匹配。另外,可以看到 protagonist 具有一个额外的属性 address,但是赋值同样会成功。 相对来讲,在比较原始类型和对象类型的时候是比较容易理解的,难的是如何判断两个函数是否兼容。判断两个函数是否兼容,首先要看参数是否兼容,第二个还要看返回值是否兼容。 先看一段代码示例: let fn1 = (a: number, b: string) => {} let fn2 = (c: number, d: string, e: boolean) => {} fn2 = fn1 // OK fn1 = fn2 // Error 代码解释: 第 4 行,将 fn1 赋值给 fn2 成立是因为: fn1 的每个参数均能在 fn2 中找到对应类型的参数 参数顺序保持一致,参数类型对应 参数名称不需要相同 第 5 行,将 fn2 赋值给 fn1 不成立,是因为 fn2 中的必须参数必须在 fn1 中找到对应的参数,显然第三个布尔类型的参数在 fn1 中未找到。 参数类型对应即可,不需要完全相同: let fn1 = (a: number | string, b: string) => {} let fn2 = (c: number, d: string, e: boolean) => {} fn2 = fn1 // OK 代码解释: fn1 的第一个参数是 number 和 string 的联合类型,可以对应 fn2 的第一个参数类型 number,所以第 4 行赋值正常。 创建两个仅是返回值类型不同的函数: let x = () => ({name: 'Alice'}) let y = () => ({name: 'Alice', location: 'Seattle'}) x = y // OK y = x // Error 代码解释: 最后一行,函数 x() 缺少 location 属性,所以报错。 类型系统强制源函数的返回值类型必须是目标函数返回值类型的子类型。由此可以得出如果目标函数的返回值类型是 void,那么源函数返回值可以是任意类型: let x : () => void let y = () => 'imooc' x = y // OK 枚举与数字类型相互兼容: enum Status { Pending, Resolved, Rejected } let current = Status.Pending let num = current = num num = current 不同枚举类型之间是不兼容的: enum Status { Pending, Resolved, Rejected } enum Color { Red, Blue, Green } let current = Status.Pending current = Color.Red // Error 类与对象字面量和接口的兼容性非常类似,但是类分实例部分和静态部分。 比较两个类类型数据时,只有实例成员会被比较,静态成员和构造函数不会比较。 class Animal { feet!: number constructor(name: string, numFeet: number) { } } class Size { feet!: number constructor(numFeet: number) { } } let a: Animal let s: Size a = s! // OK s = a // OK 代码解释: 类 Animal 和类 Size 有相同的实例成员 feat 属性,且类型相同,构造函数参数虽然不同,但构造函数不参与两个类类型比较,所以最后两行可以相互赋值。 类的私有成员和受保护成员会影响兼容性。 允许子类赋值给父类,但是不能赋值给其它有同样类型的类。 class Animal { protected feet!: number constructor(name: string, numFeet: number) { } } class Dog extends Animal {} let a: Animal let d: Dog a = d! // OK d = a // OK class Size { feet!: number constructor(numFeet: number) { } } let s: Size a = s! // Error 代码解释: 第 13 行,子类可以赋值给父类。 第 14 行,父类之所以能够给赋值给子类,是因为子类中没有成员。 最后一行,因为类 Animal 中的成员 feet 是受保护的,所以不能赋值成功。 (编辑:汽车网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |