關於編寫TypeScript的.d.ts檔案
建議您直接跳轉到上面的網址檢視最新版本。
介紹
當使用外部JavaScript庫或新的宿主API時,你需要一個宣告檔案(.d.ts)定義程式庫的shape。 這個手冊包含了寫.d.ts檔案的高階概念,並帶有一些例子,告訴你怎麼去寫一個宣告檔案。
指導與說明
流程
最好從程式庫的文件開始寫.d.ts檔案,而不是程式碼。 這樣保證不會被具體實現所幹擾,而且相比於JS程式碼更易讀。 下面的例子會假設你正在參照文件寫宣告檔案。
名稱空間
當定義介面(例如:“options”物件),你會選擇是否將這些型別放進名稱空間裡。 這主要是靠主觀判斷 -- 使用的人主要是用這些型別宣告變數和引數,並且型別命名不會引起命名衝突,放在全域性名稱空間裡更好。 如果型別不是被直接使用,或者沒法起一個唯一的名字的話,就使用名稱空間來避免與其它型別發生衝突。
回撥函式
許多JavaScript庫接收一個函式做為引數,之後傳入已知的引數來呼叫它。 當為這些型別與函式簽名的時候,不要把這個引數標記成可選引數。 正確的思考方式是“會提供什麼樣的引數?”,不是“會使用到什麼樣的引數?”。 TypeScript 0.9.7+不會強制這種可選引數的使用,引數可選的雙向協變可以被外部的linter強制執行。
擴充套件與宣告合併
寫宣告檔案的時候,要記住TypeScript擴充套件現有物件的方式。 你可以選擇用匿名型別或介面型別的方式宣告一個變數:
匿名型別var
declare var MyPoint: { x: number; y: number; };
介面型別var
interface SomePoint { x: number; y: number; }
declare var MyPoint: SomePoint;
從使用者角度來講,它們是相同的,但是SomePoint型別能夠通過介面合併來擴充套件:
interface SomePoint { z: number; }
MyPoint.z = 4; // OK
是否想讓你的宣告是可擴充套件的取決於主觀判斷。 通常來講,儘量符合library的意圖。
類的分解
TypeScript的類會創建出兩個型別:例項型別,定義了型別的例項具有哪些成員;建構函式型別,定義了類建構函式具有哪些型別。 建構函式型別也被稱做類的靜態部分型別,因為它包含了類的靜態成員。
你可以使用typeof
關鍵字來拿到類靜態部分型別,在寫宣告檔案時,想要把類明確的分解成例項型別和靜態型別時是有用且必要的。
下面是一個例子,從使用者的角度來看,這兩個宣告是等同的:
標準版
class A {
static st: string;
inst: number;
constructor(m: any) {}
}
分解版
interface A_Static {
new(m: any): A_Instance;
st: string;
}
interface A_Instance {
inst: number;
}
declare var A: A_Static;
這裡的利弊如下:
- 標準方式可以使用extends來繼承;分解的類不能。這可能會在未來版本的TypeScript裡改變:是否允許任何的extends表示式
- 都允許之後為類新增靜態成員
- 允許為分解的類再新增例項成員,標準版不允許
- 使用分解類的時候,為成員起合理的名字
命名規則
一般來講,不要給介面加I字首(比如:IColor)。 類為TypeScript裡的介面型別比C#或Java裡的意義更為廣泛,IFoo命名不利於這個特點。
例子
下面進行例子部分。對於每個例子,先是使用使用方法,然後是型別宣告。 如果有多個好的宣告表示方法,會列出多個。
引數物件
使用方法
animalFactory.create("dog");
animalFactory.create("giraffe", { name: "ronald" });
animalFactory.create("panda", { name: "bob", height: 400 });
// Invalid: name must be provided if options is given
animalFactory.create("cat", { height: 32 });
型別
namespace animalFactory {
interface AnimalOptions {
name: string;
height?: number;
weight?: number;
}
function create(name: string, animalOptions?: AnimalOptions): Animal;
}
帶屬性的函式
使用方法
zooKeeper.workSchedule = "morning";
zooKeeper(giraffeCage);
型別
// Note: Function must precede namespace
function zooKeeper(cage: AnimalCage);
namespace zooKeeper {
var workSchedule: string;
}
可以用new呼叫也可以直接呼叫的方法
使用方法
var w = widget(32, 16);
var y = new widget("sprocket");
// w and y are both widgets
w.sprock();
y.sprock();
型別
interface Widget {
sprock(): void;
}
interface WidgetFactory {
new(name: string): Widget;
(width: number, height: number): Widget;
}
declare var widget: WidgetFactory;
全域性的/不清楚的Libraries
使用方法
// Either
import x = require('zoo');
x.open();
// or
zoo.open();
型別
declare namespace zoo {
function open(): void;
}
declare module "zoo" {
export = zoo;
}
外部模組的單個複雜物件
使用方法
// Super-chainable library for eagles
import eagle = require('./eagle');
// Call directly
eagle('bald').fly();
// Invoke with new
var eddie = new eagle('Mille');
// Set properties
eddie.favorite = 'golden';
型別
// Note: can use any name here, but has to be the same throughout this file
declare function eagle(name: string): eagle;
declare namespace eagle {
var favorite: string;
function fly(): void;
}
interface eagle {
new(awesomeness: number): eagle;
}
export = eagle;
回撥函式
使用方法
addLater(3, 4, x => console.log('x = ' + x));
型別
// Note: 'void' return type is preferred here
function addLater(x: number, y: number, (sum: number) => void): void;