介绍
typescript是一门由微软公司发布的开源编程语言,目前主流的前端框架vue、react、angular都支持使用typescript来进行开发,typescript被认为是JavaScript的超集,主要提供了类型系统和对es6的支持
typescript的优势
- 提供了类型系统,使得代码比如函数一眼就可以看出如何使用,便于代码的维护和语义化。有了类型系统使得很多问题在编译的时候就报错了,这样比在运行时就出错更加方便,更早的暴露已知也能提高开发效率
- typescript是JavaScript的超级,js文件可以直接名称成ts。typescript即使不显式的定义类型也能进行类型推论。typescript代码即使编译报错也会生成JavaScript代码
- 兼容第三方库,即使第三方库不是使用ts写的,也可以编写单独的类型文件供 TypeScript 读取,当然大部分第三方库也是提供了ts的版本
- ts支持了es6和部分未来即将进入es7、es8的部分规范
- ts学习成本偏高,需要有一定的OOP基础,前端对接口、泛型、类、枚举类型有一定的难度
javascript数据类型
- 在JavaScript中数据类型分为原始数据类型和引用数据类型
- 原始数据类型String Boolean Number null undefined symbol
- 引用数据类型Object Function Date Error RegExp
typescript中数据类型
- 变量的类型系统就是控制变量类型的随意变化,变量一旦定义为某种类型就不能被修改,因此ts是强类型语言,而JavaScript中变量的类型是可以随意变化的,归类为弱类型语言
- boolean 原始数据类型-布尔值
let isHandsome: boolean = false
- typescript中也有布尔值对象,注意这个是布尔值对象,其他string、number类似
let isHandsome: Boolean = new Boolean(1)
- number 原始数据类型-数字,二进制和八进制在编译时会被编译成十进制
let count: number = 100
// 二进制
let computer: number = 0b10101
// 八进制
let age: number = 0o677
// 十六进制
let salary: number = 0x00A
- string 原始数据类型-字符串,typescript中也支持模板字符串
let str: string = 'you are handsome'
let sentance: string = `I know ${str}`
- void 空值,在JavaScript中没有空值的概念,在ts中void可以作为函数的返回值,当然也可以定义一个空值,但是并没有实际的意义,因为你只能给他赋值undefined和null
function test (): void {
let params: void = undefined
let params2: void = null
console.log('我是没有返回值的函数')
}
- null和undefined是所有类型的子类型,可以赋值给任意类型,当然也可以自己定义类型。与void的区别是:void类型的变量不能随意赋值给其他类型的变量
let u: undefined = undefined
let n: null = null
let str2: string = undefined
let str3: string = null
let num: number = undefined
let num2: number = null
- any 任意值,表示变量可以赋值为任意类型,当然可以是对象类型,也可以调用方法
let hobby: any = '打麻将'
console.log(hobby.name)
hobby = 100
hobby = false
hobby = undefined
- 未声明类型的变量,在ts中一个变量如果没有声明类型就会默认为any类型
let person
person = 'Mike'
person = {
say () {
console.log('hehe')
}
}
person.say()
-
联合类型(union types)表示取值可以是声明的某一种,联合类型注意:当一个变量是联合类型时,并且我们不能确定他是什么类型时只能访问变量联合类型都有的属性和方法,否则会报错
let myFavorite: string | number
myFavorite = '我可以是字符串类型和数字类型'
// 类型推论系统会推断出myFavorite是字符串类型,可以访问length属性
console.log(myFavorite.length)
myFavorite = 1000
// 类型推论系统会推断出myFavorite是数字类型,不可以访问length属性
myFavorite.length
// 数字类型可以使用
myFavorite.toFixed(2)
function testUnion (something: string | number): void {
// 数字类型是没有length属性,这样就会报错
console.log(something.length)
// 数字类型和string类型都有toString方法
// 正常访问
console.log(something.toString())
}
-
typescript中的对象类型--接口
在面向对象编程中,接口是对行为的抽象,而具体的行动需要由类(class)去实现(implements),在typescript中接口不仅能对类的行为进行抽象,还可以对“对象的形状”进行描述
使用接口类型定义的变量必须保持接口要求的“形状”,不能多也不能少,接口可以要求可选参数、任意添加参数
处于某些设计,我们需要对某些参数只允许进行读取而不能修改,可以添加readonly来对参数进行修饰,使用readonly修饰的参数只能在第一次变量被对象赋值时,后期如果对该参数进行赋值就会报错,因为该参数只可读
// 接口建议是首字母大写,大驼峰
interface Person {
name: string,
age: number
}
// 定义了接口Person又定义了一个变量tom而且tom类型是接口Person这样tom的形状就被约束了必须是Person
// 变量tom的形状必须保持与接口Person一样,不能多也不能少,不然会报错,当然也是可以定义可选参数的
let tom: Person = {
name: 'Tom',
age: 18
}
// 当然ts也提供了可选参数,允许进行变量与接口进行不完全匹配
interface Student {
name: string,
age: number,
class?: string
}
// 可选参数表示可有可无,但是接口中未定义的参数依然是不允许添加的
let st1: Student = {
name: 'mike',
age: 19,
class: 'class one'
}
let st2: Student = {
name: 'mike',
age: 19
}
// 如果希望一个接口可以添加任意属性并且是任意类型,可以使用以下定义
interface Text {
name: string,
age: number,
[propName: string]: any
}
// 注意,如果允许添加任意属性,那么接口中的参数必须是propName所规定的类型
// age是number类型,但是允许添加任意类型是string类型,这样就会报错
interface Texts {
name: string,
age: number,
[propName: string]: string
}
// 添加readonly对参数进行修饰,这样的参数就只能被读取,而不能被修改
interface one {
readonly id: number,
name: string,
age: number,
[propName: string]: string
}
-
数组,ts中数组定义方法多,使用灵活
数组一旦定义为某个,数组中成员必须都是该类型,否则编译时报错。当然也有any类型的数组
let arr: number[] = [1, 2, 3]
// 类型推断系统会推断出arr是一个number数组类型,非法,不通过编译
arr.push('4')
// 非法,不通过编译
let arr2: number[] = [1, '2', 3]
// 联合类型,合法通过编译
let arr3: (number | string)[] = [1, '2', 3]
// 数组的any类型
let arr5: any = [1, 'hello', { age: 10 }]
-
数组泛型-也是定义数组的一种方法
let arr4: Array<number> = [1, 2, 3]
-
接口描述数组
// 表示索引必须是number类型,值也必须是number类型
interface NumberArray {
[index: number]: number
}
-
类数组-并不是数组类型
比如函数的参数列表arguments,常见的类数组都有自己(内置对象)的接口定义,比如arguments的接口定义就是IArguments
function test (): void {
console.log(arguments)
let args: IArguments = arguments
}
-
函数类型
- 函数是一等公民
- 常见函数定义的两种方式:函数声明和函数表达式
- 函数有输入和输出,在ts中可以对函数的输入和输出进行类型约束
- 在typescript中函数形参和实参必须是一一对应的,不能多也不能少,不然无法通过编译,当然也是可以标记可选类型的
- 可以使用接口定义函数形状
- 函数形参可以配置默认值,如果形参配置了默认值,默认就是可选参数
- es6中的扩展运算符...
- 方法(函数)的重载,一个函数接受不同的参数做出不同的行动,函数重载需要注意的是,typescript会从函数最早的定义开始识别匹配,所以多个相同的函数如果有包含关系就需要把精确的定义先写在前面,最后写实现,函数的重载目的就是为了更好的语义话函数定义
// javascript中的函数有输入有输出
function test (x,y) {
return x + y
}
// typescript中的函数可以对输入和输出进行约束
function sum(x: number, y: number): number {
return x + y
}
// typescript中的函数设置可选参数,必填参数放前面,可选放后面
function sum(x: number, y?: number): number {
return x + y
}
// 函数表达式,但是这个通过赋值操作的类型推断的
let mySum = function (x: number, y: number): number {
return x + y
}
// 函数表达式手动添加类型
// 注意 => 不是es6中箭头函数,这个符号右边代表的是输出类型,左边代表的是输入类型
let mySum2: (x: number, y:number) => number = function (x: number, y:number): number {
return x + y
}
// 使用接口定义函数的形状
interface Search {
(source: string, subString: string): boolean
}
let my: Search
my = function (source: string, subString: string) {
return source.search(subString) !== -1
}
// 形参默认值
function getFullName (firstName: string, lastName: string = 'Mike'): string {
return firstName + lastName
}
let result = getFullName('Jackson')
// 数组的扩展运算符
function myPush(array: any[], ...items: any[]): void {
items.forEach(item => {
array.push(item)
})
}
myPush([], 1, 2, '3')
// 需求,根据输入的数字的类型和字符串进行反序
// 如果不使用重载,使用联合类型
function myReverse (x: string | number): string | number {
if (typeof x === 'number') {
return Number(x.toString().split('').reverse().join(''))
} else if (typeof x === 'string') {
return x.split('').reverse().join('')
}
}
// 使用重载
function myReverse2 (x: string): string
function myReverse2 (x: number): number
function myReverse2 (x: string | number): string | number {
if (typeof x === 'number') {
return Number(x.toString().split('').reverse().join(''))
} else if (typeof x === 'string') {
return x.split('').reverse().join('')
}
}
typescript的类型推论
typescript类型推论系统会在变量的第一次声明和赋值的时候推断出这个变量的类型
let result = 'what the fuck'
// typescript的类型推论系统已经推断出result的类型是string再赋值number就会报错
result = 100
typescript的类型断言-手动指定变量类型
在联合类型中,我们并不知道变量一定是什么类型,但是有时候我们又需要提前调用变量的某个属性或者方法,这个时候我们就可以断言该变量是某种类型,但是规矩是,断言的类型必须是联合类型中的一种,如果最终传入的变量不是断言的类型,访问依然可以进行,但是会得到undefined
类型断言支持2中语法,<要断言的类型>或者as
// 类型断言
function test (variable: string | number): void {
// 现在就想使用字符串的length属性
console.log((<string>variable).length)
console.log((variable as number).toFixed(2))
}
// 得到长度5但是字符串没有toFixed方法会报错,编译可以通过
test('hello')
// number类型没有长度,得到undefined,编译可以通过
test(123)
声明文件
在使用第三方库,一些全局关键字在typescript中我们并不知道他是什么,比如jQuery中的$和jQuery
例如$("#id")或者jQuery("#id") 在typescript直接使用是会报错的,这个时候就需要用到声明文件
通常我们会把类型声明单独放到一个文件中,这就是声明文件,还会约定声明文件以.d.ts结尾,然后需要用到声明的文件的最开头使用///进行引用声明文件
declare var jQuery: (selector: string) => any
// 引用,在需要声明文件的文件最开始使用 /// 进行引用声明文件
/// <reference path="./jQuery.d.ts" />
第三方声明文件
一般不需要自己来声明,网上都有别人声明好了的,typescript还推荐开发者使用@types来管理第三方的库
比如安装jQuery:npm install @types/jquery --save-dev
内置对象
在JavaScript中有很对内置对象如Error、Date、RegExp、Boolean、Number 等等
在typescript中我们可以根据这些内置对象创建变量
内置对象的定义文件都在typescript的核心库定义文件中
let bool: Boolean = new Boolean(1)
let err: Error = new Error('Error occurred')
let date: Date = new Date()
let reg: RegExp = /[a-z]/
DOM和BOM的内置对象
有Document
、HTMLElement
、Event
、NodeList等
内置对象的定义文件都在typescript的核心库定义文件中
let body: HTMLElement = document.body
let allDiv: NodeList = document.querySelectorAll('div')
document.addEventListener('click', function (e: MouseEvent) {
// Do
})
typescript 核心库的定义文件
typescript 核心库的定义文件定义了浏览器环境所有需要的类型,并且预置在typescript中,我们在使用一些常用的方法时,其实typescript已经做了很多的类型判断工作,比如pow(10,'2')是会报错的
我们在ts中使用的很多方法和事件,ts在背后都做很多的类型推断工作,这些定义文件都在typescript的核心库文件中
Node
在typescript中没有内置Node,如果想要使用ts写Node那就需要引入第三方声明文件
npm install @types/node --save-dev