Typescript简介
TypeScript是由微软公司在2012年正式发布,现在也有8年的不断更新和维护了,TypeScript的成长速度是非常快的,现在已经变成了前端必会的一门技能。TypeScript其实就是JavaScript的超集,也就是说TypeScript是建立在JavaScript之上的,最后都会转变成JavaScript。这就好比漫威里的钢铁侠,没穿装甲之前,他实力一般,虽然聪明有钱,但还是接近人类。但是有了装甲,他就厉害太多了,甚至可以和神干一架。
TypeScript开发环境搭建
如果你想使用TypeScript来编写代码,你需要先安装一下它的开发环境,这并不复杂。
1.安装Node的运行环境
你可以到Node.js
官网去下载Node进行安装(https://node.js.org),建议你下载LTS
版本,也就是长期支持版本。安装的过程我就不演示了,这个过程就和安装QQ一样,没有任何难度。
如果你已经安装了,可以打开命令行工具,然后使用node -v
命令查看安装的版本,但是一般还有一个命令需要检测一下,就是npm -v
,如果两个命令都可以输出版本号,说明你的Node安装已经没有任何问题了。
2.全局安装TypeScript
你要使用TypeScript先要在你的系统中全局安装一下TypeScript
,这里你可以直接在VSCode中进行安装,安装命令可以使用npm也可以使用yarn
1 | npm install typescript -g |
这两个你使用那个都是可以的,根据喜好自行选择,我在视频中使用的npm
进行安装。
3.建立项目目录和编译TS文件
在E盘
(当然你可以在你喜欢的任何一个地方安装),新建一个目录,我这里起的目录名字叫做TypeScriptDemo
,然后把这个文件在VSCode中打开。 我在视频里用了命令行的形式建立,直接使用ctrl+r
打开运行,然后在运行的文本框里输入cmd
,回车后,打开命令行工具,在命令行中输入下面的命令。
1 | e: |
完成后,打开E盘,打开VSCode,把新建立的文件夹拖入到VSCode当中,新建一个demo1.ts`文件,写入下面代码:
1 | function jsfei(){ |
这时候你使用node demo1.ts
是执行不成功的,因为Node不能直接运行TypeScript
文件,需要用tsc demo1.ts
转换一下,转换完成后typeScript
代码被编译成了javaScript
代码,新生成一个demo1.js
的文件,这时候你在命令行输入node demo1.js
,在终端里就可以顺利的输出jsfei
的字符了。
4.ts-node的安装和使用
但是这样操作的效率实在是太低了,你可以使用ts-node
插件来解决这个问题,有了这个插件,我们就不用再编译了,而使用ts-node
就可以直接看到编写结果。
使用npm
命令来全局安装,直接在命令行输入下面的命令:
1 | npm install -g ts-node |
安装完成后,就可以在命令中直接输入如下命令,来查看结果了。
1 | ts-node Demo1.ts |
TypeScript的静态类型
TypeScript的一个最主要特点就是可以定义静态类型,英文是Static Typing。那到底是什么意思那?太复杂的概念性东西这里就不讲了,你可以简单的理解“静态类型”为,就是你一旦定义了,就不可以再改变了。比如你是男人就是男人,一辈子都要作男人;是女人就是女人,一辈子都是女人。这个事不可以改变!呃….好像现在也可以随便变来变去啊,这里说的是正常情况。但是它还有一些特性,这个并不像表面的那么简单。现在我们就来学习。
如何定义静态类型
建立一个新的demo2.ts
文件,然后写下这段代码:
1 | const count : number = 1; |
这就是最简单的定义一个数字类型的count
的变量,这里的: number
就是定义了一个静态类型。这样定义后count
这个变量在程序中就永远都是数字类型了,不可以改变了。比如我们这时候给count
复制一个字符串,它就报错了。
1 | //错误代码 |
但这只是最简单的理解,再往深一层次理解,你会发现这时候的count
变量,可以使用number
类型上所有的属性和方法。我们可以通过在count
后边打上一个.
看出这个特性,并且编辑器会给你非常好的提示。这也是为什么我喜欢用VScode
编辑器的一个原因。
自定义静态类型
你还可以自己去定义一个静态类型,比如现在你定义一个小姐姐
的类型,然后在声明变量的时候,就可以使用这个静态类型了,看下面的代码。
1 | interface XiaoJieJie { |
这时候你如果声明变量,跟自定义不一样,VSCode
直接就会报错。需要注意的是,这时候xiaohong
变量也具有uname
和age
属性了。
这个地方你需要记住的是,如果使用了静态类型,不仅意味着变量的类型不可以改变,还意味着类型的属性和方法也跟着确定了。这个特点就大大提高了程序的健壮性,并且编辑器这时候也会给你很好的语法提示,加快了你的开发效率。
随着你不断的深入学习,你会对这两个优点有更深的理解。
TypeScript基础静态类型和对象类型
在TypeScript静态类型分为两种,一种是基础静态类型,一种是对象类型,这两种都经常使用,非常重要,我们先来看一下什么是基础静态类型。
基础静态类型
基础静态类型非常简单,只要在声明变量的后边加一个:
号,然后加上对应的类型哦。比如下面的代码,就是声明了一个数字类型的变量,叫做count
。
1 | const count : number = 918; |
类似这样常用的基础类型还有,我这里就举几个最常用的哦,null
,undefinde
,symbol
,boolean
,void
这些都是最常用的基础数据类型,至于例子,我这里就不详细的写了,后面碰到,我们再继续讲解。
对象类型
我们先来看一个例子,通过例子有经验的小伙伴就知道个大概了,然后我们再来讲解。新建一个文件demo3.ts
(你可以跟我不一样),然后写下如下代码。
1 | const xiaoJieJie:{ |
写完后,我们在terminal
(终端)中输入ts-node demo3.ts
,可以看到结果输出了大脚
。这就是一个经典的对象类型,也是最简单的对象类型。对象类型也可以是数组,比如现在我们需要很多小姐姐,我们就可以这样写。
1 | const xiaoJieJies : String [] = ['谢大脚','刘英','小红'] |
这时候的意思是,变量xiaoJieJies
必须是一个数组,数组里的内容必须是字符串。你可以试着把字符串改为数字,VSCode
会直接给我们报错。
1 | const xiaoJieJies : String [] = ['谢大脚','刘英',123] |
现在都讲究面向对象编程,我这面向对象编程这么多年了,也没再多编出来一个。我们再来看看下面的代码。这个代码就是用类的形式,来定义变量。
1 | class Person{} |
这个意思就是dajiao
必须是一个Person
类对应的对象才可以。我们还可以定义一个函数类型,并确定返回值。代码如下:
1 | const jianXiaoJieJie : ()=> string =()=>{return '大脚'} |
那我们现在总结一下对象类型可以有几种形式:
- 对象类型
- 数组类型
- 类类型
- 函数类型
这几种形式我们在TypeScript
里叫做对象类型。
TypeScript中的类型注释和类型推断
TypeScript中的两个基本概念:类型注解
和类型推断
,这两个概念在我们编写TypeScript代码时会一直使用(重点),但很多教程都没有讲解,不过在官方文档中有比较好的解释。你现在可能还不能完全理解我说的这两个概念,但是你看完文章后就会有很直观的了解啦。
type annotation 类型注解
这个概念我们在前三节课中一直使用,只是我没明确这个概念和关系,所以你会觉的很陌生。这就好比,你身边有一个特别漂亮的姑娘,她一直很喜欢你,你也很喜欢她,但窗户纸一直没捅破,直到有一天你们喝多后,去了如家酒店(谈了谈心),你们的关系就明确了。
学程序并没有这么复杂,我们直接点,新建一个文件demo4.ts
,然后看代码:
1 | let count : number; |
这段代码就是类型注解,意思是显示的告诉代码,我们的count
变量就是一个数字类型,这就叫做类型注解
。是不是一下就明白了,其实程序这东西就这么简单,真正复杂的是人。
type inferrence 类型推断
当你明白了类型注解
的概念之后,再学类型推断就更简单了,先来看一段代码。还是在Demo4.ts
文件中写入下面的代码。
1 | let countInference = 123 |
这时候我并没有显示的告诉你变量countInference
是一个数字类型,但是如果你把鼠标放到变量上时,你会发现TypeScript自动把变量注释为了number
(数字)类型,也就是说它是有某种推断能力的,通过你的代码TS会自动的去尝试分析变量的类型。这个就彷佛是人的情商比较高,还没等女生表白那,你就已经看出她的心思。
工作使用问题(潜规则)
- 如果
TS
能够自动分析变量类型, 我们就什么也不需要做了 - 如果
TS
无法分析变量类型的话, 我们就需要使用类型注解
先来看一个不用写类型注解的例子:
1 | const one = 1; |
再来看一个用写类型注解的例子:
1 | function getTotal(one , two){ |
这种形式,就需要用到类型注释了,因为这里的one
和two
会显示为any
类型。这时候如果你传字符串,你的业务逻辑就是错误的,所以你必须加一个类型注解
,把上面的代码写成下面的样子。
1 | function getTotal(one : number, two :number){ |
这里有的一个问题是,为什么total
这个变量不需要加类型注解,因为当one
和two
两个变量加上注解后,TypeScript就可以自动通过类型推断,分析出变量的类型。
当然TypeScript也可以推断出对象中属性的类型,比如现在写一个小姐姐的对象,然后里边有两个属性。
1 | const XiaoJieJie = { |
写完后你把鼠标放在XiaoJieJie
对象上面,就会提示出他里边的属性,这表明TypeScript也分析出了对象的属性的类型。
在写TypeScript代码的一个重要宗旨就是每个变量,每个对象的属性类型都应该是固定的,如果你推断就让它推断,推断不出来的时候你要进行注释。
TypeScript函数参数和返回类型定义
这节主要学习一下函数的参数类型定义和返回值的定义,学完这节内容后,你会对函数的参数和返回值类型定义都有通透的了解。
简单类型定义
上节课我们写了一个getTotal
的函数,并且对传入的参数作了定义,我们再复习一遍。
新建一个文件demo5.ts
,然后写入代码
1 | function getTotal(one : number, two :number){ |
这时候我们写的代码其实是有一个小坑的,就是我们并没有定义getTotal
的返回值类型,虽然TypeScript
可以自己推断出返回值是number
类型。 但是如果这时候我们的代码写错了,比如写程了下面这个样子。
1 | function getTotal(one : number, two :number){ |
这时候total
的值就不是number
类型了,但是不会报错。有的小伙伴这时候可能会说,可以直接给total
一个类型注解,比如写成这个样子。
1 | const total : number =getTotal(1,2) |
这样写虽然可以让编辑器报错,但是这还不是很高明的算法,因为你没有找到错误的根本,这时错误的根本是getTotal()
函数的错误,所以合适的做法是给函数的返回值加上类型注解,代码如下:
1 | function getTotal(one : number, two :number) : number{ |
这段代码就比较严谨了,所以小伙伴们在写代码时,尽量让自己的代码更加严谨。
函数无返回值时定义方法
有时候函数是没有返回值的,比如现在定义一个sayHello
的函数,这个函数只是简单的terminal
打印,并没有返回值。
1 | function sayHello(){ |
这就是没有返回值的函数,我们就可以给他一个类型注解void
,代表没有任何返回值。
1 | function sayHello() : void{ |
如果这样定义后,你再加入任何返回值,程序都会报错。
never返回值类型
如果一个函数是永远也执行不完的,就可以定义返回值为never
,那什么样的函数是永远也执行不完的那?我们先来写一个这样的函数(比如执行执行的时候,抛出了异常,这时候就无法执行完了)。
1 | function errorFuntion() : never{ |
还有一种是一直循环,也是我们常说的死循环,这样也运行不完,比如下面的代码:
1 | function forNever() : never{ |
函数参数为对象(解构)时
这个坑有很多小伙伴掉下去过,就是当一个函数的参数是对象时,我们如何定义参数对象的属性类型。我先写个一般javaScript
的写法。
1 | function add ({one , two}){ |
在浏览器中你会看到直接报错了,意思是total
有可能会是任何类型,那我们要如何给这样的参数加类型注解
那?最初你可能会这样写。
1 | function add ({one :number , two :number}){ |
你在编辑器中会看到这种写法是完全错误的。那正确的写法应该是这样的。
1 | function add ({one , two } : {one:number, two:number}) :number{ |
如果参数是对象,并且里边只有一个属性时,我们更容易写错。 错误代码如下:
1 | function getNumber ({one }:number){ |
看着好像没什么问题,但实际这是有问题的,正确的代码应该时这样的。
1 | function getNumber ({one } :{one:number}) :number{ |
这样写才是正确的写法,小伙伴们赶快动手练习一下吧,刚开始学你可能会觉的很麻烦,但是你写的时间长了,你就会发现有规矩还是好的。人向往自由,缺鲜有人能屈驾自由。
TypeScript中数组类型的定义
这节课学习一下TypeScript中的数组类型,一般的数组类型定义我们已经接触过了,只是没有单独拿出来讲,所以先来复习一下。
一般数组类型的定义
现在我们可以定义一个最简单的数组类型,比如就是数字类型,那么就可以这么写:
1 | const numberArr = [1,2,3] |
这时候你把鼠标放在numberArr
上面可以看出,这个数组的类型就是number类型。这是TypeScript通过类型推断
自己推断出来的。 如果你要显示的注解,也非常简单,可以写成下面的形式。
1 | const numberArr:number[] = [1,2,3] |
同样道理,如果你的数组各项是字符串,你就可以写成这样。
1 | const stringArr : string [] = ['a','b','c'] |
也就是说你可以定义任意类型的数组,比如是undefined
。
1 | const undefinedArr : undefined[]=[undefined,undefined] |
这时候问题来了,如果数组中有多种类型,比如既有数字类型,又有字符串的时候。那我们要如何定义那。 很简单,只要加个()
,然后在里边加上|
就可以了,具体看代码。
1 | const arr: ( number| string )[] = [1,'string',2] |
数组简单类型的定义就是这样了,并不难。
数组中对象类型的定义
如果你作过一些项目,你就会知道真实的项目中数组中一定会有对象的出现。那对于这类带有对象的数组定义就稍微麻烦点了。 比如现在我们要定义一个有很多小姐姐的数组,每一个小姐姐都是一个对象。这是的定义就编程了这样。
1 | const xiaoJieJies : {name:string , age:Number}[] = [ |
这种形式看起来比较麻烦,而且如果有同样类型的数组,写代码也比较麻烦,TypeScript为我们准备了一个概念,叫做类型别名
(type alias)。
比如刚才的代码,就可以定义一个类型别名
,定义别名的时候要以type
关键字开始。现在定义一个Lady
的别名。
1 | type Lady = {name:string , age:Number}; |
有了这样的类型别名以后哦,就可以把上面的代码改为下面的形式了。
1 | type Lady = {name:string , age:Number}; |
这样定义是完全起作用的,比如我们下面在对象里再加入一个属性,这时候编译器就会直接给我们报错了。
这时候有的小伙伴就会问了,我用类进行定义可以吗?答案是可以的,比如我们定义一个Madam
的类,然后用这个类来限制数组的类型也是可以的。
1 | class Madam { |
可以看到结果,我们这么写也是完全可以的。
TypeScript中元组的使用和类型约束
TypeScript中提供了元组
的概念,这个概念是JavaScript
中没有的。但是不要慌张,其实元组在开发中并不常用,也可能是我的经历还不够。一般只在数据源是CSV
这种文件的时候,会使用元组。其实你可以把元组看成数组的一个加强,它可以更好的控制或者说规范里边的类型。
元组的基本应用
我们先来看一个数组和这个数组注解的缺点,比如我们有一个小姐姐数组,数组中有姓名、职业和年龄,代码如下:
1 | const xiaojiejie = ['dajiao','teacher',28] |
这时候把鼠标放到xiaojiejie
变量上面,可以看出推断出来的类型。我们就用类型注解的形式给他作一个注解,代码如下:
1 | const xiaojiejie :(string | number)[] = ['dajiao','teacher',28] |
这时候你已经增加了代码注解,但是这并不能很好的限制,比如我们把代码改成下面的样子,TypeScript
依然不会报错。
1 | const xiaojiejie :(string | number)[] = ['dajiao',28,'teacher'] |
我们只是简单的把数组中的位置调换了一下,但是TypeScript
并不能发现问题,这时候我们需要一个更强大的类型,来解决这个问题,这就是元组。
元组和数组类似,但是类型注解时会不一样。
1 | const xiaojiejie : [string,string ,number] = ['dajiao','teacher',28] |
这时候我们就把数组中的每个元素类型的位置给固定住了,这就叫做元组。
元组的使用
目前我的工作中不经常使用元组,因为如果要使用元组,完全可以使用对象的形式来代替,但是如果你维护老系统,你会发现有一种数据源时CSV
,这种文件提供的就是用逗号隔开的,如果要严谨的编程就需要用到元组了。例如我们有这样一组由CSV
提供的(注意这里只是模拟数据)。
1 | 'dajiao','teacher',28 |
如果数据源得到的数据时这样的,你就可以使用元组了。
1 | const xiaojiejies:[string,string,number][]=[ |
要搞清楚元组和数组的区别,在理解后能在项目中适当的时候使用不同的类型。
TypeScript中的interface接口
现在公司要开始招聘小姐姐了,这时候你需要一些小姐姐的简历投递和自动筛选功能,就是不符合简历要求的会直接被筛选掉,符合的才可以进入下一轮的面试。那最好的解决方法就是写一个接口。TypeScript中的接口,就是用来规范类型的。
Interface接口初步了解
现在我们要作一个简历的自动筛选程序,很简单。年龄小于25岁,胸围大于90公分的,可以进入面试环节。我们最开始的写法是这样的。(新建一个文件Demo8.ts,然后编写如下代码)
1 | const screenResume=( name:string, age:number,bust:number)=>{ |
写好后,好像我们的程序写的不错,可以在终端中使用ts-node demo8.ts
进行查看。这时候老板又增加了需求,说我必须能看到这些女孩的简历。于是你又写了这样一个方法。
1 | const getResume=(name:string,age:number,bust:number)=>{ |
这时候问题来了,程序开发中一直强调“代码重用”,两个方法用的类型注解一样,需要作个统一的约束。大上节课我们学了一个类型别名
的知识可以解决代码重复的问题,这节课我们就学习一个更常用的语法接口
(Interface).
我们可以把这两个重复的类型注解,定义成统一的接口。代码如下:
1 | interface Girl { |
有了接口后,我们的程序也要作一些修改,需要写成下面的样子。这样就更像是面向对象编程了。
1 | const screenResume=( girl:Girl)=>{ |
这时候我们代码就显得专业了很多,以后再用到同样的接口也不怕了,直接使用girl
就可以了。
接口和类型别名的区别
现在我们学了接口
,也学过了类型别名
,这两个语法和用处好像一样,我先表个态,确实用起来基本一样,但是也有少许的不同。
类型别名可以直接给类型,比如
string
,而接口必须代表对象。
比如我们的类型别名
可以写出下面的代码:
1 | type Girl1= stirng |
但是接口就不能这样写,它必须代表的是一个对象,也就是说,你初始化girl
的时候,必须写出下面的形式.
1 | const girl={ |
接口非必选值得定义
这节课我们多学一点,因为接口这里的知识点还是挺多的。比如这时候老板又有了新的要求,要求尽量能看到小姐姐的腰围,但是不作强制要求,就是可选值吗。那接口如何定义那?其实typeScript
已经为我们准备好了相应的办法,就是在:
号前加一个?
比如把Girl
的接口写成这样。
1 | interface Girl { |
然后我们再修改一下getResume
方法,写成这样。
1 | const getResume=( girl:Girl)=>{ |
这时候在定义girl
对象的时候,就可以写saistline
(腰围),也可以不写了。
好了,这节课就先到这里,Interface
(接口)的知识并没有讲完,我们下节课接着讲。
TypeScript中的interface接口2
允许加入任意值
简历一般是有自由发挥的空间的,所以这时候需要一些任意值,就是自己愿意写什么就写什么。这时候interface
接口也是支持的。方法如下: 我们接着上节课的代码,新建一个demo6.ts
,然后把上节课代码拷贝过来。
1 | interface Girl { |
这个的意思是,属性的名字是字符串类型,属性的值可以是任何类型。
这时候我们在对象里给一个性别,代码如下:
1 | const girl={ |
再修改一下代码,这首就没有错误了。
1 | const getResume=( girl:Girl)=>{ |
这时候我们的程序是不报错的,但是如果我们去掉刚才的设置,就会报错。
1 | [propname:string]:any; //去掉 |
接口里的方法
接口里不仅可以存属性,还可以存方法,比如这时候有个say()
方法,返回值是string
类型。这时候你就不要再想成简历了,你需要更面向对象化的编程,想象成一个人。
1 | interface Girl { |
加上这个say()
方法后,程序马上就会报错,因为我们对象里没有say方法。那我们就要给对象一个say方法
1 | const girl={ |
接口和类的约束
我们都知道JavaScript从ES6
里是有类这个概念的,类可以和接口很好的结合,我们先来看一个例子。下面的
1 | class XiaoJieJie implements Girl{ |
这时候类会直接报错,所以我们需要把这个类写的完全点。
1 | class XiaoJieJie implements Girl{ |
接口间的继承
接口也可以用于继承的,比如你新写一个Teacher
接口,继承于Person
接口。
1 | interface Teacher extends Girl{ |
比如这时候老板说了,只看Teacher级别的简历,那我们需要修改getResume()
方法。
1 | const getResume=( girl:Teacher)=>{ |
修改后,你就会发现下面我们调用getResume()
方法的地方报错了,因为这时候传的值必须有Teach
方法,
1 | getResume(girl) |
修改girle
对象,增加teach()
方法,这时候就不会报错了。
1 | const girl={ |
关于接口的知识就讲到这里吧,这基本包含了接口80%的知识,还有些基本不用的语法,我就不讲了。如果课程中遇到,我们再讲。学会了接口,你还需要明白一件事,就是接口只是对我们开发的约束,在生产环境中并没有体现。也可以说接口只是在TypeScript里帮我们作语法校验的工具,编译成正式的js
代码,就不会有任何用处了。
- 本文作者: Raphael_Li
- 本文链接: https://lifei-2019.github.io/Typescript/
- 版权声明: 本博客所有文章除特别声明外,均采用 Apache License 2.0 许可协议。转载请注明出处!