TypeScript 是 JavaScript 的一个超集,支持 ECMAScript 6 标准。
TypeScript 由微软开发的自由和开源的编程语言。
TypeScript 设计目标是开发大型应用,它可以编译成纯 JavaScript,编译出来的 JavaScript 可以运行在任何浏览器上。
以上为网上对 Typescript 的一些解释,那我们为什么要学 Typescript?
提到前端我们首先会想到 HTML,CSS,JavaScript 三大家族,我们掌握这三个就可以在前端界获得一席之地,怎么突然又冒出个 Typescript,真心是学不动了,但是众所周知,Vue 创始人尤雨溪尤大大已经宣布 Vue3.x 代码库将使用 Typescript 编写,并且在知乎上对于"Typescript 不适合在 Vue 开发业务中使用吗?" 的提问中做出回答,传送门:https://www.zhihu.com/question/310485097/answer/591869966,Vue 又是现在国内主流的前端框架之一,只能说,如果不学 Typescript,尤大大都救不了你了。
众所周知,从本质上来说,JavaScript是一种自由松散语言,它的语法规则并不是那么严格。正因为如此,我们就更容易犯错,而且,即使是在运行的时候,我们也不能找到所有的错误。鉴于此,TypeScript作为JavaScript的增强版,它的语法更严格,我们在编写代码的时候就能够发现大部分错误。不仅如此,按照TypeScript官方的说法,TypeScript使得我们能够以JavaScript的方式实现自己的构思。TypeScript对面向对象的支持也非常完善,它拥有面向对象编程语言的所有特性。
TypeScript最大的目的是让程序员更具创造性,提高生产力,它将极大增强JavaScript编写应用的开发和调试环节,让JavaScript能够方便用于编写大型应用和进行多人协作。
不过目前最后运行时还需要将TypeScript编译为JavaScript。
那接下来我们就来看看如何安装使用 Typescript。
在安装 Typescript 之前我们要先安装 NodeJs,然后运行
npm install -g typescript
以上命令会在全局环境下安装 tsc
命令,安装完成之后,我们就可以在任何地方执行 tsc
命令了。
编译一个 TypeScript 文件很简单:
tsc demo.ts
运行上面的代码,我们就可以将 demo.ts 生成一个可以让浏览器解析的 demo.js 的文件。
我们约定使用 TypeScript 编写的文件以 .ts
为后缀,用 TypeScript 编写 React 时,以 .tsx
为后缀。
那么我们在编写代码的时候不能每次都手动编译 .ts 文件,我们想要的是实时编译 .ts 文件,接下来我们就以 webstorm 编辑器来使 .ts 文件进行实时编译。其他编辑器可自行百度如何实时编译。
webstorm 版本:
创建一个 demo 项目,然后在项目中创建一个 tsconfig.json 的文件,内容如下:
这里只是简单的配置,详细参数配置:https://www.tslang.cn/docs/handbook/tsconfig-json.html
打开Webstorm,为TypeScript文件更改编译设置,File->Settings->Tool->File Watchers->TypeScript,这里我们需要选择TypeScript,但是File Watchers下默认是不存在的。需要点击右侧“+”号,选择,弹出 New Watcher,设置好圈红线的部分,点击ok。勾选“TypeScript”,点击ok。
File->Settings->Languages & Frameworks->TypeScript
根据上面的操作我们就可以实时编译 .ts 文件了。
目录结构如下,在 index.html 中银润 test.js
test.ts 会实时编译为 test.js 文件。
test.ts
index.html 开发者工具中的打印日志
根据上面的步骤我们就可以对 ts 文件进行实时编译了,接下来我们就来看一下 Typescript 的一些基本用法。
从上图我们可以看出 Typescript 包含了 ES6 和 ES5,那我们就可以在 typescript 中使用 es5 和 es6,同时进行了扩展,语法上跟我们之前讲的 Java 语法有很多相似。
Typescript 基础类型
Typescript 基础类型有:布尔类型(boolean)、数字类型(number)、字符串类型(string)、数组类型(array)、元组类型(tuple)、枚举类型(enum)、任意类型(any)、null 和 undefined 、void、never 类型等,接下来我们就一一来看一下这些类型的应用。
1 /** 2 * 在定义完参数进行赋值时, 3 * 必须按照给定的参数类型进行赋值 4 * 否则会出现编译问题 5 * 但在页面当中还是会进行编译 6 * 但是不提倡这么做 7 */ 8 // boolean 9 let flag: boolean = true; 10 console.log(flag); // true 11 // let flag1:boolean = 123; // Type '123' is not assignable to type 'boolean'. 12 13 // number 14 let num: number = 123; 15 console.log(num); // 123 16 17 // string 18 let str: string = "abc"; 19 console.log(str); // abc 20 21 // array 两种定义方式 22 let arrS: string[] = ["123", "abc"]; // 数组内元素必须为 string 23 let arrA: Array<number> = [123, 456]; // 数组内元素必须为 number 24 console.log(arrS); // ["123", "abc"] 25 console.log(arrA); // [123, 456] 26 27 // tuple 元祖类型,属于数组的一种,可以为每个元素指定类型 28 let tup: [number, string] = [123, "abc"]; 29 console.log(tup); // [123, "abc"] 30 // let tup1:[number,string] = ["abc",123]; // 报错 31 32 // enum 枚举类型 33 /** 34 * 在日常生活或者开发中 35 * 很多都不能或者不容易使用数据表达 36 * 如:颜色,日期,角色,性别等 37 * 例如在开发中我们常用 -1 表示 error,用 0 表示 success 38 * 这个就可以成为枚举 39 */ 40 enum Flag { 41 error = -1, 42 success = 0 43 } 44 45 let e: Flag = Flag.error; 46 console.log(e); // -1 47 48 /** 49 * 如果枚举元素不赋值,则默认取值为下标 50 * 如果某个元素取值为数字 n 51 * 后面的元素如果不取值则默认为 n+1,以此类推 52 * 如果某个元素取值为 string 类型 53 * 后面的元素则必须取值,取值类型无要求 54 */ 55 enum Color { 56 red, blue, black = 4, yellow, green = "green", white = 123 57 } 58 59 let red: Color = Color.red; 60 let blue: Color = Color.blue; 61 let black: Color = Color.black; 62 let yellow: Color = Color.yellow; 63 let green: Color = Color.green; 64 let white: Color = Color.white; 65 console.log(red, blue, black, yellow, green, white); // 0 1 4 5 "green" 123 66 67 // undefined 68 let un: undefined; 69 console.log(un); // undefined 70 71 // 我们也可以通过 | 来赋值多种类元素 72 let uns: number | undefined; 73 74 // any 类型,可以为任意类型 75 let an: any = 123; 76 console.log(an); // 13 77 an = "abc"; 78 console.log(an) // abc
在上面的代码中,我们演示了一下 Typescript 中的一些基本类型的使用,接下来我们再来看一下在函数中数据类型的应用。
Typescript 函数方法
1 // 如果没有返回值,则在方法名后面加 :void 2 function test(): void { 3 console.log("test") 4 } 5 6 test(); // test 7 8 // 如果有返回值,则在方法名后面加 :返回值的类型 9 function num(): number { 10 return 123; 11 } 12 13 console.log(num()); // 123 14 15 function str(): string { 16 return "abc"; 17 } 18 19 console.log(str()); // "abc" 20 21 // 定义传参 22 function getData(name: string, age: number): void { 23 console.log(`${name}--${age}`) 24 } 25 26 getData("张三", 18); // 张三--18 27 // getData("张三"); // 报错 Expected 2 arguments, but got 1. 28 // getData("张三","18"); // 报错 Argument of type '"18"' is not assignable to parameter of type 'number'. 29 30 /** 31 * 方法可选参数 32 * 在参数后面添加 ? 33 * 表示该参数为可选参数 34 */ 35 function getInfo(name: string, age?: number): string { 36 if (age) { 37 return `${name}--${age}` 38 } else { 39 return `${name}` 40 } 41 } 42 43 console.log(getInfo("张三", 18)); // 张三--18 44 console.log(getInfo("张三")); // 张三 45 46 /** 47 * 方法默认参数 48 * 在参数后面直接赋值 49 * 表示该参数直接当做了被传入参数 50 */ 51 function getUser(name: string, age: number = 18): string { 52 if (age) { 53 return `${name}--${age}` 54 } else { 55 return `${name}` 56 } 57 } 58 59 console.log(getUser("张三", 18)); // 张三--18 60 console.log(getUser("张三")); // 张三--18 61 62 /** 63 * 剩余参数 64 * 如果在传参过程中 65 * 前面的参数已经给定 66 * 在调用函数传参时会先将 67 * 传入的参数作为指定参数 68 * 剩余参数必须为最后一个参数传入 69 */ 70 // 正常的传参 71 function sum1(...arr: number[]): number { 72 let sum: number = 0; 73 for (let i = 0; i < arr.length; i++) { 74 sum += arr[i]; 75 } 76 return sum; 77 } 78 console.log(sum1(1, 2, 3, 4)); // 10 79 80 // a 作为第一个参数,其余的为剩余参数,即 (a,剩余参数) 81 function sum2(a: number, ...arr: number[]): number { 82 let sum: number = 0; 83 for (let i = 0; i < arr.length; i++) { 84 sum += arr[i]; 85 } 86 return sum; 87 } 88 console.log(sum2(1, 2, 3, 4)); // 10
在上面的代码中,我们实现了在 ts 中如何定义方法和如何进行方法传参,跟定义基本类型一样需要对方法进行有效的规定。
接下来我们再来看一下在 ts 中如何实现类和类的继承。
Typescript 类
在 ES5 中,我们是通过构造方法和原型链的方式进行继承的,在之前的文章中我们也讲过如何实现继承,传送门:https://www.cnblogs.com/weijiutao/p/12090916.html,Typescript 包含 ES6,所以本章着重讲解一下 ES6 中 class 关键字的类和继承。
1 // 在 ts 中定义类 2 class Person { 3 name: string; 4 5 constructor(name: string) { 6 this.name = name; 7 } 8 9 getName(): void { 10 console.log(this.name); 11 } 12 13 setName(name: string): string { 14 return this.name = name; 15 } 16 17 work(): void { 18 console.log("父类在工作") 19 } 20 } 21 22 let p = new Person("张三"); 23 p.getName(); // 张三 24 p.setName("李四"); 25 p.getName(); // 李四 26 p.work(); // 父类在工作 27 28 // 在 ts 中实现继承 29 /** 30 * 通过 extends 继承了 Person 的属性和方法 31 */ 32 class Student extends Person { 33 constructor(name: string) { 34 super(name); 35 } 36 37 // 子类自己的方法 38 run(): void { 39 console.log(this.name + "在运动") 40 } 41 42 // 子类重写父类的方法 43 work(): void { 44 console.log("子类在工作") 45 } 46 47 } 48 49 let s = new Student("王五"); 50 s.getName(); // 王五 51 s.run(); // 王五在运动 52 s.work(); // 子类在工作
* 类里面的修饰符
* Typescript 里面定义属性的时候
* 给我们提供了三种修饰符
* public:公有类型,在类里面、子类、类外面都可以访问
* protected:保护类型,在类里面,子类里面可以访问,类外面无法访问
* private:私有类型,在类里面可以访问,子类,类外面无法访问
* 属性不加修饰符,默认为公有属性
1 /** 2 * 类里面的修饰符 3 * Typescript 里面定义属性的时候 4 * 给我们提供了三种修饰符 5 * public:公有类型,在类里面、子类、类外面都可以访问 6 * protected:保护类型,在类里面,子类里面可以访问,类外面无法访问 7 * private:私有类型,在类里面可以访问,子类,类外面无法访问 8 * 属性不加修饰符,默认为公有属性 9 */ 10 11 class Person { 12 name: string; 13 public age: number = 18; 14 protected sex: string = "男"; 15 private city: string = "北京"; 16 17 constructor(name: string) { 18 this.name = name; 19 } 20 21 // 在本类中访问 public 类型 22 getName(): void { 23 console.log(this.name); 24 } 25 26 // 在本类中访问 public 类型 27 getAge(): void { 28 console.log(this.age); 29 } 30 31 // 在本类中访问 protected 类型 32 getSex(): void { 33 console.log(this.sex); 34 } 35 36 // 在本类中访问 private 类型 37 getCity(): void { 38 console.log(this.city); 39 } 40 41 } 42 43 let p = new Person("张三"); 44 // 外部访问 public 类型 45 console.log(p.name); // 张三 46 // 外部访问 public 类型 47 console.log(p.age); // 18 48 // 外部访问 protected 类型 49 // console.log(p.sex); // 报错 Property 'sex' is protected and only accessible within class 'Person' and its subclasses. 50 // 外部访问 private 类型 51 // console.log(p.city); // 报错 Property 'city' is private and only accessible within class 'Person'. 52 53 class Student extends Person { 54 constructor(name: string) { 55 super(name); 56 } 57 58 getInfo(): void { 59 // 在子类中访问 public 属性 60 console.log(this.name); 61 // 在子类中访问 private 属性 62 console.log(this.age); 63 // 在子类中访问 protected 属性 64 console.log(this.sex); 65 // 在子类中访问 private 属性 66 // console.log(this.city) // 报错 Property 'city' is private and only accessible within class 'Person'. 67 } 68 } 69 70 let s = new Student("王五"); 71 s.getInfo(); // 王五 18 男
在上面的代码中我们实现了一下 class 中的修饰符,接下来我们再来看一下 class 的静态属性、静态方法
1 /** 2 * 通过 static 关键字可以定义静态属性和静态方法 3 * 静态属性和静态方法直接通过 类. 来实现 4 */ 5 6 class Person { 7 name: string; 8 static age: number = 18; 9 10 constructor(name: string) { 11 this.name = name; 12 } 13 14 run(): void { 15 console.log(`${this.name}在运动`) 16 } 17 18 /** 19 * 静态方法无法通过 this 调用类里面的属性 20 * 通过 类. 调用 21 */ 22 static work(): void { 23 console.log("父类静态方法" + Person.age) 24 } 25 } 26 27 let p = new Person("张三"); 28 p.run(); // 张三在运动 29 console.log(p.name); // 张三 30 31 console.log(Person.age); // 18 32 Person.work(); // 父类静态方法18
在上面的代码中我们实现了一下 class 中的静态属性、静态方法,接下来我们再来看一下 class 的多态
1 /** 2 * 我们在定义类方法后 3 * 子类继承该类,并根据实际情况 4 * 来实现自己所需要的类方法 5 */ 6 class Person { 7 name: string; 8 age: number; 9 10 constructor(name: string) { 11 this.name = name; 12 } 13 14 work(): void { 15 console.log(`${this.name}在工作`) 16 } 17 18 19 } 20 21 class Student extends Person { 22 constructor(name: string) { 23 super(name); 24 } 25 26 run(): void { 27 console.log(`${this.name}在学习`) 28 } 29 } 30 let s = new Student("张三"); 31 s.run(); // 张三在学习 32 33 class Teacher extends Person { 34 constructor(name: string) { 35 super(name); 36 } 37 38 run(): void { 39 console.log(`${this.name}在讲课`) 40 } 41 } 42 let t = new Teacher("李四"); 43 t.run(); // 李四在讲课
在上面的代码中,我们定义了 Person 类并定义了一个 work 方法,然后 Student 和 Teacher 类分别继承了 Person,但是根据自己的角色重写了 work 方法,这就是一种多态。
接下来我们再来看一下 class 中的抽象类
1 /** 2 * 抽象类是提供其他类继承的基类,不能被实例化 3 * abstract 关键字定义抽象类和抽象方法 4 * 子抽象类中的抽象方法不能包含具体实现,必须在实现类中实现 5 * 抽象类和抽象方法只是用来定义标准 6 */ 7 abstract class Person { 8 name: string; 9 10 constructor(name: string) { 11 this.name = name; 12 } 13 14 abstract work(): any; 15 } 16 17 // 无法被实例化 18 // let p = new Person(); // 报错 error TS2511: Cannot create an instance of an abstract class. 19 20 class Student extends Person { 21 constructor(name: string) { 22 super(name); 23 } 24 25 work(): any { 26 console.log(`${this.name}在学习`) 27 } 28 } 29 30 let s = new Student("张三"); 31 s.work(); // 张三在学习
Typescript 接口
在面向对象的编程中,接口是一种规范的定义,它定义了行为和动作的规范,在程序设计里面,几口是一种限制和规范的作用。接口定义了某一批类所需要遵循的规范,接口不关心这些类的内部状态数据,也不关心这些类里面方法的实现细节,它只规定这批类里面必须提供某些方法,提供这些方法的类就可以满足实际需要,Typescript 中的接口类似于 Java,同时还增加了更灵活的接口类型,包括属性、函数、数组类等。
在日常生活中,我们会接触到很多类似接口的问题,比如 USB 接口,我们在电脑上插鼠标,键盘,U盘的时候不用去考虑它到底能不能插进去,只要型号对了就肯定能插进去,接口就相当于一个标准,你要想把鼠标插到我的电脑上,在出厂时就必须遵守该电脑定义的接口标准。
接下来我们就来看一下接口:
1、属性和类接口
1 interface FullName { 2 firstName:string, 3 lastName:string, 4 sayHi: ()=>string 5 } 6 7 let person:FullName = { 8 firstName:"张", 9 lastName:"三", 10 sayHi: ():string =>{return "hell world"} 11 }; 12 13 console.log(person.firstName); // 张 14 console.log(person.lastName); // 三 15 console.log(person.sayHi()); // hello world
在上面的代码中,我们通过 interface 编写了一个 FullName 的接口,然后定义了一个 person 的变量来实现这个接口,那么我们就可以使用该接口里面的属性了。
2、函数类型接口
全部评论
请发表评论