ts快速入门
TypeScript快速入门
对于前端开发者,TypeScript的重要性就不必多言了。如果你经常写一些类库,ts绝对会让你库更加规范。如果你是一个新技术追求者,那你会去学习vue3.2、react18,那势必会与TypeScript结合起来开发。
ts的介绍
1995网景公司的程序员布莱登艾奇,花了10天就开发了JavaScript。对这门语言的定位为网页脚本语言,用来做表单校验。说白了,就是应付领导随意开发的一个产品,用完就扔掉。结果随着时间的推移,JavaScript这门语言发展得越来越好。而且做的应用越来越大,所以你会发现JavaScript出现了疲软,不太适合开发大型项目,具体原因如下:
- js中这个变量时没有类型,只有值有类型。
- 太灵活,很多bug都是在运行的时候才出来的。
- js编写的应用,写代码的过程有些bug排查不了,只有等到上线之后才会出现问题。
- 复杂的继承实现
微软公司为了开发大型应用,开发了新的一门语言名字叫做TypeScript,从名字来看其重要的功能就是类型约束与类型推导,其更加严谨,让开发者在写代码的过程中就发现错误(很多时候JavaScript需要运行之后才能发现错误)。特别适合开发大型应用。
- TypeScript是JavaScript的超集(也就是包含关系)
- TypeScript的文件后缀名,.ts
- 最后TypeScript要编译成JavaScript,才能运行到浏览器(node环境)
- 更好的代码提示(vscode)
- 在编写代码过程中什么都能点出来
- 主要的功能:类型推断与类型约束
总结: js由于历史原因不太适合开发大型应用,然后微软开发一门新的编程语言ts,ts全包含js,而且还做了一系列的跨站,让ts适合开发大型项目,让你项目更加严谨。 ts是趋势,你学习vue3.2,react18,一定会与ts结合
官网: https://www.tslang.cn/docs/handbook/basic-types.html
环境搭建
本次是为了学习快速搭建一个环境。你需要注意:在实际开发中脚手架会帮你搭建好。其主要功能如下:
- 实时编译ts文件
- 实时运行编译之后的js文件
学习ts环境搭建步骤如下:
全局安装typescript
- yarn global add typescript
- npm i typescript -g
- 如果你的控制显示tsc不是一个命令,那就是你的yarn还没有环境变量,
- 你使用npm安装
安装成功之后,你会发现你拥有了一个命令tsc(TypeScript compiler)。然后可以运行一下命令查看版本
- tsc --version
- 如果你的vscode的终端不行,那今天咱们就是用cmd
然后再新创建一个文件夹,命名为ts-study
- 在其中创建一个ts文件
将一个ts文件编译成js文件
- tsc 文件名.ts
生成项目的ts配置文件
- tsc --init
配置项目的ts,编译之后的版本,编译之后的文件放在哪里?
{ "compilerOptions": { // 把ts编译成多少版本 "target": "es2016", // 模块类型 "module": "commonjs", // 编译后文件放置的位置 "outDir": "./dist", } }
实时编译整个项目:在根目录下运行: tsc -w
实时运行编译后的js 【选做】
- yarn global add nodemon npm i nodemon -g 实时的运行一个js文件,只要这个js文件发生改变就会再次运行它
- cd dist (去编译的js文件的位置)
- nodemon 文件名.js
ts的类型
。因为ts是js的超集,所以js有的基本类型ts都会有。然后在此基础上扩展很多类型。
定义基本类型【基本】
js有的基本类型,ts都会有.分别是:string、number、boolean、null、undefined、symbol
// 隐式定义一个数字
let num1 = 123
// 显示定义一个数字
let num2:number = 123
any类型【少用】
如果你申明一个变量不给其赋值初始值,那么他就是一个any。或者你可以显示定义一个变量为any类型。注意此玩意不能滥用,它有以下几个特点:
失去类型,代码可能会缺失
any类型的变量可以赋值其他任何类型(跟写js没什么两样了)
// 显示申明一个any类型 let a:any = 123 a = '123' let b:number = 234 // 这里因为a是any,所以它可以赋值给任意类型 a = b
联合类型【常用】
在项目比较常用
// 定义一个类型可能是string又可能是number
let a: string | number = '1234'
类型断言【常用】
as操作符,我们自己推导类型,不用ts帮我们推导
let a
// a是一个any类型
a = '我,是,字,符,串'
// 我自己推导
let arr = (a as string).split(',')
void类型【不常用】
与any相反,就是没有任何类型,常用来表示一个没有返回的函数的返回类型
// 此函数没有返回值,所以它的返回类型为void
function test():void {
console.log('xxx')
}
never类型【不常用】
永远不能达到,如果一个函数抛出错误,一个永远执行不完的函数。其返回值都是never。基本不怎么用
function error():never {
throw Error('')
}
// 推断的返回值类型为never
function fail() {
return error();
}
// 返回never的函数必须存在无法达到的终点
function infiniteLoop(): never {
while (true) {
}
}
枚举
枚举用来定义一个有穷集合:比如:性别(男女),方向(上下左右),订单状态(已下单,待收货,已收货,待评价,已完成)。其语法如下:
// 枚举的使用,有点类似于 key:value的形式
enum Sex { 女, 男 }
console.log(Sex.男)
console.log(Sex[0])
ts中的数组
在开发大型项目的时候,通常要规定一个数组中能装的具体类型
let arr: string[] = ['123']
let arr1: Array<number> = [1,2,3,4]
元组【不常用】
定义一个长度为2的数组,第一个为string类型,第二个为number类型。
let arr3: [string, number] = ['字符', 123]
但是也可以越界,但是越界之后值必须为规定的类型中的一种
let arr3: [string, number] = ['字符', 123]
// 这里push的是 string | number的联合类型
arr3.push(456)
ts中的函数【重要的很】
普通js函数比较灵活,参数随便传,返回可有可无。在大型应用中,通常我们需要对函数进行约束,比如参数的个数,每个参数的类型,返回值的类型....
function add(x:number, y:number):number {
return x + y
}
定义了一个函数:参数两个,都是数字类型。而且返回值的类型为数字
可选参数
?使用函数的形参里面表示该参数可有可无
function sayHi(name: string, age: number, girlFriend?:string):string {
let res = `我叫:${name},今年:${age}`
if(girlFriend) {
res += `, 我女朋友是:${girlFriend}`
}
return res
}
console.log(sayHi('张三', 18, '赵丽颖'))
默认参数
function sayHi(name: string, age: number, girlFriend:string = '张丽颖'):string {
let res = `我叫:${name},今年:${age}`
if(girlFriend) {
res += `, 我女朋友是:${girlFriend}`
}
return res
}
console.log(sayHi('缪佳耕', 18))
注意默认参数不能和可选参数一起使用
剩余参数
求和
function add(x:number, y:number, ...args:number[]):number {
return args.reduce((prev, cur) => prev += cur, x+y)
}
console.log(add(1,2,3,4,5,6,7))
ts中的接口【常用】
ts中的接口:interface。功能强大,项目中用得比较多,在一些第三库里面也比较常见,所以学习ts必要会使用interface。 通常是用来描述对象,有些时候也可以用来描述函数
约束对象【重要的很】
定义一个接口,约束一个人的对象。
interface IPerson {
name: string
age: number
}
let p1:IPerson = {
name: '张三',
age: 24
}
定义了一个接口越来约束人,这个人有两个属性:name与age,其中name是string类型,age是number类型。
仅读属性
interface IPerson {
// 仅读属性
readonly name: string
age: number
}
let p1:IPerson = {
name: '张三',
age: 24
}
可选属性
interface IPerson {
// 仅读属性
readonly name: string
// 可选属性
age?: number
}
let p1:IPerson = {
name: '张三',
age: 24
}
扩展属性
interface IPerson {
// 仅读属性
readonly name: string
// 可选属性
age?: number
// 扩展属性
[key:string]: any
}
接口继承【了解】
接口之间还可以继承,比如上诉,我们定义了一个人,下面我们要定义一个男人的接口。
// 人
interface IPerson {
readonly name: string
age: number
}
// 男人
interface IMan extends IPerson {
wife: string
lover: string
}
接口约束函数【了解】
用接口来秒速以及约束函数
interface ISayHi {
(name:string, age:number): string
}
let sayHi:ISayHi = (name, age) => {
return ''
}
在上诉例子中给男人加一个自我介绍的函数,其实现如下:
// 人
interface IPerson {
readonly name: string
age: number
}
// 男人
interface IMan extends IPerson {
wife: string
lover: string
// 一个自我介绍函数
sayHi(name:string, age:number):string
}
const m:IMan = {
name: '张三',
age: 24,
wife:'xx',
lover:'yy',
sayHi(name, age) {
return `我叫:${name}, 今年:${age}`
}
}
接口实现Class【了解】
在java语言中,接口通常用来指定一个类(你可以理解为提供了一种规范),然后其他开发者去实现它。
// 人
interface IPerson {
readonly name: string
age: number
[key:string]: any
}
// 实现一个接口
class Person implements IPerson {
[key: string]: any
name: string
age: number
constructor(name:string, age:number) {
this.name = name
this.age = age
}
}
const p = new Person('jgmiu', 24)
ts中的type
在ts中除了ts自带的类型,它也允许我们自己定义一个类型。初学者会把它和interface搞混,其实两者没任何关系(雷锋与雷峰塔的关系吧),只是他们做的事情有一些交叉点
基本语法
你可以把type类比为var、const、let,只是后置是定义变量,而type是定义类型
// 定义一个自己的类型
type myType = string | number
let aa: myType = 12
我们定义了一个自己的类型myType,其实它就是一个联合类型。
定义一个对象
type user = {
readonly name: string
age: number
}
let u1:user = {
name: 'xxx',
age: 24
}
此处功能与interface重合,但是type没有继承一说
定义一个函数类型
type add = (x:number, y:number, ...args:number[]) => number
const addFunc:add = (x, y, ...args) => {
return args.reduce((prev, cur) => prev += cur, x + y)
}
console.log(addFunc(1,2,3,3,4,5))
定义函数与interface的语言还是有少许差别。
ts中的class
定义一个人类
ts重点额class与es6的class语法差不多。我们还是以定义一个Person(人类),然后再定义一个Man(男人)的例子来展开说明。
// 实现一个接口
class Person {
[key: string]: any
name: string
age: number
constructor(name:string, age:number) {
this.name = name
this.age = age
}
}
const p = new Person('jgmiu', 24)
定义一个男人类(继承人类)
class Person {
name:string
age:number
constructor(name:string, age:number) {
this.name = name
this.age = age
}
}
class Man extends Person {
wife:string
lover:string
constructor(name:string, age:number, wife:string, lover:string) {
super(name, age)
this.wife = wife
this.lover = lover
}
playWithWife() {
console.log(`今天和我老婆${this.wife}出去吃米线`)
}
playWithLover() {
console.log(`今天和我爱人${this.lover}出去吃大餐`)
}
}
属性修饰符 【了解!!】
- public(公共的)
- 什么都可以访问
- private(私有的)
- 出了在自身的class能访问,其他地方都不能访问(子类也拿不到)
- protected(受保护)
- 跟private差不多的,唯一的区别就是,子类能够访问
ts中泛型【重要的很】
泛型让我们的函数,class,type更加灵活,重用性更高。学习ts,能看懂泛型是必备的技能。
引入例子(在type中使用)
下面有两个函数。第一个是两个数字相加返回数字,第二个是两个字符串相加返回字符串。
function numAdd(x:number, y:number):number {
return x + y
}
function strAdd(x:string, y:string):string {
return x + y
}
两个函数有很多公共的地方,不同的就是类型。这个时候就改泛型登场了。
type add<T> = (x:T, y:T) => T
const numAddFunc:add<number> = (x,y) => {
return x + y
}
const strAddFunc:add<string> = (x,y) => {
return x + y
}
你可以发型类型可以向传参那样传进去了
泛型在interface中的使用
// 次数的firend,我们不确定用户使用的使用,到底是一个对象,还是一个字符串,
// 所以此处使用一个泛型,用T作为一个占位
interface IUser<T = string> {
name: string,
firend: T
}
const u1:IUser = {
name: '张三',
firend: 'xxx'
}
const u2: IUser<{name: string, id: number}> = {
name: '李四',
firend: {
name: 'xxx',
id: 0
}
}
泛型在class中使用
class User<T = string> {
name: string
friend: T
constructor(name:string, friend: T) {
this.name = name
this.friend = friend
}
}
为什么yarn安装的包不能运行
环境变量没配置。 不管是yarn还是npm全局安装的包都有一个存放的文件夹,我们需要把此文件夹配置为环境变量。
怎么查看npm/yarn全安装的包的位置
- npm -g bin
- yarn global bin
然后将路径配置为环境变量