自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

TypeScript 深水區(qū):三種類(lèi)型來(lái)源和三種模塊語(yǔ)法

開(kāi)發(fā) 前端
深入掌握 TypeScript 的話(huà),除了學(xué)習(xí)類(lèi)型定義以及類(lèi)型編程,這三種類(lèi)型聲明的來(lái)源(lib、@types、用戶(hù)目錄),以及三種模塊聲明的方式(namespace、module、es module),還有全局類(lèi)型的聲明(global、reference),也都是要掌握的。

TypeScript 給 JavaScript 添加了一套類(lèi)型語(yǔ)法,我們聲明變量的時(shí)候可以給變量加上類(lèi)型信息,這樣編譯階段就可以檢查出變量使用的對(duì)不對(duì),也就是類(lèi)型檢查。

給變量添加類(lèi)型,很自然可以想到時(shí)在聲明的時(shí)候指定:

比如對(duì)象:

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

const guang: Person = {
name: 'guang'
}

比如函數(shù):

function add(num1: number, num2: number): number {
return num1 + num2;
}

這樣當(dāng)使用它們的時(shí)候,比如變量賦值、函數(shù)調(diào)用,就可以通過(guò)類(lèi)型信息檢查出使用的對(duì)不對(duì):

圖片

圖片

TypeScript 這樣設(shè)計(jì)類(lèi)型語(yǔ)法沒(méi)啥問(wèn)題,但是只是這樣還不夠。

我們自己寫(xiě)的代碼可以這樣聲明類(lèi)型,但不是我們寫(xiě)的呢?

比如 JS 引擎提供的 Number、String、Date、RegExp,瀏覽器環(huán)境的 HTMLElement、Event 等 api。

這些 api 是執(zhí)行引擎內(nèi)置的實(shí)現(xiàn),但我們代碼里會(huì)用到它們,也同樣需要檢查使用的對(duì)不對(duì),也就是類(lèi)型檢查。怎么給這些 api 加上類(lèi)型呢?

TypeScript 類(lèi)型聲明的三種來(lái)源

TypeScript 設(shè)計(jì)了 declare 的語(yǔ)法,可以單獨(dú)聲明變量的類(lèi)型:

比如對(duì)象:

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

declare const guang: Person;

比如函數(shù):

declare function add(num1: number, num2: number): number;

這樣單獨(dú)聲明了類(lèi)型,使用這些 api 的時(shí)候也就能做類(lèi)型檢查。

像 JS 引擎那些 api,還有瀏覽器提供的 api,這些基本是必用的,而且都有標(biāo)準(zhǔn)的。所以 TypeScript 給內(nèi)置了它們的類(lèi)型聲明。

TypeScript 包下有個(gè) lib 目錄,里面有一堆 lib.xx.d.ts 的類(lèi)型聲明文件,這就是 TS 內(nèi)置的一些類(lèi)型聲明。

圖片

圖片

因?yàn)檫@些只是聲明類(lèi)型,而沒(méi)有具體的 JS 實(shí)現(xiàn),TS 就給單獨(dú)設(shè)計(jì)了一種文件類(lèi)型,也就是 d.ts, d 是 declare 的意思。

比如 lib.dom.d.ts 里的類(lèi)型聲明:

圖片

因?yàn)槭?ts 內(nèi)置的,所以配置一下就可以用了:

圖片

tsconfig.json 里配置下 compilerOptions.lib,就可以引入對(duì)應(yīng)的 d.ts 的類(lèi)型聲明文件。

有的同學(xué)可能會(huì)說(shuō),可是內(nèi)置的類(lèi)型聲明也不多呀,只有 dom 和 es 的。

確實(shí),因?yàn)?JS 的 api 還有瀏覽器的 api 都是有標(biāo)準(zhǔn)的,那自然可以按照標(biāo)準(zhǔn)來(lái)定義類(lèi)型。其余的環(huán)境的 api 可能沒(méi)有標(biāo)準(zhǔn),經(jīng)常變,那自然就沒(méi)法內(nèi)置了,比如 node。所以 lib 里只有 dom 和 es 的類(lèi)型聲明。

