當使用外部JavaScript庫或新的宿主API時,你需要一個聲明文件(.d.ts)定義程序庫的shape。 這個手冊包含了寫.d.ts文件的高級概念,并帶有一些例子,告訴你怎么去寫一個聲明文件。
最好從程序庫的文檔而不是代碼開始寫.d.ts文件。 這樣保證不會被具體實現(xiàn)所干擾,而且相比于JS代碼更易讀。 下面的例子會假設你正在參照文檔寫聲明文件。
當定義接口(例如:“options”對象),你會選擇是否將這些類型放進命名空間里。 這主要是靠主觀判斷 -- 如果使用的人主要是用這些類型來聲明變量和參數,并且類型命名不會引起命名沖突,則放在全局命名空間里更好。 如果類型不是被直接使用,或者沒法起一個唯一的名字的話,就使用命名空間來避免與其它類型發(fā)生沖突。
許多JavaScript庫接收一個函數做為參數,之后傳入已知的參數來調用它。 當用這些類型為函數簽名的時候,不要把這些參數標記成可選參數。 正確的思考方式是“(調用者)會提供什么樣的參數?”,不是“(函數)會使用到什么樣的參數?”。 TypeScript 0.9.7+不會強制這種可選參數的使用,參數可選的雙向協(xié)變可以被外部的linter強制執(zhí)行。
寫聲明文件的時候,要記住TypeScript擴展現(xiàn)有對象的方式。 你可以選擇用匿名類型或接口類型的方式聲明一個變量:
declare let MyPoint: { x: number; y: number; };
interface SomePoint { x: number; y: number; }
declare let MyPoint: SomePoint;
從使用者角度來講,它們是相同的,但是SomePoint類型能夠通過接口合并來擴展:
interface SomePoint { z: number; }
MyPoint.z = 4; // OK
是否想讓你的聲明是可擴展的取決于主觀判斷。 通常來講,盡量符合library的意圖。
TypeScript的類會創(chuàng)建出兩個類型:實例類型,定義了類型的實例具有哪些成員;構造函數類型,定義了類構造函數具有哪些類型。 構造函數類型也被稱做類的靜態(tài)部分類型,因為它包含了類的靜態(tài)成員。
你可以使用typeof
關鍵字來拿到類靜態(tài)部分類型,在寫聲明文件時,想要把類明確的分解成實例類型和靜態(tài)類型時是有用且必要的。
下面是一個例子,從使用者的角度來看,這兩個聲明是等同的:
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 let A: A_Static;
這里的利弊如下:
一般來講,不要給接口加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 {
let workSchedule: string;
}
let w = widget(32, 16);
let 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 let widget: WidgetFactory;
// 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.kind = 'golden';
interface Eagle {
(kind: string): Eagle;
new (kind: string): Eagle;
kind: string;
fly(): void
}
declare var Eagle: Eagle;
export = Eagle;
// Common pattern for node modules (e.g. rimraf, debug, request, etc.)
import sayHello = require('say-hello');
sayHello('Travis');
declare module 'say-hello' {
function sayHello(name: string): void;
export = sayHello;
}
addLater(3, 4, x => console.log('x = ' + x));
// Note: 'void' return type is preferred here
function addLater(x: number, y: number, callback: (sum: number) => void): void;
如果你想看其它模式的實現(xiàn)方式,請在這里留言! 我們會盡可能地加到這里來。