TypeScript 入门:高级类型
类型断言
const nums = [2,66,88]// TS 的类型推断无法确定res 的结果const res = nums.find(i => i >0)// 而此时,res 就不能当作纯数字使用// const t = res * res // res 报错// 我们根据代码逻辑,知道res 一定是number,就可以直接断言const n1 = res as number// 使用res断言后的结果 n1const n2 = n1 * n1console.log(n2)
断言的语法规则就是变量xx as 类型xx或者<类型xx> 变量xx,意思就是告诉 TS ,我的代码一定是某种类型的,放心使用。<类型xx> 变量 xx 的方式在 React 中会与 JSX 语法规则冲突,因此一般更建议使用 as 语法规则。
接口 Interfaces
基本使用
functionprint(post:Objpost){// 传入的对象中,必须有 title 和 contentconsole.log(post.title)console.log(post.content)}// 但是,并不能保证调用者一定传入符合条件的对象print({name:'xiling'})使用接口进行内容约束:// 定义接口interface Objpost{// 属性名:类型 //使用逗号和分号或者不写都可以title:stringcontent:number}// 形参使用接口,标注传入的内容,符合接口的定义functionprint(post:Objpost){// 传入的对象中,必须有 title 和 contentconsole.log(post.title)console.log(post.content)}print({name:'xiling'}) // 报错print({title:'lisi',content:66})

可选与只读属性
// 定义接口interface Objpost {// 属性名:类型 //使用逗号和分号或者不写都可以title: stringcontent: numbersubtitle?: string // 可选成员属性readonly summary: string // 只读属性,一旦赋值则不可修改}// 形参使用接口,标注传入的内容,符合接口的定义functionprint(post: Objpost) {// 传入的对象中,必须有 title 和 contentconsole.log(post.title)console.log(post.content)}let obj:Objpost = { title:'lisi',content:66,summary:'xiling' }obj.summary='liuneng'// 修改只读属性,报错print(obj)
动态成员
interfaceCache {// [定义动态成员:类型]:值类型[props:string]:string}const obj: Cache = {}// 动态添加对象成员obj.title='javaScript'obj.content='html'console.log(obj)
类的使用
基本使用
class Person{// 在类中初始化属性定义name:string='lisi'// 定义初始值age:numberconstructor(name:string,age:number){this.name=namethis.age=agethis.sex='男'// 没有初始化的成员会报错}SayHi(msg:string):void{console.log(`Hello ${this.name},${msg}`)}}
访问修饰符
class Person{// 类中的修饰符有三种 public private protected// public 公有属性,任何地方都可以访问// protected 受保护属性,只有自己和继承的子类中能访问// private 私有属性,只在类内部访问public name:string='lisi'private age:numberprotected gender:booleanconstructor(name:string,age:number){this.name=namethis.age=agethis.gender=true}SayHi(msg:string):void{console.log(`Hello ${this.name},${msg}`)console.log(this.gender)}}class Student extends Person{sya(){this.gender}}let stu =newStudent('lisi',66)stu.namestu.age// 私有属性,外部访问报错stu.gender// 受保护属性,外部访问报错
类的只读属性
class Person{// 设置成员为只读属性,一旦赋值,不可修改public readonly name:string='lisi'constructor(name:string,age:number){this.name=name}SayHi(msg:string):void{this.name='liuneng'// 修改报错}}
类与接口
// 人和动物都有运动和吃东西的行为// 都有这样的行为,但确实不一样的;// 这样的情况下,我们就可以使用接口描述不同的行为约束class Person {eat(food: string) {console.log(`使用筷子:${food}`)}run(distance: number) {console.log(`双脚:${distance}`)}}class Animal {eat(food: string) {console.log(`撕咬的吃:${food}`))}run(distance: number) {console.log(`四蹄${distance}`)}}
人和动物都有运动和吃东西的行为,都有这样的行为,但确实不一样的。这样的情况下,我们就可以使用接口描述不同的行为约束。
// 接口只做行为的约束,不做具体方法的实现interface eatAndrun {eat(food: string):voidrun(distance: number):void}// 使用 implements 关键字进行约束class Person implements eatAndrun {eat(food: string) {console.log(`使用筷子:${food}`)}// 如果缺少约束的成员,则会报错// run(distance: number) {// console.log(`双脚:${distance}`)// }}
但是在实际的开发中,接口约束会更加的细致,就类似摩托车也能跑,但并不是人或动物,细致的约束会更加灵活。
// 对接口进行细致化的拆分interface eat {eat(food: string):void}interface run {run(distance: number):void}// 使用多个接口,需要逗号,隔开class Person implements eat,run {eat(food: string) {console.log(`使用筷子:${food}`)}// 如果缺少约束的成员,则会报错run(distance: number) {console.log(`双脚:${distance}`)}}
抽象类与抽象方法
抽象类
与接口类似,但是接口只能进行规范约束不能具体实现,而抽象类可以具体实现。抽象类的定义很简单,只需要在类声明的前面添加 abstract 就可以了。
abstract class Animal {eat(food:string):void{console.log(`撕咬的吃:${food}`)}}
类一旦被抽象,就不能被实例化,只能使用子类继承使用。
// newAnimal() // 报错--无法创建抽象类的实例class Dog extends Animal{}
抽象方法
abstract class Animal {eat(food:string):void{console.log(`撕咬的吃:${food}`)}// 定义抽象方法run , 需要在继承的子类中实现(必须实现)abstract run(distance:number):void}class Dog extends Animal{// 抽象方法必须在子类中实现run(distance: number):void {console.log(distance)}}
泛型
在定义函数、接口或者类的时候,没有指定具体的类型,等到使用时才会具体指定的一种特征。
// 函数定义时,参数及返回值并不能确定类型// 或者,函数需要在运行时固定类型// 函数名后面使用 <占位符> ,在不确定类型的地方,使用 :占位符function createArray<T>(length: number, value: T): T[] {const arr =Array<T>(length).fill(value)return arr}// 函数调用时,使用函数名<泛型的具体类型> (实参1,实参2)createArray<string>(3,'xl')
类型声明
我们在进行项目开发时,肯定会用到一些第三方模块,但是这些第三方模块不一定是 TS 的,那就无法使用强类型的友好开发体验。
以 lodash 为例,使用 npm install lodash 安装后,通过 import 语法引入。
// 无法找到模块“lodash”的声明文件。import {camelCase} from'lodash'// camelCase 将字符串转为驼峰格式const res =camelCase('hello xiling') // 没有类型提示console.log(res)
引入后就出现了错误提示:'无法找到模块“lodash”的声明文件。'
同时提示中也告诉我们安装 npm install @types/lodash -D 模块来解决这个问题,已开发依赖的方式安装即可,@types/lodash 其实就是声明文件,成功安装之后,操作就消失了。
而有的模块直接拥有声明文件,这时就不需要额外安装扩展库了。
但是,有一些相对陈旧的库,在使用时自身没有声明文件,也没有对应的文件库这就需要我们自己在代码中进行处理了。
我们依然以 lodash 为例,npm uninstall @types/lodash 卸载类型声明文件,模拟这种情况,无法找到模块“lodash”的声明文件。
camelCase 也没有类型提示:因为确实没有类型声明文件,所以,我们尝试解决 camelCase 的类型提示,其实只需要使用 declare 关键字进行标注就可以了:
// 无法找到模块“lodash”的声明文件。import {camelCase} from'lodash'declare functioncamelCase(input:string):string// camelCase 将字符串转为驼峰格式const res =camelCase('hello xiling') // 没有类型提示console.log(res)
Over。
推荐阅读:
更新不易,点个“在看”和“赞”吧(●'◡'●)!