那 node 環(huán)境,還有其他環(huán)境里的內(nèi)置 api 怎么配置類(lèi)型聲明呢?

node 等環(huán)境的 api 因?yàn)闆](méi)有標(biāo)準(zhǔn)而沒(méi)有被 TS 內(nèi)置,但 TS 同樣也支持了這些環(huán)境的類(lèi)型聲明的配置。

方式是通過(guò) @types/xxx 的包:

圖片

TS 會(huì)先加載內(nèi)置的 lib 的類(lèi)型聲明,然后再去查找 @types 包下的類(lèi)型聲明。

這樣,其他環(huán)境的類(lèi)型聲明就可以通過(guò)這種方式來(lái)擴(kuò)展。

圖片

@types 包是在 DefinitelyTyped 這個(gè)項(xiàng)目下統(tǒng)一管理的,想創(chuàng)建一個(gè) @types 包的話(huà)要去看一下他們的文檔。

圖片

一般來(lái)說(shuō),很快就可以發(fā)到 npm 的:

圖片

我們知道,TS 內(nèi)置的那些 lib 是可以配置的,擴(kuò)展的這些 @types/xx 的包自然也可以配置:

圖片

可以指定加載 @types 目錄下的哪些包,還可以修改查找 @types 包的目錄(默認(rèn)是 node_modules/@types):

圖片

除了給 node 等環(huán)境的 api 加上類(lèi)型聲明外,@types 包還有一種用途,就是給一些 JS 的包加上類(lèi)型聲明:

如果代碼本身是用 ts 寫(xiě)的,那編譯的時(shí)候就可以開(kāi)啟 compilerOptions.declaration,來(lái)生成 d.ts 文件:

圖片

然后在 package.json 里配置 types 來(lái)指定 dts 的位置:

圖片

這樣就不需要單獨(dú)的 @types 包了。

但如果代碼不是用 ts 寫(xiě)的,那可能既需要單獨(dú)寫(xiě)一個(gè) @types/xxx 的包來(lái)聲明 ts 類(lèi)型,然后在 tsconfig.json 里配置下,加載進(jìn)來(lái)。

比如常用的 vue3 就不需要 @types/vue 包,因?yàn)楸旧硎怯?ts 寫(xiě)的,npm 包里也包含了 dts 文件。

但是 react 不是 ts 寫(xiě)的,是用的 facebook 自己的 flow,自然就需要 @types/react 的包來(lái)加上 ts 類(lèi)型聲明。

至此,ts 內(nèi)置的 dom 和 es 的類(lèi)型聲明,其他環(huán)境還有一些包的類(lèi)型聲明我們都知道怎么加載了。

那自己寫(xiě)的 ts 代碼呢?

這些其實(shí)我們經(jīng)常配置,就是配置下編譯的入口文件,通過(guò) includes 指定一堆,然后通過(guò) excludes 去掉一部分。還可以通過(guò) files 再單獨(dú)包含一些:

圖片

tsc 在編譯的時(shí)候,會(huì)分別加載 lib 的,@types 下的,還有 include 和 files 的文件,進(jìn)行類(lèi)型檢查。

這就是 ts 類(lèi)型聲明的三種來(lái)源。

現(xiàn)在還有一個(gè)問(wèn)題,有的 api 是全局的,有的 api 是某個(gè)模塊的,ts 是怎么聲明全局 api 的類(lèi)型,怎么聲明模塊內(nèi)的 api 的類(lèi)型呢?

全局類(lèi)型聲明 vs 模塊類(lèi)型聲明

我們寫(xiě)的 JS 代碼就是有的 api 是全局的,有的 api 是模塊內(nèi)的,所以 TS 需要支持這個(gè)也很正常。

