TypeScript(五)枚举、泛型、模块化、类型声明的查找

本文整理来自深入Vue3+TypeScript技术栈-coderwhy大神新课,只作为个人笔记记录使用,请大家多支持王红元老师。

枚举类型

枚举类型是为数不多的TypeScript特有的特性之一。枚举其实就是将一组可能出现的值,一个个列举出来,定义在一个类型中,这个类型就是枚举类型。枚举允许开发者定义一组命名常量,常量可以是数字、字符串类型。

// 前面我们是这样定义方向的
// type Direction = "left" | "Right" | "Top" | "Bottom"

// 使用枚举,一般大写
enum Direction {
  LEFT,
  RIGHT,
  TOP,
  BOTTOM
}

function turnDirection(direction: Direction) {
  switch (direction) {
    case Direction.LEFT:
      console.log("改变角色的方向向左")
      break;
    case Direction.RIGHT:
      console.log("改变角色的方向向右")
      break;
    case Direction.TOP:
      console.log("改变角色的方向向上")
      break;
    case Direction.BOTTOM:
      console.log("改变角色的方向向下")
      break;
    default:
      const foo: never = direction;
      break;
  }
}

turnDirection(Direction.LEFT)
turnDirection(Direction.RIGHT)
turnDirection(Direction.TOP)
turnDirection(Direction.BOTTOM)

枚举类型的值

枚举类型默认是有值的,比如上面的枚举,默认值是这样的:

当然,我们也可以给枚举其他值,这个时候会从100进行递增:

我们也可以给他们赋值其他的类型:

认识泛型

软件工程的主要目的是构建不仅仅明确和一致的API,还要让你的代码具有很强的可重用性,比如我们可以通过函数来封装一些API,通过传入不同的函数参数,让函数帮助我们完成不同的操作,但是对于参数的类型是否也可以参数化呢?

什么是类型的参数化?
我们来提一个需求:封装一个函数,传入一个参数,并且返回这个参数。

如果我们是TypeScript的思维方式,要考虑这个参数和返回值的类型需要一致:

上面的代码虽然实现了,但是不适用于其他类型,比如string、boolean、Person等类型:

泛型实现类型参数化

虽然any是可以的,但是定义为any的时候,我们其实已经丢失了类型信息。比如我们传入的是一个number,那么我们希望返回的可不是any类型,而是number类型,所以,我们需要在函数中可以捕获到参数的类型是number,并且同时使用它来作为返回值的类型。

我们需要在这里使用一种特性的变量 - 类型变量(type variable),它作用于类型,而不是值。

这里我们可以使用两种方式来调用它:

  • 方式一:通过 <类型> 的方式将类型传递给函数。
  • 方式二:通过类型推导,自动推导出我们传入变量的类型。在这里会推导出它们是字面量类型的,因为字面量类型对于我们的函数也是适用的。

传入多个类型参数

当然我们也可以传入多个类型:

平时在开发中我们可能会看到一些常用的名称:

T:Type的缩写,类型
K、V:key和value的缩写,键值对
E:Element的缩写,元素
O:Object的缩写,对象

泛型接口

在定义接口的时候我们也可以使用泛型:

当然我们也可以给泛型添加默认类型:

泛型类

我们也可以编写一个泛型类:

// 回忆数组的写法
const names1: string[] = ["abc", "cba", "nba"]
const names2: Array<string> = ["abc", "cba", "nba"] // 不推荐(react jsx <>)

泛型约束

有时候我们希望传入的类型有某些共性,但是这些共性可能不是在同一种类型中,比如string和array都是有length的,或者某些对象也是会有length属性的,那么只要是拥有length的属性都可以作为我们的参数类型,那么应该如何操作呢?

模块化开发

TypeScript支持两种方式来控制我们的作用域:

  • 模块化:每个文件可以是一个独立的模块,支持ES Module,也支持CommonJS;
  • 命名空间:通过namespace来声明一个命名空间;

模块化如下:

使用如下:

import { add, sub } from "./utils/math";
console.log(add(20, 30));  // 50
console.log(sub(20, 30));  // -10

命名空间namespace

命名空间在TypeScript早期时,称之为内部模块,主要目的是将一个模块内部再进行作用域的划分,防止一些命名冲突的问题。

类型声明的查找

之前我们所有的typescript中的类型,几乎都是我们自己编写的,但是我们也有用到一些其他的类型:

