Typescript 書寫聲明文件(多是最全的)

前言

對於爲第三方模塊/庫寫聲明文件以前,咱們須要知道第三方模塊/庫,是否須要聲明文件,或者是否已有聲明文件。html

  • 若第三方模塊/庫,是ts編寫且無聲明文件, 可使用--declaration配置選項來生成; 能夠在命令行中添加 --declaration(簡寫 -d),或者在 tsconfig.json 中添加 declaration:true 選項
  • 若第三方模塊/庫,是js編寫,分爲兩種狀況:
1. 與該 npm 包綁定在一塊兒,能夠經過查找該庫的`package.json`中的`types`屬性
2. 發佈到 @types 裏,能夠在官方提供的第三方聲明文件庫(http://microsoft.github.io/TypeSearch/)中查找
複製代碼

如若上面的狀況都不符合, 則須要咱們本身手寫聲明文件vue

前置知識

在書寫聲明文件以前,咱們須要瞭解Typescript 相關知識, 能夠自行查閱官方文檔,或閱讀我前一篇TypeScript 總結篇, 固然有須要寫聲明文件的須要,確定是對Typescript有了解,可能已有相關實踐,在此只是友情提示。 除此以外還須要對TS中的模塊化有所瞭解,以下git

模塊化

模塊(module)

主要是解決加載依賴關係的,側重代碼的複用。跟文件綁定在一塊兒,一個文件就是一個module,模塊寫法和ES6同樣。github

命名空間(namespace)

同Java的包、.Net的命名空間同樣,TypeScript的命名空間將代碼包裹起來,經過export關鍵字對外暴露須要在外部訪問的對象。 主要用於組織代碼,解決命名衝突,會在全局生成一個對象,定義在namespace內部的都要經過這個對象的屬性訪問。ajax

隨着 ES6 的普遍應用,現已經不建議使用 ts 中的 namespace,而推薦使用 ES6 模塊化方案,但在聲明文件中,declare namespace 仍是比較經常使用的。typescript

namespace聲明能夠用來添加新類型,值和命名空間,只要不出現衝突。
與class/namespace等類型合併npm

/// 三斜線指令

///<reference types=「UMDModuleName/globalName」 /> ts 早期模塊化的標籤, 用來導入依賴, ES6普遍使用後, 在編寫TS文件中不推薦使用, 除了如下的場景使用///, 其餘場景使用 import 代替
在聲明文件中, 依賴全局庫或被全局庫依賴, 具體:json

  1. 庫依賴全局庫, 由於全局庫不能使用import導入
  2. 全局庫依賴於某個 UMD 模塊,由於全局庫中不能出現import/export, 出現則爲npm/UMD

注意: 三斜線指令必須放在文件的最頂端,三斜線指令的前面只容許出現單行或多行註釋。數組

模塊或一個 UMD 庫依賴於一個 UMD 庫,使用 import * as 語句引入模塊bash

書寫聲明文件

第三方庫使用場景:

  • 全局變量:經過 <script>標籤引入第三方庫,注入全局變量
  • npm 包:經過 import foo from 'foo' 導入,符合ES6 模塊規範
  • UMD 庫:既能夠經過<script>標籤引入,又能夠經過 import 導入
  • 模塊插件:經過 import 導入後,能夠改變另外一個模塊的結構
  • 直接擴展全局變量:經過 <script> 標籤引入後,改變一個全局變量的結構。好比爲String.prototype 新增了一個方法
  • 經過導入擴展全局變量:經過import導入後,能夠改變一個全局變量的結構

類庫分爲三類:全局類庫、模塊類庫、UMD類庫

全局變量

經過<script>標籤引入第三方庫, 注入全局變量 全局變量的聲明文件主要有如下幾種語法:

  • declare var 聲明全局變量
  • declare function 聲明全局方法
  • declare class 聲明全局類
  • declare enum 聲明全局枚舉類型
  • declare namespace 聲明全局對象(含有子屬性)
  • interface 和 type 聲明全局類型

主要看下 declare namespace

// src/jQuery.d.ts

declare namespace jQuery {
    const version: number;
    class Event {
        blur(eventType: EventType): void
    }
    enum EventType {
        CustomClick
    }
    interface AjaxSettings {
        method?: 'GET' | 'POST'
        data?: any;
    }
    function ajax(url: string, settings?: AjaxSettings): void;
}
複製代碼

declare namespace聲明全局命名空間,去掉declare namespace, 即從中提出代碼,再在前面加上declare便是聲明各全局變量

// 聲明全局函數,其餘同理
declare function ajax(url: string, settings?: any): void;
複製代碼

npm 包

在npm包中, 經過import foo from 'foo'導入npm包。npm 包的聲明文件主要有如下幾種語法:

export // 導出變量
export namespace // 導出(含有子屬性的)對象
export default // ES6 默認導出
export = // commonjs 導出模塊
複製代碼

export

在 npm 包的聲明文件中,使用 declare 再也不會聲明一個全局變量,而只會在當前文件中聲明一個局部變量。只有在聲明文件中使用 export 導出,而後在使用方 import 導入後,纔會應用到這些類型聲明。

與非聲明文件寫法相似, 使用import導入, ES6模塊語法

混用 declare 和 export

使用 declare 先聲明多個變量,最後再用 export 一次性導出
注: interface 前是不須要 declare 的

export default

注意,只有 function、class 和 interface 能夠直接默認導出,其餘的變量須要先定義出來,再默認導出

export namespace

用來導出一個擁有子屬性的對象

export =

在 commonjs 規範中, 使用exports/module.exports導出模塊, 針對這類模塊的聲明文件,須要使用export =導出

declare module "a" {
    export let a: number
    export function b(): number
    export namespace c{
        let cd: string
    }
}


import * as A from 'a'
A.a //
A.b()
A.c.cd // 
複製代碼
// 函數
declare module 'app' {
  function fn(some:number):number 
  export = fn
}

const app = reqiure('app')
app()// 調用fn
複製代碼
// 變量/常量
declare module 'let' {
  let oo:number = 2
  export = oo
}

const o = reqiure('let')
o // 使用
複製代碼

UMD 庫

既能夠經過 <script> 標籤引入,又能夠經過 import 導入的庫,稱爲 UMD 庫。相比於 npm 包的類型聲明文件,須要額外聲明一個全局變量,爲了實現這種方式,ts 提供了一個新語法 export as namespace

export as namespace
做用: 局部變量生命爲全局變量
通常使用 export as namespace 時,都是已有 npm 包的聲明文件,再基於它添加一條 export as namespace 語句,便可將聲明好的一個變量聲明爲全局變量

// types/foo/index.d.ts
export as namespace foo; // 全局導出
export = foo; // or export default foo; 導出npm模塊
declare function foo(): string;
declare namespace foo {
    const bar: number;
}
複製代碼

直接擴展全局變量

// global.extend.d.ts
interface String {
    prependHello(): string;
}
複製代碼
// src/index.ts
'xx'.prependHello()
複製代碼

在 npm/UMD 中擴展全局變量

對於一個 npm 包或者 UMD 庫的聲明文件,只有 export 導出的類型聲明才能被導入 導入此庫以後能夠擴展全局變量, 須要使用 declare global

// types/foo/index.d.ts

declare global {
    interface String {
        prependHello(): string;
    }
}

export default function foo(): string;
複製代碼
// src/index.ts

import foo from './foo'
'bar'.prependHello()
複製代碼

模塊插件

導入一個模塊插件, 改變原有模塊結構, 原有模塊已有聲明文件, 導入的模塊插件沒有聲明文件

// types/moment-plugin/index.d.ts

import * as moment from 'moment'; // 原有模塊

declare module 'moment' {
    export function foo(): moment.CalendarKey;
}
複製代碼
// src/index.ts

import * as moment from 'moment';
import 'moment-plugin';

moment.foo();
複製代碼

單文件多模塊

declare module 也可用於在一個文件中一次性聲明多個模塊的類型

// types/foo-bar.d.ts

declare module 'foo' {
    export interface Foo {
        foo: string;
    }
}

declare module 'bar' {
    export function bar(): string;
}
複製代碼
// src/index.ts

import { Foo } from 'foo';
import * as bar from 'bar';

let f: Foo;
bar.bar();
複製代碼

其餘

shims-vue.d.ts

Ambient Declarations(通稱:外部模塊定義) ,主要爲項目內全部的 vue 文件作模塊聲明,畢竟 ts 默認只識別 .d.ts、.ts、.tsx 後綴的文件;(即便補充了 Vue 得模塊聲明,IDE 仍是無法識別 .vue 結尾的文件,這就是爲何引入 vue 文件時必須添加後綴的緣由,不添加編譯也不會報錯)

shims-jsx.d.ts

JSX 語法的全局命名空間,這是由於基於值的元素會簡單的在它所在的做用域裏按標識符查找(此處使用的是**無狀態函數組件 (SFC)**的方法來定義),當在 tsconfig 內開啓了 jsx 語法支持後,其會自動識別對應的 .tsx 結尾的文件,可參考官網 jsx

語言類型分類

靜態類型、動態類型和弱類型、強類型
靜態類型:編譯期就知道每個變量的類型。類型錯誤編譯失敗是語法問題。如Java、C++
動態類型:編譯期不知道類型,運行時才知道。類型錯誤拋出異常發生在運行時。如JS、Python
弱類型:容忍隱式類型轉換。如JS,1+'1'='11',數字型轉成了字符型
強類型:不容忍隱式類型轉換。如Python,1+'1'會拋出TypeError

小結

關於我對聲明文件的實踐,詳見ts-declare, 另外,關於文章中,不對/不妥之處,歡迎指出,感謝~

參考

www.tslang.cn/docs/handbo… ts.xcatliu.com/basics/decl…
zhuanlan.zhihu.com/p/58123993
blog.poetries.top/2019/09/03/…
juejin.im/post/5d22b1…

相關文章
相關標籤/搜索