当使用一个外部JavaScript库时,或新的host API,就需要使用一个声明文件(declaration file:.d.ts)来描述库的shape。这里涉及到编写定义文件的一些高级概念,然后用一些例子来展示如何将各种概念转变为匹配的定义文件描述。
编写.d.ts文件的最佳方式是从库的文档而不是从代码开始。从文档开始保证你不会深陷实现细节,通常这比阅读JS代码更为容易。下面的例子就像你正在阅读这些例子代码的文档一样。
当定义接口(例如"options"对象)时,有选项是否将这些类型放入一个模块中。这主要是一个调用决策,使用者是否可能声明变量或类型参数,类型名称是否会与其他类型名称冲突,还是将它们放在全局命名空间中?如果类型不可能被直接引用,或者其名称不会有唯一,那么使用模块来防止类型冲突。
许多JavaScript库接受函数为一个参数,然后传入一组参数来激活该函数调用。当编写这些类型的函数签名时,不要讲这些参数标记为可选。正确的思考方式是"提供什么参数"(What parameters will be provided?"),而不是"利用什么参数"(What parameters will be consumed?")。虽然TypeScript 0.9.7及更高版本不会强制要求可选, 但参数可选可能被外部强制要求(bivariance on argument optionality might be enforced by an external linter)。
当编写定义文件时,重要的是记住TypeScript扩展现有对象的规则。你可以选择用匿名类型或接口类型来声明变量:
匿名类型变量(Anonymously-typed var)
declare var MyPoint: { x: number; y: number; };
接口类型变量(Interfaced-typed var)
interface SomePoint { x: number; y: number; }
declare var MyPoint: SomePoint;
从使用观点看这些声明是相同的,但类型SomePoint可以通过接口合并来扩展:
interface SomePoint { z: number; }
MyPoint.z = 4; // OK
是否想要按照这种方式来扩展声明优点判断调用(is a bit of a judgement call)。要一如既往在这里表示库的内容(?)。
TypeScript中的class有两种类型: 实例类型(the instance type),它定义一个类实例有哪些成员,和构造函数类型,它定义类构造函数有哪些成员。构造函数类型也被称为静态类型( "static side" type),因为它包括类的静态成员。
当使用typeof关键字引用类的静态side时,当使用分解类模式(decomposed class pattern),即明确将类实例与类的静态类型区分开的一种模式,编写定义文件时有时候就很有用或必要。
举个例子,从使用观点看下面两个声明几乎完全一致:
Standard
class A {
static st: string;
inst: number;
constructor(m: any) {}
}
Decomposed
interface A_Static {
new(m: any): A_Instance;
st: string;
}
interface A_Instance {
inst: number;
}
declare var A: A_Static;
权衡如下:
标准类可以从外部来继承;分解类不可以。这可能在后续的TypeScript版本中改变,如果允许任意扩展表达式的话(arbitrary extends expressions are allowed)。
标准类和分解类都可以通过声明合并(through declaration merging)在后续向static side添加成员。
可以向分解类添加实例成员,但标准类不可以。
当编写分解类时需要给出更多类型的合理名称。
通常不要再接口前面用I做前缀(例如IColor),这是因为TypeScript中的接口概念比C#或Java更宽泛,不使用IFoo命名约定方式。
下面看一些例子。对每个例子,先是库的使用(Usage)样例,然后是使用样例的类型定义。当有多个好的表现方式时,可能列出多个类型定义的样例。
Usage
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 });
Typing
module animalFactory {
interface AnimalOptions {
name: string;
height?: number;
weight?: number;
}
function create(name: string, animalOptions?: AnimalOptions): Animal;
}
Usage
zooKeeper.workSchedule = "morning";
zooKeeper(giraffeCage);
Typing
// Note: Function must precede module
function zooKeeper(cage: AnimalCage);
module zooKeeper {
var workSchedule: string;
}
Usage
var w = widget(32, 16);
var y = new widget("sprocket");
// w and y are both widgets
w.sprock();
y.sprock();
Typing
interface Widget {
sprock(): void;
}
interface WidgetFactory {
new(name: string): Widget;
(width: number, height: number): Widget;
}
declare var widget: WidgetFactory;
Usage
// Either
import x = require('zoo');
x.open();
// or
zoo.open();
Typing
module zoo {
function open(): void;
}
declare module "zoo" {
export = zoo;
}
Usage
// Super-chainable library for eagles
import eagle = require('./eagle');
// Call directly
eagle('bald').fly();
// Invoke with new
var eddie = new eagle(1000);
// Set properties
eagle.favorite = 'golden';
Typing
// Note: can use any name here, but has to be the same throughout this file
declare function eagle(name: string): eagle;
declare module eagle {
var favorite: string;
function fly(): void;
}
interface eagle {
new(awesomeness: number): eagle;
}
export = eagle;
Usage
addLater(3, 4, (x) => console.log('x = ' + x));
Typing
// Note: 'void' return type is preferred here
function addLater(x: number, y: number, (sum: number) => void): void;
如果你发现还有其他模式愿意归档,可以提交comment给我们,我们会把它添加在这里。
[1] http://www.typescriptlang.org/Handbook#writing-dts-files
[2] TypeScript系列1-简介及版本新特性, http://my.oschina.net/1pei/blog/493012
[3] TypeScript手册翻译系列1-基础类型, http://my.oschina.net/1pei/blog/493181
[4] TypeScript手册翻译系列2-接口, http://my.oschina.net/1pei/blog/493388
[5] TypeScript手册翻译系列3-类, http://my.oschina.net/1pei/blog/493539
[6] TypeScript手册翻译系列4-模块, http://my.oschina.net/1pei/blog/495948
[7] TypeScript手册翻译系列5-函数, http://my.oschina.net/1pei/blog/501273
[8] TypeScript手册翻译系列6-泛型, http://my.oschina.net/1pei/blog/513483
[9] TypeScript手册翻译系列7-常见错误与mixins, http://my.oschina.net/1pei/blog/513499
[10] TypeScript手册翻译系列8-声明合并, http://my.oschina.net/1pei/blog/513649
[11] TypeScript手册翻译系列9-类型推断, http://my.oschina.net/1pei/blog/513652
[12] TypeScript手册翻译系列10-类型兼容性, http://my.oschina.net/1pei/blog/513833