但 JS 的模塊規(guī)范不是一開(kāi)始就有的,最開(kāi)始是通過(guò)在全局掛一個(gè)對(duì)象,然后這個(gè)對(duì)象上再掛一些 api 的方式,也就是命名空間 namespace。

所以 TS 最早支持的模塊化方案自然也就是 namespace:

namespace Guang {
export interface Person {
name: string;
age?: number;
}

const name = 'guang';
const age = 20;

export const guang: Person = {
name,
age
}
export function add(a: number, b: number):number {
return a + b;
}
}

理解 namespace 的話(huà)可以看一下編譯后的代碼:

圖片

就是全局上放一個(gè)對(duì)象,然后對(duì)象上再掛幾個(gè)暴露出去的屬性。

看了編譯后的代碼,是不是 namespace 瞬間就學(xué)會(huì)了~

后來(lái),出現(xiàn)了 CommonJS 的規(guī)范,那種不能叫 namespace 了,所以 TS 支持了 module,

很容易想到,@types/node 的 api 定義就是一堆的 module:

圖片

這個(gè) module 和 namespace 有什么區(qū)別呢?

其實(shí)真沒(méi)什么區(qū)別,只不過(guò) module 后一般接一個(gè)路徑,而 namespace 后一半是一個(gè)命名空間名字。其他的語(yǔ)法都一樣的。

而且這個(gè)結(jié)論是有依據(jù)的:

圖片

圖片

用 astexplorer.net 看一下 parse 后的 AST,兩者的 AST類(lèi)型都是一樣的。也就是說(shuō)編譯器后續(xù)的處理都一樣,那不是一種東西是什么。

再后來(lái)的故事大家都知道了,JS 有了 es module 規(guī)范,所以現(xiàn)在推薦直接用 import export 的方式來(lái)聲明模塊和導(dǎo)入導(dǎo)出了。

額外多了的,只不過(guò)有一個(gè) import type 的語(yǔ)法,可以單獨(dú)引入類(lèi)型:

import type {xxx} from 'yyy';

所以現(xiàn)在聲明模塊不咋推薦用 namespace 和 module,還是盡量用 es module 吧。

那全局的類(lèi)型聲明呢?

有了 es module 之后,TS 有了一個(gè)單獨(dú)的設(shè)計(jì):

dts 中,如果沒(méi)有 import、export 語(yǔ)法,那所有的類(lèi)型聲明都是全局的,否則是模塊內(nèi)的。

我們?cè)囼?yàn)一下:

include 配置 src 下的 ts 文件,然后再用 files 引入 global.d.ts 文件:

圖片

在 global.d.ts 里聲明一個(gè) func 函數(shù):

圖片

在 src/index.ts 里是有提示的:

圖片

編譯也不報(bào)錯(cuò):

圖片

加上一個(gè) import 語(yǔ)句:

圖片

編譯就報(bào)錯(cuò)了,說(shuō)是找不到 func:

圖片

這說(shuō)明 func 就不再是全局的類(lèi)型了。

這時(shí)候可以手動(dòng) declare global:

圖片

再試一下,編譯就通過(guò)了:

圖片

而且不止是 es module 的模塊里可以用 global 聲明全局類(lèi)型,module 的方式聲明的 CommonJS 模塊也是可以的:

比如 @types/node 里就有不少這種全局類(lèi)型聲明:

圖片

這就是 3 種 typescript 聲明模塊的語(yǔ)法,以及聲明全局類(lèi)型的方式。

那么如果就是需要引入模塊,但是也需要全局聲明類(lèi)型,有什么更好的方式呢?

有,通過(guò)編譯器指令 reference。這樣既可以引入類(lèi)型聲明,又不會(huì)導(dǎo)致所有類(lèi)型聲明都變?yōu)槟K內(nèi)的:

圖片

