TypeScript的认识和基本使用

132 阅读11分钟

typeScript概述

什么是typeScript

TypeScript是一种由微软开发的自由和开源的编程语言。它是JavaScript的一个超集,添加了可选的静态类型和面向对象编程。TypeScript扩展了JavaScript的语法,所以任何现有的JavaScript程序可以不加改变的在TypeScript下工作。

简单来讲,typeScript并不是一门新的编程语言

typescript是JavaScript的超集(js有的ts都有)

ts在js的基础上增加了类型支持,并且更加支持面向对象编程

typsScript的优势

  1. typeScript 增加了代码的可读性和可维护性
  • 类型系统实际上是最好的文档,大部分的函数看看类型的定义就知道如何使用了。
  • 可以在编译阶段就发现大部分的错误,这总比在运行时出错好。
  • 增强了编译器和IDE的功能,包括代码补全,接口提示,跳转到定义,重构等。
  1. typeScript非常包容
  • typescript是javascript的超集,.js文件可以直接重命名为.ts即可。
  • 即使不显示的定义类型,也能够自动做出类型推导。
  • 可以定义从简单到复杂的几乎一切类型。
  • 兼容第三方库,即使第三方库不是用 typeScript 写的,也可以编写单独的类型文件供 typeScript 读取
  1. typeScript拥有活跃的社区
  • 大部分第三方库都有提供给 TypeScript 的类型定义文件
  • Google 开发的 Angular2 就是使用 TypeScript 编写的
  • TypeScript 拥抱了 ES6 规范,也支持部分 ESNext 草案的规范

typeScript的类型注解

所谓typeScript,注重的就是一个type——类型, ts是强类型语言,js有的类型它都有,js没有了类型它也有,在ts中如何声明一个Number类型的变量:

let age1 : number  // 代表声明一个number类型的变量age1
let age2 : number = 18 // 代表声明一个number类型的变量age2,并赋值为18
let age3 : number = '18' // 这样在ts中会报错,因为已经声明了变量age3类型为number,
												//  不能将字符串赋值给这个变量

虽然声明一个ts变量比js麻烦了一点,但是这样在开发中有利于避免错误,并且方便后期维护,这样一看就知道这个变量是什么类型的了,并且也不能随意赋予其他类型的值给这个变量

type——类型别名

type可以声明ts中提供的所有类型变量

type的常用写法:

//未使用type
let sum:(x:number,y:number) => number
sum=(x,y)=>x+y
const result = sum(1,2)
console.log(result)
 
 
//使用type
type PlusType=(x:number,y:number)=>number
let sum2:PlusType=(x,y)=>x+y
const result2=sum2(2,3)
console.log(result2)
 
// type结合联合类型
type strOrNum=string|number
let result3:strOrNum='12'
result3=12
 
//比较常用
type Directions='Up'|'Down'|'Left'|'Right'
let toWhere:Directions='Down'
 
//type结合接口
interface IName{name:string}
type IPerson=IName&{age:number}
let person:IPerson={name:'jim',age:123}

interface——接口

基本使用

interface只能声明对象类型

当一个对象类型被多次使用时,一般会使用接口(interface)来描述对象的类型,达到复用的目的

解释:

  1. 使用 interface 关键字来声明接口
  2. 接口名称(比如,此处的 IPerson),可以是任意合法的变量名称,推荐以 I 开头
  3. 声明接口后,直接使用接口名称作为变量的类型
  4. 因为每一行只有一个属性类型,因此,属性类型后没有 ;(分号)
// 语法
interface 接口名 {}
// 例:
interface IPerson {
  name: string
  age: number
  sayHi(): void
}

