typeScript概述
什么是typeScript
TypeScript是一种由微软开发的自由和开源的编程语言。它是JavaScript的一个超集,添加了可选的静态类型和面向对象编程。TypeScript扩展了JavaScript的语法,所以任何现有的JavaScript程序可以不加改变的在TypeScript下工作。
简单来讲,typeScript并不是一门新的编程语言
typescript是JavaScript的超集(js有的ts都有)
ts在js的基础上增加了类型支持,并且更加支持面向对象编程
typsScript的优势
- typeScript 增加了代码的可读性和可维护性
- 类型系统实际上是最好的文档,大部分的函数看看类型的定义就知道如何使用了。
- 可以在编译阶段就发现大部分的错误,这总比在运行时出错好。
- 增强了编译器和IDE的功能,包括代码补全,接口提示,跳转到定义,重构等。
- typeScript非常包容
- typescript是javascript的超集,.js文件可以直接重命名为.ts即可。
- 即使不显示的定义类型,也能够自动做出类型推导。
- 可以定义从简单到复杂的几乎一切类型。
- 兼容第三方库,即使第三方库不是用 typeScript 写的,也可以编写单独的类型文件供 typeScript 读取
- 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)来描述对象的类型,达到复用的目的
解释:
- 使用 interface 关键字来声明接口
- 接口名称(比如,此处的 IPerson),可以是任意合法的变量名称,推荐以 I 开头
- 声明接口后,直接使用接口名称作为变量的类型
- 因为每一行只有一个属性类型,因此,属性类型后没有 ;(分号)
// 语法
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
}
解释:
- 使用 extends(继承)关键字实现了接口 Point3D 继承 Point2D
- 继承后,Point3D 就有了 Point2D 的所有属性和方法(此时,Point3D 同时有 x、y、z 三个属性)
type与interface的区别
- 相同点
-
- 都可以描述一个对象或者函数
- 都允许拓展继承(extends)
- 不同点
-
- 类型别名可以用于其它类型 (联合类型、元组类型、基本类型(原始值)),interface只支持对象类型
- interface可以多次定义,声明合并,type 不支持
- type 能使用 in 关键字生成映射类型,但 interface 不行
- 默认导出方式不同
typeScript的类型系统
ts提供了以下的变量类型:
- js已有类型
- number——数字
- string——字符串
- boolean——布尔值
- Array——数组类型
- Function——方法类型
- Object——对象类型
- void——用于标识方法返回值的类型,表示该方法没有返回值
- null——表示对象值缺失。
- undefined——用于初始化变量为一个未定义的值
- 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函数类型
- 函数的类型实际上指的是:函数参数和返回值的类型
- 为函数指定类型的两种方式:
-
- 单独指定参数、返回值的类型
- 同时指定参数、返回值的类型
单独指定参数、返回值的类型:
// 函数声明
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() {}
}
- 解释:
-
- 使用 {} 来描述对象结构
- 属性采用属性名: 类型的形式
- 方法采用方法名(): 返回值类型的形式
使用类型别名
- 注意:直接使用 {} 形式为对象添加类型,会降低代码的可读性(不好辨识类型和值)
- 推荐:使用类型别名为对象添加类型
// 创建类型别名
type Person = {
name: string
sayHi(): void
}
// 使用类型别名作为对象的类型:
let person: Person = {
name: 'jack',
sayHi() {}
}
类型推论
- 在 TS 中,某些没有明确指出类型的地方,TS 的类型推论机制会帮助提供类型
- 换句话说:由于类型推论的存在,这些地方,类型注解可以省略不写
- 发生类型推论的 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 类型推论机制,可以得到答案:
- 变量 str1 的类型为:string
- 变量 str2 的类型为:'Hello TS'
解释:
- str1 是一个变量(let),它的值可以是任意字符串,所以类型为:string
- 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