大家是否会奇怪,我们的HTMLImageElement类型来自哪里呢?甚至是document为什么可以有getElementById的方法呢?其实这里就涉及到typescript对类型的管理和查找规则了。

我们这里先给大家介绍另外的一种typescript文件:.d.ts文件(.declare.js的简写)。
我们之前编写的typescript文件都是 .ts 文件,这些文件最终会输出 .js 文件,也是我们通常编写代码的地方,还有另外一种 .d.ts 文件,它是用来做类型的声明(declare)。 它仅仅用来做类型检测,告知typescript我们有哪些类型。

那么typescript会在哪里查找我们的类型声明呢?

  • 内置类型声明;
  • 外部类型声明;比如第三方库axios,就是自带类型声明的,也就是自带.d.ts文件。但是lodash没有自带.d.ts文件,所以不能直接使用。
  • 自定义类型声明;

内置类型声明

内置类型声明是typescript自带的,它帮助我们内置了JavaScript运行时的一些标准化API的声明文件,包括比如Math、Date等内置类型,也包括DOM API,比如Window、Document等。内置类型声明通常在我们安装typescript的环境中会带有的:https://github.com/microsoft/TypeScript/tree/main/lib

外部类型声明

外部类型声明通常是我们使用一些库(比如第三方库)时,需要的一些类型声明。这些库通常有两种类型声明方式:

自定义类型声明

什么情况下需要我们来自定义类型声明呢?

  • 情况一:我们使用的第三方库是一个纯的JavaScript库,没有对应的声明文件。
  • 情况二:我们给自己的代码中声明一些类型,方便在其他地方直接进行使用。

① 声明模块

虽然公有库DefinitelyTyped里面已经给lodash声明类型了,我们假设它没有给lodash声明类型,我们就需要自己自定义类型声明。我们新建coderwhy.d.ts文件,代码如下:

// 给lodash声明模块
declare module 'lodash' {
  // 声明模块里的方法
  export function join(arr: any[]): void
}

声明模块和模块里的方法以后,我们通过如下方式使用lodash就不会报错了。

import lodash from 'lodash'

console.log(lodash.join(["abc", "cba"]))

② 声明变量、函数、类

当我们在index.html中的script标签中写如下代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>

  <script>
    // 变量
    let whyName = "coderwhy"
    let whyAge = 18
    let whyHeight = 1.88

    // 函数
    function whyFoo() {
      console.log("whyFoo")
    }

    // ES5定义类就是这样定义的
    function Person(name, age) {
      this.name = name
      this.age = age
    }
  </script>

</body>
</html>

然后在main.ts中使用:

console.log(whyName)
console.log(whyAge)
console.log(whyHeight)

whyFoo()

const p = new Person("why", 18)
console.log(p)

按照js代码执行的顺序,上面代码应该不会报错,但是在TypeScript中,上面代码不能通过TypeScript的类型约束,因为TypeScript不知道上面那些变量、函数、类是什么,所以我们需要声明一下,在coderwhy.d.ts文件中新增如下代码:

// 声明变量
declare let whyName: string
declare let whyAge: number
declare let whyHeight: number

// 声明函数
declare function whyFoo(): void

// 声明类
declare class Person {
  name: string
  age: number
  constructor(name: string, age: number)
}

这样代码就不会报错了。

③ 声明文件

在某些情况下,我们也可以声明文件。比如在开发vue的过程中,默认是不识别我们的.vue文件的,那么我们就需要对其进行文件的声明。比如在开发中我们使用了 jpg 这类图片文件,默认typescript也是不支持的,也需要对其进行声明。

// 声明文件
declare module '*.jpg'
declare module '*.jpeg'
declare module '*.png'
declare module '*.svg'
declare module '*.gif'

声明之后通过import nhltImage from './img/nhlt.jpg',引入图片就不会报错了。

④ 声明命名空间

比如我们在index.html中通过script标签引入了jQuery,这时候我们就可以在index.html中通过$.ajax使用了,但是无法在main.ts中使用,这时候我们可以声明命名空间,如下:

// 声明命名空间
declare namespace $ {
  export function ajax(settings: any): any
}

声明之后在main.ts中就可以使用了:

tsconfig.json文件

tsconfig.json是用于配置TypeScript编译时的配置选项:https://www.typescriptlang.org/tsconfig

我们这里讲解几个比较常见的:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容