1. 程式人生 > >關於編寫TypeScript的.d.ts檔案

關於編寫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;