TypeScript的使用

65 阅读15分钟

TypeScript是一种由微软开发的自由和开源的编程语言。它是JavaScript的一个超集,而且本质上向这个语言添加了可选的静态类型和基于类的面向对象编程。TypeScript扩展了JavaScript的语法,所以任何现有的JavaScript程序都可以运行在TypeScript环境中,TypeScript是为大型应用的开发而设计,并且可以编译为JavaScript

为什么使用TypeScript:

  1. TypeScript 是由微软开发的一款开源的编程语言
  2. TypeScript 遵循当前以及未来出现的 ECMAScript 规范
  3. TypeScript 面向对象编程特性,丰富的class扩展功能
  4. TypeScript 中引入了模块的概念
  5. TypeScript 是扩展了 JavaScript 的语法的 JavaScript超集
  6. TypeScript 简化了JavaScript代码,增加了代码的可读性和可维护性
  7. TypeScript 兼容第三方库,被众多大型框架支持
  8. TypeScript 适用于开发大型的应用,拥有完备的系统设计能力,提高了多人开发时的规范
  9. TypeScript 拥有强大的类型系统和静态类型检查能力
  10. TypeScript 被主流的编辑器支持,编码期间可以检测错误,例如:类型检测,语法提示
  11. TypeScript 不但兼容现有的JavaScript代码,也拥有兼容未来版本的JavaScript的能力

安装TypeScript:

需要安装nodejs,使用npm全局安装TypeScript

npm install -g typescript

命令行输入命令,安装成功会出现对应版本号

tsc -v

TypeScript文件编译成JavaScript文件:

浏览器无法解析后缀名为.ts的TypeScript文件,需要我们将TypeScript文件编译成JavaScript文件

手动编译

命令行执行命令,会生成同名的.js的JavaScript文件

tsc 文件名.ts

自动编译

在项目根目录下命令行执行以下命令,创建配置文件tsconfig.json配置参考编译选项参考

tsc --init

命令行执行以下命令,编辑器会执行tsconfig.json文件的选项,将.ts文件编译生成对应的.js文件

tsc

TypeScript的基本使用:

TypeScript 变量声明

TypeScript 的变量是强类型的,在声明变量时,需要定义一个类型,通过类型批注提供静态类型以在编译时启动类型检查

TypeScript 变量的命名规则:
  • 变量名称可以包含数字和字母
  • 除了下划线 _ 和美元 $ 符号外,不能包含其他特殊字符,包括空格
  • 变量名不能以数字开头
TypeScript 声明变量:
  • 声明变量的类型及初始值:[变量名] : [类型] = 值
let val: string = 'abc';
  • 声明变量的类型,但没有初始值,变量值会设置为undefined:[变量名] : [类型]
let val: string;
  • 声明变量并初始值,但不设置类型,该变量可以是任意类型:[变量名] = 值
let val = 'abc';
  • 声明变量没有设置类型和初始值,类型可以是任意类型,默认初始值为undefined:[变量名]
let val;

TypeScript 的数据类型

相比 JavaScript 的数据类型,除 boolean,null,undefined,number,string,array,object 这些相同数据类型,TypeScript 新增了 any任意类型,Tuple元祖,enum枚举,void,never

  • boolean布尔类型:和 JavaScript 相同,值为 true 或 false
//声明一个boolean布尔类型的变量,该变量可以被赋值为true或false
let val: boolean = true
val = false
  • number数字类型:和 JavaScript 相同,除了支持十进制和十六进制字面量,TypeScript还支持ECMAScript 2015中引入的二进制和八进制字面量
