likes
comments
collection
share

TypeScript

作者站长头像
站长
· 阅读数 15

TypeScript

介绍

TypeScript 是 JavaScript 类型的超集,它可以编译成纯 JavaScript。TypeScript 可以在任何浏览器、任何计算机和任何操作系统上运行,并且是开源的。

  • 由微软开发的一个开源项目,对 javascript 的一个扩展
  • 弥补了许多 javascript 的不足(比如由于 javascript 弱类型原因,许多错误只能在运行时知道
  • 对类型进行了强化
  • 扩展了许多面向对象的用法
  • 当下很火的一个技术(GitHub84.8k 的 start,11k 的 fork 足见其火热程度)
  • 本质上还是 Javascript

优点

  • 多端可用,完美契合前端技术栈(web 端 Vue 项目、微信小程序、uniapp 均可使用)
  • 兼容性高
    • 支持 ES 新特性,包括还在提案中的特性
    • 比较新的特性编译成 ES5 代码
  • 友好的代码提示
  • 具有文档特性,许多方法看类型定义就知道如何使用
  • 增强了编辑器的代码提示功能
  • 强类型约束,编译前尽可能减少错误和 bug
  • 编辑阶段和编译阶段会报错,但不影响编译结果
  • 更多面向对象编程的特性
  • 易于重构
  • 易于团队合作
  • 社区活跃,大部分项目都有 TypeScript 的声明文件
  • 越来越多的库用 TypeScript 编写(React、Vue3 等),是未来的趋势

配置 Typescript 环境

安装相关工具库

npm i typescript -g    ts语法的解析库
npm i ts-node@8 -g     让node环境直接编译ts代码
npm i nodemon -g     检测代码变化,自动重启

新建 typescript 项目,并在其中生成环境相关配置文件,目录结构如下

-ts-demo
---src
---nodemon.json
---package.json
  • 通过 npm init 自动生成 package.json,后续在其中添加启动命令
  • 手动新建 nodemon.json 文件,作为自动化检测工具的配置

nodemon.json 中的配置

{
  "verbose": false,
  "debug": false,
  "exec": "ts-node  ./src/index.ts", //让ts-node执行ts文件
  "ignore": [
    "mochawesome-report",
    "node_modules",
    "./test",
    "**/*.d.ts",
    "*.test.ts",
    "*.spec.ts",
    "fixtures/*",
    "test/**/*",
    "docs/*"
  ],
  "events": {
    "restart": ""
  },
  "watch": ["./src"], //检测src目录中的代码变化
  "ext": "ts tsx",
  "inspect": true
}

package.json 中的配置

{
  "name": "typescript",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "nodemon --config ./nodemon.json" //启动代码编译及监听的命令
  },
  "author": "",
  "license": "ISC"
}

检测是否可用 运行

  • 使用 npm run start 运行查看结果

使用

基础数据类型

布尔(boolean)

允许值只有两种:true/false

  • let isClose:boolean=true

数值类型(number)

  • let num: number = 6

字符串类型(string)

  • let myName:string='jepson'

null、undefined

在TypeScript中null、undefined是所有类型的子类型,所有其他类型都能够被赋值为null和undefined

  • let u:undefined=undefined
  • let n:null=null

symbol(永远唯一的值)

空值(void)

void表示空值,只能赋值为null/undefined(所有类型的子类型),如果用于变量中这或许没什么用,但是用于函数中表示不返回任何值

let myFirstName:void=undefined
let myLastName:void=null
function fn2():void {
  return undefined//不可以
}
function fn3():void {
  console.log('this is fn3')
}

any

any用于不确定其类型时所指定的类型,表示任何类型,如:第三方库、后端返回数据不确定(尽量要求后端返回的数据确定)、动态内容等:

let anyValue:any=true
anyValue=20
anyValue='anyValue'
anyValue=Symbol()
anyValue=null
anyValue=undefined
anyValue=new Error('anyvalue')
anyValue=['anyValue']

应尽量避免在项目中使用any

函数

函数是Javascript的一等公民

//函数声明式
function sum(a:number,b:number):number{
    return a+b
}

//函数表达式
const reduce= (a:number,b:number):number=>a-b

console.log(sum(1,1))//2
console.log(reduce(1,1))//0

可选参数与默认参数

  • 可选参数使用?:定义,表示这个参数是非必须的:
function haveLaunch(food:string,cuisine:string,fruits?:string){
    console.log(`今天中午吃:${food}${cuisine}${fruits}`);
}
haveLaunch('米饭','豆芽',undefined)
  • 默认参数
上面的修改fruits:string='没有水果'
  • 默认参数与可选参数不同
    • 默认参数没有位置限制,第一个参数也能设置默认参数,后面可以带必选参数
    • 默认参数如果传入undefinednull不会取代默认值,而可选参数会直接复制;利用这一特性可以为每个参数设置默认值,如果是默认值可以传入undefinednull

=>与箭头函数

在TypeScript类型定义中=>用于表示函数,左边是输入类型,右边是输出类型; 而在ES6中,=>代表的是一个箭头函数,左边参数,右边代码块

let add: (num1: number, num2: number) => number   //类型表达
add = (num1, num2) => num1 + num2   //根据上面的类型表达,定义了一个函数

rest参数(剩余参数)

function sum(a:number,b:number,...numArr:number[]):number{
    let num = a + b
    numArr.forEach((n:number)=>{
        num+=n
    })
    return num
}
console.log(sum(1,2,3,4,5)); //15

