當(dāng)使用外部JavaScript庫(kù)或新的宿主API時(shí),你需要一個(gè)聲明文件(.d.ts)定義程序庫(kù)的shape。 這個(gè)手冊(cè)包含了寫(xiě).d.ts文件的高級(jí)概念,并帶有一些例子,告訴你怎么去寫(xiě)一個(gè)聲明文件。
最好從程序庫(kù)的文檔而不是代碼開(kāi)始寫(xiě).d.ts文件。 這樣保證不會(huì)被具體實(shí)現(xiàn)所干擾,而且相比于JS代碼更易讀。 下面的例子會(huì)假設(shè)你正在參照文檔寫(xiě)聲明文件。
當(dāng)定義接口(例如:“options”對(duì)象),你會(huì)選擇是否將這些類(lèi)型放進(jìn)命名空間里。 這主要是靠主觀(guān)判斷 -- 如果使用的人主要是用這些類(lèi)型來(lái)聲明變量和參數(shù),并且類(lèi)型命名不會(huì)引起命名沖突,則放在全局命名空間里更好。 如果類(lèi)型不是被直接使用,或者沒(méi)法起一個(gè)唯一的名字的話(huà),就使用命名空間來(lái)避免與其它類(lèi)型發(fā)生沖突。
許多JavaScript庫(kù)接收一個(gè)函數(shù)做為參數(shù),之后傳入已知的參數(shù)來(lái)調(diào)用它。 當(dāng)用這些類(lèi)型為函數(shù)簽名的時(shí)候,不要把這些參數(shù)標(biāo)記成可選參數(shù)。 正確的思考方式是“(調(diào)用者)會(huì)提供什么樣的參數(shù)?”,不是“(函數(shù))會(huì)使用到什么樣的參數(shù)?”。 TypeScript 0.9.7+不會(huì)強(qiáng)制這種可選參數(shù)的使用,參數(shù)可選的雙向協(xié)變可以被外部的linter強(qiáng)制執(zhí)行。
寫(xiě)聲明文件的時(shí)候,要記住TypeScript擴(kuò)展現(xiàn)有對(duì)象的方式。 你可以選擇用匿名類(lèi)型或接口類(lèi)型的方式聲明一個(gè)變量:
declare let MyPoint: { x: number; y: number; };
interface SomePoint { x: number; y: number; }
declare let MyPoint: SomePoint;
從使用者角度來(lái)講,它們是相同的,但是SomePoint類(lèi)型能夠通過(guò)接口合并來(lái)擴(kuò)展:
interface SomePoint { z: number; }
MyPoint.z = 4; // OK
是否想讓你的聲明是可擴(kuò)展的取決于主觀(guān)判斷。 通常來(lái)講,盡量符合library的意圖。
TypeScript的類(lèi)會(huì)創(chuàng)建出兩個(gè)類(lèi)型:實(shí)例類(lèi)型,定義了類(lèi)型的實(shí)例具有哪些成員;構(gòu)造函數(shù)類(lèi)型,定義了類(lèi)構(gòu)造函數(shù)具有哪些類(lèi)型。 構(gòu)造函數(shù)類(lèi)型也被稱(chēng)做類(lèi)的靜態(tài)部分類(lèi)型,因?yàn)樗祟?lèi)的靜態(tài)成員。
你可以使用typeof
關(guān)鍵字來(lái)拿到類(lèi)靜態(tài)部分類(lèi)型,在寫(xiě)聲明文件時(shí),想要把類(lèi)明確的分解成實(shí)例類(lèi)型和靜態(tài)類(lèi)型時(shí)是有用且必要的。
下面是一個(gè)例子,從使用者的角度來(lái)看,這兩個(gè)聲明是等同的:
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;
這里的利弊如下:
一般來(lái)講,不要給接口加I前綴(比如:IColor)。 因?yàn)門(mén)ypeScript的接口類(lèi)型概念比C#或Java里的意義更為廣泛,IFoo命名不利于這個(gè)特點(diǎn)。
下面進(jìn)行例子部分。對(duì)于每個(gè)例子,首先使用應(yīng)用示例,然后是類(lèi)型聲明。 如果有多個(gè)好的聲明表示方法,會(huì)列出多個(gè)。
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;
如果你想看其它模式的實(shí)現(xiàn)方式,請(qǐng)?jiān)?a rel="nofollow" >這里留言! 我們會(huì)盡可能地加到這里來(lái)。