let person: IPerson = {
  name: 'jack',
  age: 19,
  sayHi() {}
接口继承

如果两个接口之间有相同的属性或方法,可以将公共的属性或方法抽离出来,通过继承来实现复用

比如,这两个接口都有 x、y 两个属性,重复写两次,可以,但很繁琐

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

更好的方式

interface Point2D { x: number; y: number }
// 继承 Point2D
interface Point3D extends Point2D {
  z: number
}

解释:

  1. 使用 extends(继承)关键字实现了接口 Point3D 继承 Point2D
  2. 继承后,Point3D 就有了 Point2D 的所有属性和方法(此时,Point3D 同时有 x、y、z 三个属性)

type与interface的区别

  • 相同点
    1. 都可以描述一个对象或者函数
    2. 都允许拓展继承(extends)
  • 不同点
    1. 类型别名可以用于其它类型 (联合类型、元组类型、基本类型(原始值)),interface只支持对象类型
    2. interface可以多次定义,声明合并,type 不支持
    3. type 能使用 in 关键字生成映射类型,但 interface 不行
    4. 默认导出方式不同

typeScript的类型系统

ts提供了以下的变量类型:

  1. js已有类型
  • number——数字
  • string——字符串
  • boolean——布尔值
  • Array——数组类型
  • Function——方法类型
  • Object——对象类型
  • void——用于标识方法返回值的类型,表示该方法没有返回值
  • null——表示对象值缺失。
  • undefined——用于初始化变量为一个未定义的值
  1. ts独有类型
  • any——代表可以是任意类型(这个类型的变量就和js直接声明的变量没有差别,不建议使用)
  • tuple——元组类型,用来表示已知元素数量和类型的数组,各元素的类型不必相同,对应位置的类型需要相同。
  • enum——枚举类型,用于定义数值集合
  • never——代表其它类型(包括 null 和 undefined)的子类型,代表从不会出现的值。
  • 联合类型——代表多种类型的组合,例如:number|string

number、string、boolean

let str:string = '112'
let flag:boolean = true
let num:number = 12

array数组的两种声明方式

let arr1:number[] = [1,2,3]			//所有项为number
let arr4:any[] = ['1123', 23] 		//每一项为任意类型
let arr2:Array<number> = [4,5,6]	//所有项为number
let arr2:Array<number|string> = [4,5,6]	//所有项为number或string

tuple 元组(array的一种)

// 可以给每个位置指定一种数据类型
let x: [string, number];
// Initialize it
x = ['hello', 10]; // OK
x = [10, 'hello']; // Error

当访问一个已知索引的元素,会得到正确的类型:

console.log(x[0].substr(1)); // OK
console.log(x[1].substr(1)); // Error, 'number' does not have 'substr'

当访问一个越界的元素,会使用联合类型替代:

x[3] = 'world'; // OK, 字符串可以赋值给(string | number)类型
console.log(x[5].toString()); // OK, 'string''number' 都有 toString
x[6] = true; // Error, 布尔不是(string | number)类型

any 任意数据类型

let six:any = 'string'
six = true
six = 123     // any类型的变量可以赋予任意类型的值

null/空 与 undefined/未定义

//null与undefined 都是never(其他类型)的子类型
let num1:number
console.log(num1);  //未赋值 报错
let num2:undefined
console.log(num2)   //不报错 打印出num2

//一般这么应用,避免没有传值的时候报错
let num3:number | undefined
let num4:null
// num4 = 123       null不能赋值 报错
console.log(null);  //不报错 打印出null

void

//函数没有返回值的时候用 void 有返回值的时候用具体的数据类型
function run():void {
    console.log(11);
}

never 其他类型(代表从不会出现的值)

let a:never
// a=null 报错
a = (()=>{	//正确
    throw new Error('错误')
})()

enum枚举类型

枚举:定义一组命名常量。它描述一个值,该值可以是这些命名常量中的一个

// 创建枚举
enum Direction { Up, Down, Left, Right }

// 使用枚举类型
function changeDirection(direction: Direction) {
  console.log(direction)
}

// 调用函数时,需要应该传入:枚举 Direction 成员的任意一个
// 类似于 JS 中的对象,直接通过 点(.)语法 访问枚举的成员
changeDirection(Direction.Up)

Function函数类型

  • 函数的类型实际上指的是:函数参数和返回值的类型
  • 为函数指定类型的两种方式:
    1. 单独指定参数、返回值的类型
    2. 同时指定参数、返回值的类型
单独指定参数、返回值的类型:
// 函数声明
function add(num1: number, num2: number): number {
  return num1 + num2
}
// 箭头函数
const add = (num1: number, num2: number): number => {
  return num1 + num2
}
同时指定参数、返回值的类型:
type AddFn = (num1: number, num2: number) => number
const add: AddFn = (num1, num2) => {
  return num1 + num2
}
  • 解释:当函数作为表达式时,可以通过类似箭头函数形式的语法来为函数添加类型
  • 注意:这种形式只适用于函数表达式
函数可选参数
  • 使用函数实现某个功能时,参数可以传也可以不传。这种情况下,在给函数参数指定类型时,就用到可选参数
  • 比如,数组的 slice 方法,可以 slice() 也可以 slice(1) 还可以 slice(1, 3)
function mySlice(start?: number, end?: number): void {  
  console.log('起始索引:', start, '结束索引:', end) 
} 
  • 可选参数:在可传可不传的参数名称后面添加 ?(问号)
  • 注意:可选参数只能出现在参数列表的最后,也就是说可选参数后面不能再出现必选参数

Object对象类型

  • JS 中的对象是由属性和方法构成的,而 TS 对象的类型就是在描述对象的结构(有什么类型的属性和方法)
  • 对象类型的写法:
// 空对象
let person: {} = {}

// 有属性的对象
let person: { name: string } = {
  name: '同学'
}

// 既有属性又有方法的对象
// 在一行代码中指定对象的多个属性类型时,使用 `;`(分号)来分隔
let person: { name: string; sayHi(): void } = {
  name: 'jack',
  sayHi() {}
}

// 对象中如果有多个类型,可以换行写:
// 通过换行来分隔多个属性类型,可以去掉 `;`
let person: {
  name: string
  sayHi(): void
} = {
  name: 'jack',
  sayHi() {}
}
  • 解释:
    1. 使用 {} 来描述对象结构
    2. 属性采用属性名: 类型的形式
    3. 方法采用方法名(): 返回值类型的形式
使用类型别名
  • 注意:直接使用 {} 形式为对象添加类型,会降低代码的可读性(不好辨识类型和值)
  • 推荐:使用类型别名为对象添加类型
// 创建类型别名
type Person = {
  name: string
  sayHi(): void
}

// 使用类型别名作为对象的类型:
let person: Person = {
  name: 'jack',
  sayHi() {}
}

类型推论

  • 在 TS 中,某些没有明确指出类型的地方,TS 的类型推论机制会帮助提供类型
  • 换句话说:由于类型推论的存在,这些地方,类型注解可以省略不写
  • 发生类型推论的 2 种常见场景:
    1. 声明变量并初始化时
    2. 决定函数返回值时
// 变量 age 的类型被自动推断为:number
let age = 18

// 函数返回值的类型被自动推断为:number
function add(num1: number, num2: number) {
  return num1 + num2
}

字面量类型

以下代码,两个变量的类型分别是什么?

let str1 = 'Hello TS'
const str2 = 'Hello TS'

通过 TS 类型推论机制,可以得到答案:

  1. 变量 str1 的类型为:string
  2. 变量 str2 的类型为:'Hello TS'

解释:

  1. str1 是一个变量(let),它的值可以是任意字符串,所以类型为:string
  2. str2 是一个常量(const),它的值不能变化只能是 'Hello TS',所以,它的类型为:'Hello TS'
  • 注意:此处的 'Hello TS',就是一个字面量类型,也就是说某个特定的字符串也可以作为 TS 中的类型
  • 任意的 JS 字面量(比如,对象、数字等)都可以作为类型使用
    • 字面量:{ name: 'jack' } [ ] 1820 'abc' false function() { }

类型断言

有时候你会比 TS 更加明确一个值的类型,此时,可以使用类型断言来指定更具体的类型。 比如:

const aLink = document.getElementById('link')

注意:该方法返回值的类型是 HTMLElement,该类型只包含所有标签公共的属性或方法,不包含 a 标签特有的 href 等属性

因此,这个类型太宽泛(不具体) ,无法操作 href 等 a 标签特有的属性或方法

解决方式:这种情况下就需要使用类型断言指定更加具体的类型

使用类型断言:

// 语法1 使用 as
const aLink = document.getElementById('link') as HTMLAnchorElement
// 语法2 使用<>
const aLink = <HTMLAnchorElement>document.getElementById('link')

\

使用typeScript

需要使用到 npm 工具安装

如果本地环境已经安装了 npm 工具,可以使用以下命令来安装。

首先打开控制台窗口

使用国内镜像:

npm config set registry https://registry.npmmirror.com

安装 typescript:

npm install -g typescript  // 这里是全局安装

安装完成后我们可以使用 tsc 命令来执行 TypeScript 的相关代码,以下是查看版本号:

tsc -v
Version 4.6.2

然后我们新建一个 app.ts 的文件,代码如下:

var message:string = "Hello World" 
console.log(message)

通常我们使用 .ts 作为 TypeScript 代码文件的扩展名(浏览器是不能直接运行.ts文件的,需要将其编译成.js文件)。

然后执行以下命令将 TypeScript 转换为 JavaScript 代码:

tsc app.ts

这时候在当前目录下(与 app.ts 同一目录)就会生成一个 app.js 文件,代码如下:

var message = "Hello World";
console.log(message);

使用 node 命令来执行 app.js 文件:

$ node app.js 
Hello World