函数重载 (Overload)

//更加准确的表达加这三句
function reverse(arg:string):string  //当传入字符串的时候,返回字符串
function reverse(arg:number):number  //当传入数字的时候,返回数字
function reverse(arg:Array<any>):Array<any>

function reverse(arg:string|number|Array<any>):string|number|Array<any>{
    if(typeof arg === 'string'){
        return arg.split('').reverse().join('')
    }else if(typeof arg === 'number'){
        return arg.toString().split('').reverse().join('')
    }else{
        return arg.reverse()
    }
}

interface(接口)

在面向对象语言中,接口(Interfaces)是一个很重要的概念,它是对行为的抽象,而具体如何行动需要由类(classes)去实现(implement)。

  • TypeScript中的interface(接口)非常灵活,常用与对类的部分行为抽象、规定对象的形状(哪些属性/方法)
interface User {
  username:string;   //注意!!! 此处为分号
  password:string;
}

//下面这个写法会报错
const u:User={
  username: 'admin',
  password: 12345678,//报错:Type 'number' is not assignable to type 'string'.
  level:1//报错:Type '{ username: string; password: string; level: number; }' is not assignable to type 'User'.
}

//这个写法不会报错
const u2:User={
  username: 'admin',
  password: '12345678'
}

可选属性 只读属性

interface User {
  readonly username:string; //readonly只读 不能在修改
  level?:number //?可选
}

函数属性

let user1: User = {
    name: "w(゚Д゚)w",
    age: 90,
    hobby: "吃饭",
    sayAge: function (n) {
        this.age = n
    }
}

动态可索引属性实现伪数组

interface Sum {
    [key: string]: number | string
}
let add: Sum = {
    a: 100,
    d: "400"
}

接口定义函数类型

interface Sum{
  (a:number,b:number):number
}
let sum:Sum = (a,b)=>{
  return a+b
}
console.log(sum(2,'3')); //报错,此处的'3'不符合类型要求
console.log(sum(2,3));

接口间的继承

interface Animal {  //基本动物行为
  eat(food:string):void;
  run():void,
  sleep():void
}

interface Person extends Animal { //新增人类属性
  name:string,
  gender:'GIRL'|'BOY'
}

数组

  • 类型+数组字面量定义let numArr:number[]
  • 数组泛型定义数组let numArr:Array<string|number>

接口定义数组

interface NumberArray{
  [index:number]:number|string
}
let numArr:NumberArray
numArr=[1,2,3]

元组

对于指定元素数量和类型的数组,推荐使用元组定义

let position:[number,number]  //定义一个经纬度位置信息
position = [106.23,66.8]

枚举

  • TypeScript的枚举(enum)非常灵活,默认自动赋值,比如定义一周的天数
enum Week{
  Monday,
  Tuesday,
  Wednesday,
  Thursday,
  Friday,
  Saturday,
  Sunday
}

console.log(Week[1]);
  • 手动赋值
enum Week{
  Monday=1,
  Tuesday,
  Wednesday,
  Thursday,
  Friday,
  Saturday,
  Sunday
}

console.log(Week[1]);

枚举的应用场景

// 枚举使用场景:后端下发的订单状态是以编码的方式来代表不同的状态
// 例如:10:待支付,15:待支付尾款 20:待发货,30:待收货,40:已完成,50:已关闭,60退款中,70已退款
// 我们通过异步请求拿到了后端的数据包如下
let glist = [
  {name:'商品1',price:9.9,status:10},
  {name:'商品2',price:19.9,status:40},
  {name:'商品3',price:99,status:15},
]

// 思考:在没有枚举的情况下如何将商品的订单状态汉字准确显示在前端页面?

// 使用枚举处理:
enum OrderStatus{
  '待支付'=10,
  '待支付尾款'=15,
  '待发货'=20,
  '待收货'=30,
  '已完成'=40
}

// 如果要展示第一条商品的订单状态
console.log(OrderStatus[glist[0].status]);

泛型

  • 那么我们需要变量,这个变量代表了传入的类型,然后再返回这个变量,它是一种特殊的变量,只用于表示类型而不是值。
function returnItem<T>(para: T): T {
    return para
}

多个类型参数

// 输入一个数组,输出该数组的逆序,并确保类型一致
function swap<T, U>(tuple: [T, U]): [U, T] {
    return [tuple[1], tuple[0]];
}

swap([7, 'seven']); // ['seven', 7]

泛型接口

interface ReturnItemFn<T> {
    (para: T): T
}

const returnItem: ReturnItemFn<number> = para => para

泛型类

  • 不使用泛类型
class Stack {
    private arr: number[] = []

    public push(item: number) {
        this.arr.push(item)
    }

    public pop() {
        this.arr.pop()
    }
}
  • 使用泛类型
class Stack<T> {
    private arr: T[] = []

    public push(item: T) {
        this.arr.push(item)
    }

    public pop() {
        this.arr.pop()
    }
}

泛型约束

type Params = number | string
class Stack<T extends Params> {
    private arr: T[] = []

    public push(item: T) {
        this.arr.push(item)
    }

    public pop() {
        this.arr.pop()
    }
}

泛型约束与索引类型

function getValue<T extends object, U extends keyof T>(obj: T, key: U) {
  return obj[key] // ok
}

类型断言

interface Person {
  name: string;
  age: number;
}

const person = {} as Person;