可以看到很多 dts 都這樣引入別的 dts 的,就是為了保證引入的類(lèi)型聲明依然是全局的:

圖片

總結(jié)

TypeScript 給 JavaScript 添加了類(lèi)型信息,在編譯時(shí)做類(lèi)型檢查。

除了在變量聲明時(shí)定義類(lèi)型外,TS 也支持通過(guò) declare 單獨(dú)聲明類(lèi)型。只存放類(lèi)型聲明的文件后綴是 d.ts。

TypeScript 有三種存放類(lèi)型聲明的地方:

  • lib:內(nèi)置的類(lèi)型聲明,包含 dom 和 es 的,因?yàn)檫@倆都是有標(biāo)準(zhǔn)的。
  • @types/xx:其他環(huán)境的 api 類(lèi)型聲明,比如 node,還有 npm 包的類(lèi)型聲明。
  • 開(kāi)發(fā)者寫(xiě)的代碼:通過(guò) include + exclude 還有 files 指定。

其中,npm 包也可以同時(shí)存放 ts 類(lèi)型,通過(guò) packages.json 的 types 字段指定路徑即可。

常見(jiàn)的是 vue 的類(lèi)型是存放在 npm 包下的,而 react 的類(lèi)型是在 @types/react 里的。因?yàn)樵创a一個(gè)是 ts 寫(xiě)的,一個(gè)不是。

巧合的是,TS 聲明模塊的方式也是三種:

  • namespace:最早的實(shí)現(xiàn)模塊的方式,編譯為聲明對(duì)象和設(shè)置對(duì)象的屬性的 JS 代碼,很容易理解。
  • module:和 namespace 的 AST 沒(méi)有任何區(qū)別,只不過(guò)一般用來(lái)聲明 CommonJS 的模塊,在 @types/node 下有很多。
  • es module:es 標(biāo)準(zhǔn)的模塊語(yǔ)法,ts 額外擴(kuò)展了 import type。

dts 的類(lèi)型聲明默認(rèn)是全局的,除非有 es module 的 import、export 的聲明,這時(shí)候就要手動(dòng) declare global 了。為了避免這種情況,可以用 reference 的編譯器指令。

深入掌握 TypeScript 的話(huà),除了學(xué)習(xí)類(lèi)型定義以及類(lèi)型編程,這三種類(lèi)型聲明的來(lái)源(lib、@types、用戶(hù)目錄),以及三種模塊聲明的方式(namespace、module、es module),還有全局類(lèi)型的聲明(global、reference),也都是要掌握的。

責(zé)任編輯:武曉燕 來(lái)源: 神光的編程秘籍
相關(guān)推薦

2011-01-18 15:35:59

jQueryJavaScriptweb

2009-06-29 18:21:29

Hibernate

2010-05-11 14:08:50

MySQL數(shù)字類(lèi)型

2018-12-13 20:14:18

物聯(lián)網(wǎng)平臺(tái)物聯(lián)網(wǎng)IOT

2009-11-24 18:15:37

博科資訊管理軟件

2009-08-03 17:41:20

ASP.NET Cac

2009-11-13 09:39:48

2010-04-02 13:15:01

Oracle跟蹤

2010-04-12 16:35:15

Oracle數(shù)據(jù)庫(kù)

2013-09-02 15:35:00

2010-11-01 11:57:18

DB2客戶(hù)端

2010-09-24 19:18:22

SQL索引

2020-05-08 07:26:16

物聯(lián)網(wǎng)平臺(tái)物聯(lián)網(wǎng)IOT

2023-10-13 00:00:00

Redis模塊空間對(duì)象

2010-09-09 10:43:56

VPN服務(wù)

2010-06-08 09:39:40

UML圖

2010-10-28 10:27:35

oracle賦權(quán)

2010-09-25 14:38:29

SQL分頁(yè)

2009-07-16 16:23:59

Swing線(xiàn)程

2009-08-06 15:26:18

C#異常類(lèi)型
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)