TypeScript手册翻译系列11-编写.d.ts文件

Writing .d.ts files

当使用一个外部JavaScript库时,或新的host API,就需要使用一个声明文件(declaration file:.d.ts)来描述库的shape。这里涉及到编写定义文件的一些高级概念,然后用一些例子来展示如何将各种概念转变为匹配的定义文件描述。

Guidelines and Specifics

Workflow

编写.d.ts文件的最佳方式是从库的文档而不是从代码开始。从文档开始保证你不会深陷实现细节,通常这比阅读JS代码更为容易。下面的例子就像你正在阅读这些例子代码的文档一样。

Namespacing

当定义接口(例如"options"对象)时,有选项是否将这些类型放入一个模块中。这主要是一个调用决策,使用者是否可能声明变量或类型参数,类型名称是否会与其他类型名称冲突,还是将它们放在全局命名空间中?如果类型不可能被直接引用,或者其名称不会有唯一,那么使用模块来防止类型冲突。

Callbacks

许多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)。

Extensibility and Declaration Merging

当编写定义文件时,重要的是记住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)。要一如既往在这里表示库的内容(?)。

类分解(Class Decomposition)

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添加成员。

  • 可以向分解类添加实例成员,但标准类不可以。

  • 当编写分解类时需要给出更多类型的合理名称。

命名约定(Naming Conventions)

通常不要再接口前面用I做前缀(例如IColor),这是因为TypeScript中的接口概念比C#或Java更宽泛,不使用IFoo命名约定方式。

举例

下面看一些例子。对每个例子,先是库的使用(Usage)样例,然后是使用样例的类型定义。当有多个好的表现方式时,可能列出多个类型定义的样例。

可选对象(Options Objects)

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;
}

带属性的函数(Functions with Properties)

Usage

zooKeeper.workSchedule = "morning";
zooKeeper(giraffeCage);


Typing

// Note: Function must precede module
function zooKeeper(cage: AnimalCage);

module zooKeeper {    
   var workSchedule: string;
}

New + callable methods

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;

全局/外部不可知?库(Global / External-agnostic Libraries)

Usage

// Either
import x = require('zoo');
x.open();
// or
zoo.open();


Typing

module zoo {  
   function open(): void;
}

declare module "zoo" {    
   export = zoo;
}

外部模块中的单个复合对象(Single Complex Object in External Modules)

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;

回调(Callbacks)

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

你可能感兴趣的:(TypeScript)