【TypeScript】型別定義檔案(*.d.ts)
在 《從 JavaScript 到 TypeScript 系列》 文章我們已經學習了 TypeScript 相關的知識。 TypeScript 的核心在於靜態型別,我們在編寫 TS 的時候會定義很多的型別,但是主流的庫都是 JavaScript 編寫的,並不支援型別系統。那麼如何讓這些第三方庫也可以進行型別推導呢?
這篇文章我們來講解 JavaScript 和 TypeScript 的靜態型別交叉口 —— 型別定義檔案。
型別定義檔案
在 TypeScript 中,我們可以很簡單的,在程式碼編寫中定義型別:
interface IBaseModel {
say(keys: string[] | null ): object
}
class User implements IBaseModel {
name: string
constructor (name: string) {
this.name = name
}
}
但是主流的庫都是 JavaScript 編寫的,TypeScript 身為 JavaScript 的超集,自然需要考慮到如何讓 JS 庫也能定義靜態型別。
TypeScript 經過了一系列的摸索,先後提出了 tsd(已廢棄)、typings(已廢棄),最終在 TypeScript 2.0 的時候重新整理了型別定義,提出了 DefinitelyTyped
DefinitelyTyped 就是讓你把 “型別定義檔案(*.d.ts)”,釋出到 npm
中,配合編輯器(或外掛),就能夠檢測到 JS 庫中的靜態型別。
型別定義檔案的以 .d.ts
結尾,裡面主要用來定義型別。
例如這是 jQuery 的型別定義檔案 中一段程式碼(為了方便理解做了一些改動)
// 定義 jQuery 需要用到的型別名稱空間
declare namespace JQuery {
// 定義基本使用的型別
type Selector = string;
type TypeOrArray<T> = T | T[];
type htmlString = string;
}
// 定義 jQuery 介面,jquery 是一個 包含 Element 的集合
interface JQuery<TElement extends Node = HTMLElement> extends Iterable<TElement> {
length: number;
eq(index: number): this;
// 過載
add(selector: JQuery.Selector, context: Element): this;
add(selector: JQuery.Selector | JQuery.TypeOrArray<Element> | JQuery.htmlString | JQuery): this;
children(selector?: JQuery.Selector): this;
css(propertyName: string): string;
html(): string;
}
// 對模組 jquery 輸出介面
declare module 'jquery' {
// module 中要使用 export = 而不是 export default
export = jQuery;
}
型別定義
*.d.ts
編寫起來非常簡單,經過 TypeScript 良好的靜態型別系統洗禮過後,語法學習成本非常低。
我們可以使用 type
用來定義型別變數:
// 基本型別
type UserName = string
// 型別賦值
type WebSite = string
type Tsaid = WebSite
可以看到 type
其實可以定義各種格式的型別,也可以和其他型別進行組合。
// 物件
type User = {
name: string;
age: number;
website: WebSite;
}
// 方法
type say = (age: number) => string
// 類
class TaSaid {
website: string;
say: (age: number) => string;
}
當然,我們也可以使用 interface
定義我們的複雜型別,在 TS 中我們也可以直接定義 interface
:
interface Application {
init(): void
get(key: string): object
}
interface
和type
(或者說 class
) 很像。
但是 type
的含義是定義自定義型別,當 TS 提供給你的基礎型別都不滿足的時候,可以使用 type
自由組合出你的新型別,而 interface
應該是對外輸出的介面。
type
不可以被繼承,但 interface
可以:
interface BaseApplication {
appId: number
}
export interface Application extends BaseApplication {
init(): void
get(key: string): object
}
declare
declare
可以建立 *.d.ts
檔案中的變數,declare 只能作用域最外層:
declare var foo: number;
declare function greet(greeting: string): void;
declare namespace tasaid {
// 這裡不能 declare
interface blog {
website: 'http://tasaid.com'
}
}
基本上頂層的定義都需要使用 declare
, class
也是:
declare class User {
name: string
}
namespace
為防止型別重複,使用 namespace
用於劃分區域塊,分離重複的型別,頂層的 namespace
需要 declare
輸出到外部環境,子名稱空間不需要 declare
。
// 名稱空間
declare namespace Models {
type A = number
// 子名稱空間
namespace Config {
type A = object
type B = string
}
}
type C = Models.Config.A
組合定義
上面我們只演示了一些簡單的型別組合,生產環境中會包含許多複雜的型別定義,這時候我們就需要各種組合出強大的型別定義:
動態屬性
有些型別的屬性名是動態而未知的,例如:
{
'10086': {
name: '中國移動',
website: 'http://www.10086.cn',
},
'10010': {
name: '中國聯通',
website: 'http://www.10010.com',
},
'10000': {
name: '中國電信',
website: 'http://www.189.cn'
}
}
我們可以使用動態屬性名來定義型別:
interface ChinaMobile {
name: string;
website: string;
}
interface ChinaMobileList {
// 動態屬性
[phone: string]: ChinaMobile
}
型別遍歷
當你已知某個類型範圍的時候,可以使用 in
和 keyof
來遍歷型別,例如上面的 ChinaMobile 例子,我們可以使用 in
來約束屬性名必須為三家運營商之一:
type ChinaMobilePhones = '10086' | '10010' | '10000'
interface ChinaMobile {
name: string;
website: string;
}
// 只能 type 使用, interface 無法使用
type ChinaMobileList = {
// 遍歷屬性
[phone in ChinaMobilePhones]: ChinaMobile
}
我們也可以用 keyof 來約定方法的引數
export type keys = {
name: string;
appId: number;
config: object;
}
class Application {
// 引數和值約束範圍
set<T extends keyof keys>(key: T, val: keys[T])
get<T extends keyof keys>(key: T): keys[T]
}
整合釋出
有兩種主要方式用來發布型別定義檔案到 npm
:
- 與你的 npm 包捆綁在一起(內建型別定義檔案)
前者,安裝完了包之後會自動檢測並識別型別定義檔案。 後者,則需要通過 npm i @types/xxxx 安裝,這就是我們前面所說的 DefinitelyTyped ,用於擴充套件 JS 庫的型別宣告。
內建型別定義檔案
內建型別定義就是把你的型別定義檔案和 npm 包一起釋出,一般來說,型別定義檔案都放在包根目錄的 types
目錄裡,例如 vue:
如果你的包有一個主 .js
檔案,需要在 package.json
裡指定主型別定義檔案。
設定 types
或 typeings
屬性指向捆綁在一起的型別定義檔案。 例如包目錄如下:
├── lib
│ ├── main.js
│ └── main.d.ts # 型別定義檔案
└── package.json
// pageage.json
{
"name": "demo",
"author": "demo project",
"version": "1.0.0",
"main": "./lib/main.js",
// 定義主型別定義檔案
"types": "./lib/main.d.ts"
}
如果主型別定義檔名是 index.d.ts 並且位置在包的根目錄裡,就不需要使用 types 屬性指定了。
├── lib
│ └── main.js
├── index.d.ts # 型別定義檔案
└── package.json
如果你發的包中,package.json
中使用了 files
欄位的話(npm
會根據 files
配置的規則決定釋出哪些檔案),則需要手動把型別定義檔案加入:
// pageage.json
{
"files": [
"index.js",
"*.d.ts"
]
}
如果只發二級目錄的話,把型別定義檔案放到對應的二級目錄下即可:
import { default as App } from 'demo/app'
釋出到 @types organizatio
釋出到 @types organizatio
的包表示源包沒有包含型別定義檔案,第三方/或原作者定義好型別定義檔案之後,釋出到 @types 中。例如 @types/express。
根據 DefinitelyTyped
的規則,和編輯器(和外掛) 自動檢測靜態型別。
如果想讓你的包釋出為 @types 包,需要提交一個 pull request 到
https://github.com/DefinitelyTyped/DefinitelyTyped
如果你正在使用 TypeScript,而使用了一些 JS 包並沒有對應的型別定義檔案,可以編寫一份然後提交到 @types
。
贈人玫瑰,手留餘香。
釋出到 @types organizatio
的包可以通過 TypeSearch 搜尋檢索,使用 npm install --save-dev @types/xxxx
安裝:
相關推薦
【TypeScript】型別定義檔案(*.d.ts)
在 《從 JavaScript 到 TypeScript 系列》 文章我們已經學習了 TypeScript 相關的知識。 TypeScript 的核心在於靜態型別,我們在編寫 TS 的時候會定義很多的型別,但是主流的庫都是 JavaScript 編寫的,並不
TypeScript 型別定義檔案(*.d.ts)自動生成工具
在開發ts時,有時會遇到沒有d.ts檔案的庫,同時在老專案遷移到ts專案時也會遇到一些檔案需要自己編寫宣告檔案,但是在需要的宣告檔案比較多的情況,就需要自動生產宣告檔案。用過幾個庫。今天簡單記錄一下。自己怎麼編寫有很多教程和文件,那裡就不介紹了。 1、為整個包新增宣告檔案 使用微軟的dt
【TypeScript】TypeScript+Ionic的Component檔案裡(ts檔案)如何建立物件
在Ionic檔案中,有以下幾個步驟: 1.import導包; import {Component} from '@angular/core'; import {IonicPage, NavParams, ToastController, AlertC
【Highchart】自定義儀表盤配置檔案,儀表盤分段及漸變色
highchart配置出儀表盤 chart: { type: 'gauge', plotBackgroundColor: null, plotBackgroundImage: null, plotBorderWi
【轉】Java根據檔案流判斷檔案型別
在讀取檔案的時候我們通常會需要判斷檔案的型別,而根據字尾獲取檔案的型別是不太準確的,所以我們需要讀取檔案流。 /** 常用檔案的檔案頭如下:(以前六位為準) JPEG (jpg),檔案頭:FFD8FF PNG (png),檔案頭:89504E47 GIF (gif),檔案頭:474
如何釋出TypeScript的宣告檔案.d.ts
最近為Dynamic Web TWAIN的JavaScript介面編寫了一份.d.ts檔案。這個檔案就像C/C++用.h檔案。好處就是在使用TypeScript開發的時候,可以提供IntelliSense智慧提示。開發者可以直接看到介面的定義。使用TypeSearch可以找到
【Android】 XML佈局檔案中,使用自定義屬性不提示和不生效
在XML檔案中使用首先要宣告 xmlns:toolbar=http://schemas.android.com/apk/res/cn.zzm.toolbar 注意,“toolbar”可以換成其他的任何名字,後面的url地址必須最後一部分必須用上自定義元件的包名。自定義屬性了
【C++】變數定義在.h標頭檔案導致 multiple definition of 的解決方法和根本原因
說明:出現這個錯誤,請你先檢查重複定義的變數是否是定義在了.h標頭檔案中,如果是,請您耐心的看完這篇文章,他會告訴你錯誤的根本原因。 如果你很著急,不想弄清楚原因,請直接按下面的方法更改: 假設重複定
【4】自定義下拉框
order viewport down jquer pos bottom last png 下拉框 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8
Android自己定義組件系列【1】——自己定義View及ViewGroup
全部 int ++ btn -i pre 剪切 final 界面 View類是ViewGroup的父類,ViewGroup具有View的全部特性。ViewGroup主要用來充當View的容器。將當中的View作為自己孩子,並對其進行管理。當然孩子也能夠是ViewGrou
【Android】自己定義圓形ImageView(圓形頭像 可指定大小)
代碼實現 err float avi rim war tor pos dsm 近期在仿手Q的UI,這裏面常常要用到的就是圓形頭像,看到 在android中畫圓形圖片的幾種辦法 這篇文章,了解了制作這樣的頭像的原理.只是裏面提供的方法另一個不足的地方就是
【BZOJ5101】[POI2018]Powód 並查集
cstring sin 多少 合並 div 一次 mes true printf 【BZOJ5101】[POI2018]Powód Description 在地面上有一個水箱,它的俯視圖被劃分成了n行m列個方格,相鄰兩個方格之間有一堵厚度可以忽略不計的
python web框架【補充】自定義web框架
數據大小 路徑 .py 用戶 ipa clr 接受 values 規範 http協議 HTTP簡介 HTTP協議是Hyper Text Transfer Protocol(超文本傳輸協議)的縮寫,是用於從萬維網(WWW:World Wide Web )服務器傳輸超文本到本
flask第二十四篇——模板【6】自定義過濾器
def 定義 lazy .py highlight abs nbsp tps word 請關註孟船長的公眾號:自動化測試實戰 大家想了解其他過濾器可以參考這裏: http://jinja.pocoo.org/docs/dev/templates/#builtin-fil
獲取TypeScript的聲明文件.d.ts
http col 安裝包 target 文件 目錄 tps div color 一、TypeScript的聲明文件就像C/C++用.h文件。當使用TypeScript調用其他已經編寫好的類庫時,可以提供IntelliSense智能提示。 二、使用npm指令來獲取.d.ts
【BZOJ1228】[SDOI2009]E&D(博弈論)
博弈 pac iostream cst 打表 。。 name ans getch 【BZOJ1228】[SDOI2009]E&D(博弈論) 題面 BZOJ 洛谷 題解 這種打表找規律的題目真的不知道可以說什麽好啊。。。 #include<iostream>
【JVM】class類檔案的結構
作為Java程式猿,我們知道,我們寫好的.java 原始碼,最後會被Java編譯器編譯成字尾為.class的檔案,該型別的檔案是由位元組組成的檔案,又叫位元組碼檔案。那麼,class位元組碼檔案裡面到底是有什麼呢?它又是怎樣組織的呢?讓我們先來大概瞭解一下他的組成結構吧。
【WXS】變數定義保留識別符號
以下字元不能作為變數名稱定義: delete void typeof null undefined NaN Infinity var if else true false require this function arguments return for while
【typescript】數字轉變為漢字(大寫)
其他語言的都有,記錄一下自己剪下的數字: /**根據數字獲取漢字*/ public changeToCN(num: number): string { let words = ["零", "一", "二", "三", "四", "五", "六", "七", "八",
【typescript】簡單公用方法
/**範圍內獲取整數隨機數*/ /**範圍內獲取整數隨機數*/ function getRandomInt(min: number, max: number): number { var Range = max - min; var Rand = Math.random();