//声明一个number数字类型的变量,该变量可以被赋值为十进制、十六进制、二进制和八进制字面量
let num: number = 6
num = 0xf00d
num = 0b1010
num = 0o744
  • string字符串类型:和 JavaScript 相同,使用双引号( ")或单引号(')表示字符串,也可以使用反引号( `)表示的模版字符串,以 ${ } 的形式嵌入表达式,它可以定义多行文本和内嵌表达式
//声明一个string字符串类型的变量
let str1: string = 'abc'

//使用模版字符串,将str1嵌入表达式
let str2: string = `my name is ${str1}`
  • null 和 undefined类型:默认情况下,null 和 undefined 是其它类型的子类型,可以赋值给其它类型,赋值后的类型会变成 null 或 undefined,变量定义没有赋值就是 undefined
//null和undefined可以赋值给其它类型
let u: undefined = undefined
let n: null = null
let num: number = null
let str: string = undefined

//变量定义没有赋值就是undefined
let val: string
console.log(val)  //undefined

当你指定了--strictNullChecks 标记,null 和 undefined 只能赋值给 void 和它们自己,此时会避免很多问题 在配置文件 tsconfig.json 里配置 –strictNullChecks 标记的启用

//tsconfig.json
{
   "compilerOptions": { 
   	   //严格的null检查模式下,null和undefined值不包含在任何类型里,只允许赋值给void和本身对应的类型
       "strictNullChecks": false
   }
}
//启用--strictNullChecks标记
let num: number;
num = 1   //正确
num = undefined   //报错
num = null   //报错
  • any任意类型:为还不清楚类型的变量指定一个类型,声明为 any 的变量可以赋予任意类型的值
//声明一个any任意类型的变量,该变量可以被赋予任意类型的值
let val: any = 'abc'
val = 9
val = true
  • array数组类型:可以使用“类型 + 方括号”来表示数组,也可以使用数组泛型“Array<类型>”来表示数组
//声明一个number类型的数组
let ary1: number[] = [1, 2, 3]

//使用数组泛型来表示数组
let ary2: Array<number> = [1, 2, 3]

//声明一个any任意类型的数组
let ary3: any[] = [1, 'a', {val: 'abc'}]
  • Tuple元祖类型:表示一个已知元素数量和类型的数组,各元素的类型不必相同,对应位置的类型需要相同
//声明一个不同位置指定类型的数组
let ary: [string, number]
ary = ['abc', 9]	//正确
ary = [9, 'abc']	//报错
  • enum枚举类型:用于定义数组集合,可以由枚举的值得到它的名字,默认情况下,从0开始为元素编号,也可以手动的指定成员的数值,枚举类型会被编译成键值对形式的双向映射,按照枚举成员的类型基本分为 数字枚举类型 和 字符串枚举类型
//数字枚举类型,默认从0开始为元素编号
enum Color {
	red, 
	green, 
	blue
}
let color1 = Color.green
console.log(color1)  //1
let color2 = Color[2]
console.log(color2)  //blue

//手动指定成员数值
enum Color {
	red = 1, 
	green = 3, 
	blue = 6
}
let color1 = Color.green
console.log(color1)  //3
let color2 = Color[6]
console.log(color2)  //blue

//字符串枚举类型
enum Color {
	red = 'r', 
	green = 'g', 
	blue = 'b'
}
let color1 = Color.green
console.log(color1)  //g
  • void类型:void 类型可以看做与 any 任意类型相反,表示没有任何类型,声明 void 类型的变量只能为它赋值为 undefined 和 null,声明 void 类型的函数表示该函数没有返回值
//声明一个void类型的变量,并赋值为undefined
let val: void = undefined

//声明一个void类型的函数,该函数没有返回值
function fn(): void {
	console.log('函数无返回值')
}
  • never类型:表示其它类型(包括 null 和 undefined)的子类型,代表永不存在的值的类型,never 类型是任何类型的子类型,可以赋值给任何类型,声明为 never 类型的变量只能被 never 类型所赋值,在函数中它通常表现为抛出异常或无法执行到终止点(例如无限循环)
//返回值为never的函数
function fail() {
    return Error("Something failed")
}

//无限循环的函数
function infiniteLoop(): never {
    while(true) {
    	......
    }
}
  • object对象类型:表示非原始类型,除number,string,boolean,symbol,null或undefined之外的类型
//声明一个object类型的变量
let obj: object = {
	name: 'abc',
	age: 18
}
TypeScript 联合类型

联合类型表示一个变量值可以是几种类型之一,使用 ' | ' 来分割每个类型,赋值时只能赋值指定类型的值,如果赋值其它类型就会报错

//声明一个联合类型的变量
let val: string | number 
val = 12  
val = 'abc' 

//声明一个联合类型的数组
let ary: number[] | string[]
ary = [1, 2, 3]
ary = ['a', 'b', 'c']
TypeScript 类型断言

类型断言可以用来手动指定一个值的类型,即允许变量从一种类型更改为另一种类型,使用“<类型>值”或“值 as 类型”来表示类型断言

//声明一个any任意类型的变量val
let val: string | number
let strLength: number = val.length   //报错
//将变量val断言为string类型,将其长度赋值给number类型的变量strLength
let strLength: number = (<string>val).length
//或
let strLength: number = (val as string).length

注意:类型断言并不是类型转换,不允许断言成一个联合类型中不存在的类型

//声明一个联合类型的变量val
let val: number | string
//不能将变量val断言为boolean类型
let b: boolean = <boolean>val  //报错
TypeScript 类型推断

类型推断指不需要指定变量的类型,TypeScript 编译器可以在声明没有指定类型的变量、函数默认参数、函数返回值等时候根据某些规则自动推断出类型,当类型推断不符合我们的预期时,可以使用类型断言来手动指定一个值的类型

let val  //类型推断为any类型
let num = 1  //类型推断为number类型
let ary = []  //类型推断为any类型的数组
let fn = (x=1) => x+1  //函数默认参数和返回值被推断为number类型
TypeScript 类型别名

类型别名用来给一个类型起个新名字,使用关键字type创建类型别名,常用于联合类型,也可以用来限制字符串的取值

//创建一个类型别名
type valType = string | number
let val: valType = 'abc'
val = 9

//对接口使用类型别名
interface People1 {
	name: string
}
interface People2 {
	age: number
}
type People = People1 | People2
let obj: People = {
	name: 'abc',
	age: 18
}
let obj1: People = {
	name: 'abc'
}
let obj2: People = {
	age: 18
}

//限制字符串的取值
type sex = '男' | '女'
function getSex(val: sex): string {
	return '性别:' + val
}
getSex('男')  //性别:男
getSex('女')  //性别:女
getSex('妖')  //报错

TypeScript 函数

函数是一组一起执行一个任务的语句, TypeScript 函数添加了更多功能,使用起来更加容易

定义函数类型

使用关键词 function 来定义函数,并定义函数参数和返回值的类型,返回值类型为 void 时表示函数没有返回值

//函数声明
function fn(x: number, y: number): number {
    return x + y
}
//或不定义返回类型,TypeScript会自动推断类型
function fn(x: number, y: number) {
    return x + y
}
//匿名函数
let fn = function (x: number, y: number): number {
    return x + y
}
//箭头函数
let fn = (x: number, y: number): number => {
	return x + y
}
函数的参数
  • 可选参数:表示该参数是不必须的,参数名后面使用 ?,便实现了可选参数,可选参数必须在必须参数的后面
//声明一个带可选参数的函数
function fn(x: number, y: number, z?: number) {
	if(z) {
		return x + y + z
	}else {
		return x + y
	}
}
console.log(fn(1, 2))	//3
console.log(fn(1, 2, 3))   //6
  • 默认参数:使用 = 指定参数的默认值,当这个参数没有传值或传值是 undefined 时,就会使用参数的默认值,调用函数的时候是可以省略该参数,有默认值的参数不一定要放在必须参数的后面,也可以放在前面,当传入 undefined 的时候,就会取默认参数指定的默认值
//声明一个带默认参数的函数
function fn(x: number, y: number = 2, z: number) {
	return x + y + z
}
console.log(fn(1, undefined, 3))   //6
  • 剩余参数:与 ES6 一样,当参数个数不确定时使用剩余参数
//声明一个带剩余参数的函数
function fn(x: number, y: number, ...resNum: number[]) {
	return Math.max(x, y, ...resNum)
}
console.log(fn(1, 2, 3, 4, 5))  //5
函数的重载

TypeScript 函数重载用来处理因函数参数不同而返回类型不同的使用场景,通过对同一个函数定义多个参数和返回值类型,在调用函数时能静态检查参数类型,实现不同的表现形式,做出不同的处理

function fn(val: string): string
function fn(val: number): number
function fn(val: any): any {
	if(typeof val == 'string') {
		console.log('name:' + val)
	}else if(typeof val == 'number') {
		console.log('age:' + val)
	}
}
fn('abc')   //name:abc
fn(9)   //age:9

TypeScript 面向对象

TypeScript 是一种面向对象的编程语言,面向对象是一种对现实世界理解和抽象的方法,主要有两个概念:对象

  • 对象:对象是类的一个实例,有状态和行为
  • 类:类是一个模板,它描述了所创建的对象共同的属性和方法,通过关键字class定义类
    • 字段:字段是类里面声明的变量,表示对象的有关数据
    • 构造函数:类实例化时调用,可以为类的对象分配内存
    • 方法:方法为对象要执行的操作
//创建一个类
class People { 
    //字段 
    word:string
    //构造函数 
    constructor(word:string) { 
        this.word = word 
    }  
    //方法 
    say():void { 
        console.log(this.word) 
    } 
}
//创建一个实例
let one = new People('hello')
one.say()  //hello
TypeScript 继承类

TypeScript 支持继承类,可以在创建类的时候继承一个已存在的类,这个已存在的类称为父类,继承它的类称为子类,类继承使用关键字extends,子类除了不能继承父类的私有成员(方法和属性)和构造函数,其他的都可以继承

//创建一个类
class People { 
    word:string
    constructor(word:string) { 
        this.word = word 
    }  
    say():void { 
        console.log(this.word) 
    } 
}
//创建一个Person类继承People类
class Person extends People { 
   //子类使用继承父类中的字段
   talk():void { 
      console.log(this.word) 
   } 
}
//创建一个实例
let one = new Person('hello')
one.talk()  //hello
TypeScript 抽象类

TypeScript 抽象类是提供其他类继承的基类(父类),不能直接被实例,抽象类中的抽象方法不包含具体实现,子类继承抽象类实现抽象方法,使用abstract关键字定义抽象类和抽象方法

//创建一个抽象类
abstract class People { 
	//抽象方法
    abstract talk()
    say():void { 
        console.log('hello') 
    } 
}
//创建一个Person类继承People类
class Person extends People { 
   //子类实现父类中的抽象方法
   talk():void { 
      console.log('hi') 
   } 
}
//创建一个实例
let one = new Person()
one.talk()  //hi
TypeScript 访问控制修饰符

TypeScript 中,可以使用访问控制修饰符来保护对类、变量、方法和构造方法的访问

  • public(默认) : 公有,可以在任何地方被访问
  • protected : 受保护,可以被其自身以及其子类和父类访问
  • private : 私有,只能被其定义所在的类访问
//创建一个类
class People { 
    //公有变量 
    word1:string = 'a'
    //受保护变量 
    protected word2:string = 'b'
    //私有变量
    private word3:string = 'c'
}
//创建一个实例
let one = new People()
console.log(one.word1)  //a
console.log(one.word2)  //报错:只能在People类及其子类中访问
console.log(one.word3)  //报错:只能在People类中访问
TypeScript 方法的重写

类继承后,子类可以对父类的方法重新定义,这个过程称之为方法的重写

//创建一个类
class People { 
    //字段 
    word:string
    //构造函数 
    constructor(word:string) { 
        this.word = word 
    }  
    //方法 
    say():void { 
        console.log(this.word) 
    } 
}
//创建一个Person类继承People类
class Person extends People { 
   //子类重新定义父类中的方法
   say():void { 
      console.log('say:' + this.word) 
   } 
}
//创建一个实例
let one = new Person('hello')
one.say()  //say:hello
TypeScript static关键字

static关键字用于定义类的数据成员(属性和方法)为静态的,静态属性和静态方法可以直接通过类名调用

//创建一个类
class People { 
    //静态属性 
    static word:string 
    //静态方法 
    static say():void { 
        console.log(this.word) 
    } 
}
//初始化静态变量
People.word = 'hello'
//调用静态方法     
People.say()  //hello    

TypeScript 接口

接口 (使用关键字interface定义)是一系列抽象方法的声明,是一些方法特征的集合,这些方法都应该是抽象的,需要由具体的类去实现接口的内容,然后第三方就可以通过这组抽象方法调用,让具体的类执行具体的方法,类使用关键字 implements实现接口,在结构复杂时,接口主要对class类起约束规范的作用,接口自身可以当作对象的类型使用

//创建一个接口
interface IPeople { 
    firstName: string, 
    lastName: string, 
    say: () => string 
 } 
 //创建一个IPeople接口类型的对象,赋值时,对象的形状必须和接口的形状保持一致
 var p:IPeople = { 
    firstName: "a",
    lastName: "bc", 
    say: ():string => {return 'hello'} 
 } 
 console.log(p.firstName)  //a
 console.log(p.lastName)  //bc
 console.log(p.say())  //hello
 
 //创建一个类实现IPeople接口
 class People implements IPeople { 
    firstName: string 
    lastName: string
    say: () => string
    constructor(firstName:string,lastName:string) { 
       this.firstName = firstName 
       this.lastName = lastName 
    } 
 } 
 //创建一个实例
 let one = new People('a', 'bc')
 console.log(one.firstName)  //a
 console.log(one.lastName)  //bc

接口的属性类型:

  • 可选属性:表示接口里不必须的属性,属性名后面加上 ? 来表示这个属性是可选的
//创建一个接口
interface Person {
    name: string,
    age?: number
}
let abc: Person = {
    name: 'abc'
}
  • 只读属性:表示对象属性只能在对象创建时赋值,之后只可以读取,不可以再修改,属性名前用 readonly 来表示这个属性是只读属性
//创建一个接口
interface Person {
    name: string,
    readonly age: number
}
let abc: Person = {
    name: 'abc',
    age: 18
}
abc.age = 20   //报错
  • 任意属性:表示接口允许有任意的属性,当属性个数不确定时,使用“[propName: string]”定义key取string类型值的任意属性,一旦定义了任意属性,那么确定属性和可选属性必须是它的类型的子集
//创建一个接口
interface Person {
    name: string,
    age: number,
    [propName: string]: any   //任意属性
}
let abc: Person = {
    name: 'abc',
    age: 18,
    gender: 'male'
}

TypeScript 泛型

TypeScript 泛型是一种创建可复用代码组件的工具,使用类型变量来创建可重用的组件,一个组件可以支持多种类型的数据,开发者可以以自己的数据类型来使用组件,类型变量是一种特殊的变量,用于表示类型而不是值

//创建一个泛型函数
function fn<T>(ary: T[]): void {
	for(let item of ary) {
		console.log(item)
	}
}
fn<number>([1, 2, 3])
fn<string>(['a', 'b', 'c'])

//创建一个泛型类
class People<T> { 
    word:T
    say(val: T):void { 
        console.log(val) 
    } 
}
//创建一个实例
let one = new People<string>()
one.say('hello')  //hello

TypeScript 模块

TypeScript模块的设计理念是可以更换的组织代码,模块是在其自身的作用域里执行,并不是在全局作用域,这意味着定义在模块里面的变量、函数和类等在模块外部是不可见的,除非明确地使用 export 导出它们,类似地,我们必须通过 import 导入其他模块导出的变量、函数、类等 模块是自声明的,两个模块之间的关系是通过在文件级别上使用import导入和export导出建立的 使用export关键字导出模块:

//requset.ts 
//导出Request类
export class Request { 
   ......
}

在使用该模块的文件中使用import关键字导入模块:

import Request = require("./requset")

TypeScript 和 JavaScript 的对比:

  • JavaScript:相比于TypeScript,JavaScript 语言发展的较早,也较为成熟,使用率高,方便找到大量成熟的开发项目和可用资源,更加灵活,适用于小型项目的开发,JavaScript 是一种脚本编写语言,无需编译,只要嵌入 HTML 代码中,就能由浏览器逐行加载解释执行
  • TypeScript:相比于JavaScript,TypeScript 语言更适用于大型项目的开发,其面向对象编程语言的结构保持了代码的清洁、一致和简单的调试,拥有静态类型检查等功能,开发期间编码时检测错误,提高了工作效率,但是 TypeScript 文件无法被浏览器解析,最终需要编译成 JavaScript 文件

文档学习:TypeScript中文网