TypeScript快速入门

130 阅读9分钟

TypeScript快速入门

对于前端开发者,TypeScript的重要性就不必多言了。如果你经常写一些类库,ts绝对会让你库更加规范。如果你是一个新技术追求者,那你会去学习vue3.2、react18,那势必会与TypeScript结合起来开发。

大纲

  • TypeScript的介绍

    • 与JavaScript的关系
    • 出现原因
    • 使用场景
  • 快速搭建TypeScript的环境

  • TypeScript的基本类型

    • JavaScript的基本类型

    • 枚举

    • any

      • 类型断言
    • void

    • never

  • 数组的约束

  • 函数的约束

    • 可选参数、默认参数、剩余参数
  • 使用interface约束对象

    • 可选属性
    • 扩展属性
    • 只读属性
  • 使用type定义类型

    • 定义对象类型
    • 定义函数类型
  • interface与type

  • TS中的装饰器

  • TS中的class

    • 创建一个类
    • 实现集成
    • 属性修饰符
  • 在vue中使用ts

ts的介绍

1995网景公司的程序员布莱登艾奇,花了10天就开发了JavaScript。对这门语言的定位为网页脚本语言,用来做表单校验。说白了,就是应付领导随意开发的一个产品,用完就扔掉。结果随着时间的推移,JavaScript这门语言发展得越来越好。而且做的应用越来越大,所以你会发现JavaScript出现了疲软,不太适合开发大型项目,具体原因如下:

  • js中这个变量时没有类型,只有值有类型。

    • 太灵活,很多bug都是在运行的时候才出来的。
  • 复杂的继承实现

微软公司为了开发大型应用,开发了新的一门语言名字叫做TypeScript,从名字来看其重要的功能就是类型约束类型推导,其更加严谨,让开发者在写代码的过程中就发现错误(很多时候JavaScript需要运行之后才能发现错误)。特别适合开发大型应用

  • TypeScript是JavaScript的超集(也就是包含关系)

    • TypeScript的文件后缀名,.ts
    • 最后TypeScript要编译成JavaScript
  • 更好的代码提示(vscode)

    • 在编写代码过程中什么都能点出来
  • 主要的功能:类型推断与类型约束

学习环境安装

本次是为了学习快速搭建一个环境。你需要注意:在实际开发中脚手架会帮你搭建好。其主要功能如下:

  • 实时编译ts文件
  • 实时运行编译之后的js文件

学习ts环境搭建步骤如下:

  • 全局安装typescript

    • yarn global add typescript
    • npm i typescript -g
  • 安装成功之后,你会发现你拥有了一个命令tsc(TypeScript compiler)。然后可以运行一下命令查看版本

    • tsc --version
  • 然后再新创建一个文件夹,命名为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
    • 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 | numberlet 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[]) => numberconst 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}出去吃大餐`)
    }
}

ts中泛型【重要】

泛型让我们的函数,class,type更加灵活,重用性更高。学习ts,能看懂泛型是必备的技能。

引入例子

下面有两个函数。第一个是两个数字相加返回数字,第二个是两个字符串相加返回字符串。

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
    }
}