diff --git a/pages/Typings for NPM Packages.md b/pages/Typings for NPM Packages.md index 7517336f0..498cf84eb 100644 --- a/pages/Typings for NPM Packages.md +++ b/pages/Typings for NPM Packages.md @@ -1,28 +1 @@ -The TypeScript compiler resolves Node module names by following the [Node.js module resolution algorithm](https://nodejs.org/api/modules.html#modules_all_together). -TypeScript can also load typings that are bundled with npm packages. -The compiler will try to discover typings for module `"foo"` using the following set of rules: - -1. Try to load the `package.json` file located in the appropriate package folder (`node_modules/foo/`). If present,read the path to the typings file described in the `"typings"` field. For example, in the following `package.json`, the compiler will resolve the typings at `node_modules/foo/lib/foo.d.ts` - - ```json - { - "name": "foo", - "author": "Vandelay Industries", - "version": "1.0.0", - "main": "./lib/foo.js", - "typings": "./lib/foo.d.ts" - } - ``` - -2. Try to load a file named `index.d.ts` located in the package folder (`node_modules/foo/`) - this file should contain typings for the package. - -The precise algorithm for module resolution can be found [here](https://github.com/Microsoft/TypeScript/issues/2338). - -### Your definition files should - -* be`.d.ts` files -* be written as external modules -* not contain triple-slash references - -The rationale is that typings should not bring new compilable items to the set of compiled files; otherwise actual implementation files in the package can be overwritten during compilation. -Additionally, **loading typings should not pollute global scope** by bringing potentially conflicting entries from different version of the same library. \ No newline at end of file +> ## This page has been moved to [Publishing](./declaration files/Publishing.md) diff --git a/pages/Writing Declaration Files.md b/pages/Writing Declaration Files.md index 8b478bef0..2d3fc682d 100644 --- a/pages/Writing Declaration Files.md +++ b/pages/Writing Declaration Files.md @@ -1,268 +1 @@ -# Introduction - -When using an external JavaScript library, or new host API, you'll need to use a declaration file (.d.ts) to describe the shape of that library. -This guide covers a few high-level concepts specific to writing declaration files, then proceeds with a number of examples that show how to transcribe various concepts to their matching declaration file descriptions. - -# Guidelines and Specifics - -## Workflow - -The best way to write a .d.ts file is to start from the documentation of the library, not the code. -Working from the documentation ensures the surface you present isn't muddied with implementation details, and is typically much easier to read than JS code. -The examples below will be written as if you were reading documentation that presented example calling code. - -## Namespacing - -When defining interfaces (for example, "options" objects), you have a choice about whether to put these types inside a namespace or not. -This is largely a judgement call -- if the consumer is likely to often declare variables or parameters of that type, and the type can be named without risk of colliding with other types, prefer placing it in the global namespace. -If the type is not likely to be referenced directly, or can't be named with a reasonably unique name, do use a namespace to prevent it from colliding with other types. - -## Callbacks - -Many JavaScript libraries take a function as a parameter, then invoke that function later with a known set of arguments. -When writing the function signatures for these types, *do not* mark those parameters as optional. -The right way to think of this is *"What parameters will be provided?"*, not *"What parameters will be consumed?"*. -While TypeScript 0.9.7 and above does not enforce that the optionality, bivariance on argument optionality might be enforced by an external linter. - -## Extensibility and Declaration Merging - -When writing declaration files, it's important to remember TypeScript's rules for extending existing objects. -You might have a choice of declaring a variable using an anonymous type or an interface type: - -#### Anonymously-typed var - -```ts -declare var MyPoint: { x: number; y: number; }; -``` - -#### Interfaced-typed var - -```ts -interface SomePoint { x: number; y: number; } -declare var MyPoint: SomePoint; -``` - -From a consumption side these declarations are identical, but the type `SomePoint` can be extended through interface merging: - -```ts -interface SomePoint { z: number; } -MyPoint.z = 4; // OK -``` - -Whether or not you want your declarations to be extensible in this way is a bit of a judgement call. -As always, try to represent the intent of the library here. - -## Class Decomposition - -Classes in TypeScript create two separate types: the instance type, which defines what members an instance of a class has, and the constructor function type, which defines what members the class constructor function has. -The constructor function type is also known as the "static side" type because it includes static members of the class. - -While you can reference the static side of a class using the `typeof` keyword, it is sometimes useful or necessary when writing declaration files to use the *decomposed class* pattern which explicitly separates the instance and static types of class. - -As an example, the following two declarations are nearly equivalent from a consumption perspective: - -#### Standard - -```ts -class A { - static st: string; - inst: number; - constructor(m: any) {} -} -``` - -#### Decomposed - -```ts -interface A_Static { - new(m: any): A_Instance; - st: string; -} -interface A_Instance { - inst: number; -} -declare var A: A_Static; -``` - -The trade-offs here are as follows: - -* Standard classes can be inherited from using `extends`; decomposed classes cannot. This might change in later version of TypeScript if arbitrary `extends` expressions are allowed. -* It is possible to add members later (through declaration merging) to the static side of both standard and decomposed classes -* It is possible to add instance members to decomposed classes, but not standard classes -* You'll need to come up with sensible names for more types when writing a decomposed class - -## Naming Conventions - -In general, you shouldn't prefix interfaces with `I` (e.g. `IColor`). -Because the concept of an interface in TypeScript is much more broad than in C# or Java, the `IFoo` naming convention is not broadly useful. - -# Examples - -Let's jump in to the examples section. For each example, sample *usage* of the library is provided, followed by the declaration code that accurately types the usage. -When there are multiple good representations, more than one declaration sample might be listed. - -## Options Objects - -#### Usage - -```ts -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 - -```ts -namespace animalFactory { - interface AnimalOptions { - name: string; - height?: number; - weight?: number; - } - function create(name: string, animalOptions?: AnimalOptions): Animal; -} -``` - -## Functions with Properties - -#### Usage - -```ts -zooKeeper.workSchedule = "morning"; -zooKeeper(giraffeCage); -``` - -#### Typing - -```ts -// Note: Function must precede namespace -function zooKeeper(cage: AnimalCage); -namespace zooKeeper { - var workSchedule: string; -} -``` - -## New + callable methods - -#### Usage - -```ts -var w = widget(32, 16); -var y = new widget("sprocket"); -// w and y are both widgets -w.sprock(); -y.sprock(); -``` - -#### Typing - -```ts -interface Widget { - sprock(): void; -} - -interface WidgetFactory { - new(name: string): Widget; - (width: number, height: number): Widget; -} - -declare var widget: WidgetFactory; -``` - -## Global / External-agnostic Libraries - -These libraries can be used as either globals or imports. -In a module file (one with any imports/exports), using these as globals is not allowed. -These are also known as [UMD](https://github.com/umdjs/umd) modules. - -#### Usage - -```ts -// Either -import x = require("zoo"); -x.open(); -// or -zoo.open(); -``` - -#### Typing - -```ts -export function open(): void; -export as namespace zoo; -``` - -## Single Complex Object in Modules - -#### Usage - -```ts -// 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"; -``` - -#### Typing - -```ts -interface Eagle { - (kind: string): Eagle; - new (kind: string): Eagle; - - kind: string; - fly(): void -} - -declare var Eagle: Eagle; - -export = Eagle; -``` - -## Function as an Module - -This is a common pattern for modules whose imported entities are callable functions. - -#### Usage - -```ts -import sayHello = require("say-hello"); -sayHello("Travis"); -``` - -#### Typing - -```ts -declare module "say-hello" { - function sayHello(name: string): void; - export = sayHello; -} -``` - -## Callbacks - -#### Usage - -```ts -addLater(3, 4, x => console.log("x = " + x)); -``` - -#### Typing - -```ts -// Note: 'void' return type is preferred here -function addLater(x: number, y: number, callback: (sum: number) => void): void; -``` - -Please post a comment [here](https://github.com/Microsoft/TypeScript-Handbook/issues) if there's a pattern you'd like to see documented! -We'll add to this as we can. +> ## This page has been moved to [Publishing](./declaration files/Introduction.md) diff --git a/pages/declaration files/By Example.md b/pages/declaration files/By Example.md new file mode 100644 index 000000000..e24c5c671 --- /dev/null +++ b/pages/declaration files/By Example.md @@ -0,0 +1,274 @@ +# Introduction + +The purpose of this guide is to teach you how to write a high-quality definition file. +This guide is structured by showing documentation for some API, along with sample usage of that API, + and explaining how to write the corresponding declaration. + +These examples are ordered in approximately increasing order of complexity. + +* [Global Variables](#global-variables) +* [Global Functions](#global-functions) +* [Objects with Properties](#objects-with-properties) +* [Overloaded Function](#overloaded-functions) +* [Reusable Types (Interfaces)](#reusable-types-interfaces) +* [Reusable Types (Type Aliases)](#reusable-types-type-aliases) +* [Organizing Types](#organizing-types) +* [Classes](#classes) + +# The Examples + +## Global Variables + +*Documentation* + +> The global variable `foo` contains the number of widgets present. + +*Code* + +```ts +console.log("Half the number of widgets is " + (foo / 2)); +``` + +*Declaration* + +Use `declare var` to declare variables. +If the variable is read-only, you can use `declare const`. +You can also use `declare let` if the variable is block-scoped. + +```ts +/** The number of widgets present */ +declare var foo: number; +``` + +## Global Functions + +*Documentation* + +> You can call the function `greet` with a string to show a greeting to the user. + +*Code* + +```ts +greet("hello, world"); +``` + +*Declaration* + +Use `declare function` to declare functions. + +```ts +declare function greet(greeting: string): void; +``` + +## Objects with Properties + +*Documentation* + +> The global variable `myLib` has a function `makeGreeting` for creating greetings, +> and a property `numberOfGreetings` indicating the number of greetings made so far. + +*Code* + +```ts +let result = myLib.makeGreeting("hello, world"); +console.log("The computed greeting is:" + result); + +let count = myLib.numberOfGreetings; +``` + +*Declaration* + +Use `declare namespace` to describe types or values accessed by dotted notation. + +```ts +declare namespace myLib { + function makeGreeting(s: string): string; + let numberOfGreetings: number; +} +``` + +## Overloaded Functions + +*Documentation* + +The `getWidget` function accepts a number and returns a Widget, or accepts a string and returns a Widget array. + +*Code* + +```ts +let x: Widget = getWidget(43); + +let arr: Widget[] = getWidget("all of them"); +``` + +*Declaration* + +```ts +declare function getWidget(n: number): Widget; +declare function getWidget(s: string): Widget[]; +``` + +## Reusable Types (Interfaces) + +*Documentation* + +> When specifying a greeting, you must pass a `GreetingSettings` object. +> This object has the following properties: +> - greeting: Mandatory string +> - duration: Optional length of time (in milliseconds) +> - color: Optional string, e.g. '#ff00ff' + +*Code* + +```ts +greet({ + greeting: "hello world", + duration: 4000 +}); +``` + +*Declaration* + +Use an `interface` to define a type with properties. + +```ts +interface GreetingSettings { + greeting: string; + duration?: number; + color?: string; +} + +declare function greet(setting: GreetingSettings): void; +``` + +## Reusable Types (Type Aliases) + +*Documentation* + +> Anywhere a greeting is expected, you can provide a `string`, a function returning a `string`, or a `Greeter` instance. + +*Code* + +```ts +function getGreeting() { + return "howdy"; +} +class MyGreeter extends Greeter { } + +greet("hello"); +greet(getGreeting); +greet(new MyGreeter()); +``` + +*Declaration* + +You can use a type alias to make a shorthand for a type: + +```ts +type GreetingLike = string | (() => string) | Greeting; + +declare function greet(g: GreetingLike): void; +``` + +## Organizing Types + +*Documentation* + +> The `greeter` object can log to a file or display an alert. +> You can provide LogOptions to `.log(...)` and alert options to `.alert(...)` + +*Code* + +```ts +const g = new Greeter("Hello"); +g.log({ verbose: true }); +g.alert({ modal: false, title: "Current Greeting" }); +``` + +*Declaration* + +Use namespaces to organize types. + +```ts +declare namespace GreetingLib { + interface LogOptions { + verbose?: boolean; + } + interface AlertOptions { + modal: boolean; + title?: string; + color?: string; + } +} +``` + +You can also created nested namespaces in one declaration: + +```ts +declare namespace GreetingLib.Options { + // Refer to via GreetingLib.Options.Log + interface Log { + verbose?: boolean; + } + interface Alert { + modal: boolean; + title?: string; + color?: string; + } +} +``` + +## Classes + +*Documentation* + +> You can create a greeter by instantiating the `Greeter` object, or create a customized greeter by extending from it. + +*Code* + +```ts +const myGreeter = new Greeter("hello, world"); +myGreeter.greeting = "howdy"; +myGreeter.showGreeting(); + +class SpecialGreeter extends Greeter { + constructor() { + super("Very special greetings"); + } +} +``` + +*Declaration* + +Use `declare class` to describe a class or class-like object. +Classes can have properties and methods as well as a constructor. + +```ts +declare class Greeter { + constructor(greeting: string); + + greeting: string; + showGreeting(): void; +} +``` + + diff --git a/pages/declaration files/Consumption.md b/pages/declaration files/Consumption.md new file mode 100644 index 000000000..279d7ae85 --- /dev/null +++ b/pages/declaration files/Consumption.md @@ -0,0 +1,38 @@ +In TypeScript 2.0, it has become significantly easier to consume declaration files, in acquiring, using, and finding them. +This page details exactly how to do all three + +# Downloading + +Getting type declarations in TypeScript 2.0 and above requires no tools apart from npm. + +As an example, getting the declarations for a library like lodash takes nothing more than the following command + +```cmd +npm install --save @types/lodash +``` + +# Consuming + +From there you’ll be able to use lodash in your TypeScript code with no fuss. +This works for both modules and global code. + +For example, once you’ve `npm install`-ed your type declarations, you can use imports and write + +```ts +import * as _ from "lodash"; +_.padStart("Hello TypeScript!", 20, " "); +``` + +or if you’re not using modules, you can just use the global variable `_`. + +```ts +_.padStart("Hello TypeScript!", 20, " "); +``` + +# Searching + +For the most part, type declaration packages should always have the same name as the package name on `npm`, but prefixed with `@types/`, + but if you need, you can check out [https://aka.ms/types](https://aka.ms/types) to find the package for your favorite library. + +> Note: if the declaration file you are searching for is not present, you can always contribute one back and help out the next developer looking for it. +> Please see the DefinitelyTyped [contribution guidelines page](http://definitelytyped.org/guides/contributing.html) for details. diff --git a/pages/declaration files/Deep Dive.md b/pages/declaration files/Deep Dive.md new file mode 100644 index 000000000..289576a39 --- /dev/null +++ b/pages/declaration files/Deep Dive.md @@ -0,0 +1,231 @@ +# Definition File Theory: A Deep Dive + +Structuring modules to give the exact API shape you want can be tricky. +For example, we might want a module that can be invoked with or without `new` to produce different types, + has a variety of named types exposed in a hierarchy, + and has some properties on the module object as well. + +By reading this guide, you'll have the tools to write complex definition files that expose a friendly API surface. +This guide focuses on module (or UMD) libraries because the options here are more varied. + +## Key Concepts + +You can fully understand how to make any shape of definition + by understanding some key concepts of how TypeScript works. + +### Types + +If you're reading this guide, you probably already roughly know what a type in TypeScript is. +To be more explicit, though, a *type* is introduced with: + +* A type alias declaration (`type sn = number | string;`) +* An interface declaration (`interface I { x: number[]; }`) +* A class declaration (`class C { }`) +* An enum declaration (`enum E { A, B, C }`) +* An `import` declaration which refers to a type + +Each of these declaration forms creates a new type name. + +### Values + +As with types, you probably already understand what a value is. +Values are runtime names that we can reference in expressions. +For example `let x = 5;` creates a value called `x`. + +Again, being explicit, the following things create values: + +* `let`, `const`, and `var` declarations +* A `namespace` or `module` declaration which contains a value +* An `enum` declaration +* A `class` declaration +* An `import` declaration which refers to a value +* A `function` declaration + +### Namespaces + +Types can exist in *namespaces*. +For example, if we have the declaration `let x: A.B.C`, + we say that the type `C` comes from the `A.B` namespace. + +This distinction is subtle and important -- here, `A.B` is not necessarily a type or a value. + +## Simple Combinations: One name, multiple meanings + +Given a name `A`, we might find up to three different meanings for `A`: a type, a value or a namespace. +How the name is interpreted depends on the context in which it is used. +For example, in the declaration `let m: A.A = A;`, + `A` is used first as a namespace, then as a type name, then as a value. +These meanings might end up referring to entirely different declarations! + +This may seem confusing, but it's actually very convenient as long as we don't excessively overload things. +Let's look at some useful aspects of this combining behavior. + +### Built-in Combinations + +Astute readers will notice that, for example, `class` appeared in both the *type* and *value* lists. +The declaration `class C { }` creates two things: + a *type* `C` which refers to the instance shape of the class, + and a *value* `C` which refers to the constructor function of the class. +Enum declarations behave similarly. + +### User Combinations + +Let's say we wrote a module file `foo.d.ts`: + +```ts +export var SomeVar: { a: SomeType }; +export interface SomeType { + count: number; +} +``` + +Then consumed it: + +```ts +import * as foo from './foo'; +let x: foo.SomeType = foo.SomeVar.a; +console.log(x.count); +``` + +This works well enough, but we might imagine that `SomeType` and `SomeVar` were very closely related + such that you'd like them to have the same name. +We can use combining to present these two different objects (the value and the type) under the same name `Bar`: + +```ts +export var Bar: { a: Bar }; +export interface Bar { + count: number; +} +``` + +This presents a very good opportunity for destructuring in the consuming code: + +```ts +import { Bar } from './foo'; +let x: Bar = Bar.a; +console.log(x.count); +``` + +Again, we've used `Bar` as both a type and a value here. +Note that we didn't have to declare the `Bar` value as being of the `Bar` type -- they're independent. + +## Advanced Combinations + +Some kinds of declarations can be combined across multiple declarations. +For example, `class C { }` and `interface C { }` can co-exist and both contribute properties to the `C` types. + +This is legal as long as it does not create a conflict. +A general rule of thumb is that values always conflict with other values of the same name unless they are declared as `namespace`s, + types will conflict if they are declared with a type alias declaration (`type s = string`), + and namespaces never conflict. + +Let's see how this can be used. + +### Adding using an `interface` + +We can add additional members to an `interface` with another `interface` declaration: + +```ts +interface Foo { + x: number; +} +// ... elsewhere ... +interface Foo { + y: number; +} +let a: Foo = ...; +console.log(a.x + a.y); // OK +``` + +This also works with classes: + +```ts +class Foo { + x: number; +} +// ... elsewhere ... +interface Foo { + y: number; +} +let a: Foo = ...; +console.log(a.x + a.y); // OK +``` + +Note that we cannot add to type aliases (`type s = string;`) using an interface. + +### Adding using a `namespace` + +A `namespace` declaration can be used to add new types, values, and namespaces in any way which does not create a conflict. + +For example, we can add a static member to a class: + +```ts +class C { +} +// ... elsewhere ... +namespace C { + export let x: number; +} +let y = C.x; // OK +``` + +Note that in this example, we added a value to the *static* side of `C` (its constructor function). +This is because we added a *value*, and the container for all values is another value + (types are contained by namespaces, and namespaces are contained by other namespaces). + +We could also add a namespaced type to a class: + +```ts +class C { +} +// ... elsewhere ... +namespace C { + export interface D { } +} +let y: C.D; // OK +``` + +In this example, there wasn't a namespace `C` until we wrote the `namespace` declaration for it. +The meaning `C` as a namespace doesn't conflict with the value or type meanings of `C` created by the class. + +Finally, we could perform many different merges using `namespace` declarations. +This isn't a particularly realistic example, but shows all sorts of interesting behavior: + +```ts +namespace X { + export interface Y { } + export class Z { } +} + +// ... elsewhere ... +namespace X { + export var Y: number; + export namespace Z { + export class C { } + } +} +type X = string; +``` + +In this example, the first block creates the following name meanings: + +* A value `X` (because the `namespace` declaration contains a value, `Z`) +* A namespace `X` (because the `namespace` declaration contains a type, `Y`) +* A type `Y` in the `X` namespace +* A type `Z` in the `X` namespace (the instance shape of the class) +* A value `Z` that is a property of the `X` value (the constructor function of the class) + +The second block creates the following name meanings: + +* A value `Y` (of type `number`) that is a property of the `X` value +* A namespace `Z` +* A value `Z` that is a property of the `X` value +* A type `C` in the `X.Z` namespace +* A value `C` that is a property of the `X.Z` value +* A type `X` + +## Using with `export =` or `import` + +An important rule is that `export` and `import` declarations export or import *all meanings* of their targets. + + diff --git a/pages/declaration files/Do's and Don'ts.md b/pages/declaration files/Do's and Don'ts.md new file mode 100644 index 000000000..b8bc129f4 --- /dev/null +++ b/pages/declaration files/Do's and Don'ts.md @@ -0,0 +1,224 @@ +# General Types + +## `Number`, `String`, `Boolean`, and `Object` + +*Don't* ever use the types `Number`, `String`, `Boolean`, or `Object`. +These types refer to non-primitive boxed objects that are almost never used appropriately in JavaScript code. + +```ts +/* WRONG */ +function reverse(s: String): String; +``` + +*Do* use the types `number`, `string`, and `boolean`. + +```ts +/* OK */ +function reverse(s: string): string; +``` + +If you're tempted to use the type `Object`, consider using `any` instead. +There is currently no way in TypeScript to specify an object that is "not a primitive". + + +## Generics + +*Don't* ever have a generic type which doesn't use its type parameter. +See more details in [TypeScript FAQ page](https://github.com/Microsoft/TypeScript/wiki/FAQ#why-doesnt-type-inference-work-on-this-interface-interface-foot---). + + + +# Callback Types + +## Return Types of Callbacks + + + +*Don't* use the return type `any` for callbacks whose value will be ignored: + +```ts +/* WRONG */ +function fn(x: () => any) { + x(); +} +``` + +*Do* use the return type `void` for callbacks whose value will be ignored: + +```ts +/* OK */ +function fn(x: () => void) { + x(); +} +``` + +*Why*: Using `void` is safer because it prevents you from accidently using the return value of `x` in an unchecked way: + +```ts +function fn(x: () => void) { + var k = x(); // oops! meant to do something else + k.doSomething(); // error, but would be OK if the return type had been 'any' +} +``` + +## Optional Parameters in Callbacks + +*Don't* use optional parameters in callbacks unless you really mean it: + +```ts +/* WRONG */ +interface Fetcher { + getObject(done: (data: any, elapsedTime?: number) => void): void; +} +``` + +This has a very specific meaning: the `done` callback might be invoked with 1 argument or might be invoked with 2 arguments. +The author probably intended to say that the callback might not care about the `elapsedTime` parameter, + but there's no need to make the parameter optional to accomplish this -- + it's always legal to provide a callback that accepts fewer arguments. + +*Do* write callback parameters as non-optional: + +```ts +/* OK */ +interface Fetcher { + getObject(done: (data: any, elapsedTime: number) => void): void; +} +``` + +## Overloads and Callbacks + +*Don't* write separate overloads that differ only on callback arity: + +```ts +/* WRONG */ +declare function beforeAll(action: () => void, timeout?: number): void; +declare function beforeAll(action: (done: DoneFn) => void, timeout?: number): void; +``` + +*Do* write a single overload using the maximum arity: + +```ts +/* OK */ +declare function beforeAll(action: (done: DoneFn) => void, timeout?: number): void; +``` + +*Why*: It's always legal for a callback to disregard a parameter, so there's no need for the shorter overload. +Providing a shorter callback first allows incorrectly-typed functions to be passed in because they match the first overload. + +# Function Overloads + +## Ordering + +*Don't* put more general overloads before more specific overloads: + +```ts +/* WRONG */ +declare function fn(x: any): any; +declare function fn(x: HTMLElement): number; +declare function fn(x: HTMLDivElement): string; + +var myElem: HTMLDivElement; +var x = fn(myElem); // x: any, wat? +``` + +*Do* sort overloads by putting the more general signatures after more specific signatures: + +```ts +/* OK */ +declare function fn(x: HTMLDivElement): string; +declare function fn(x: HTMLElement): number; +declare function fn(x: any): any; + +var myElem: HTMLDivElement; +var x = fn(myElem); // x: string, :) +``` + +*Why*: TypeScript chooses the *first matching overload* when resolving function calls. +When an earlier overload is "more general" than a later one, the later one is effectively hidden and cannot be called. + +## Use Optional Parameters + +*Don't* write several overloads that differ only in trailing parameters: + +```ts +/* WRONG */ +interface Moment { + diff(b: MomentComparable): number; + diff(b: MomentComparable, unitOfTime: string): number; + diff(b: MomentComparable, unitOfTime: string, round: boolean): number; +} +``` + +*Do* use optional parameters whenever possible: + +```ts +/* OK */ +interface Moment { + diff(b: MomentComparable, unitOfTime?: string, round?: boolean): number; +} +``` + +Note that this collapsing should only occur when all overloads have the same return type. + +*Why*: This is important for two reasons. + +TypeScript resolves signature compatibility by seeing if any signature of the target can be invoked with the arguments of the source, + *and extraneuous arguments are allowed*. +This code, for example, exposes a bug only when the signature is correctly written using optional parameters: + +```ts +function fn(x: (a: string, b: number, c: number) => void) { } +var x: Moment; +// When written with overloads, OK -- used first overload +// When written with optionals, correctly an error +fn(x.diff); +``` + +The second reason is when a consumer uses the "strict null checking" feature of TypeScript. +Because unspecified parameters appear as `undefined` in JavaScript, it's usually fine to pass an explicit `undefined` to a function with optional arguments. +This code, for example, should be OK under strict nulls: + +```ts +var x: Moment; +// When written with overloads, incorrectly an error because of passing 'undefined' to 'string' +// When written with optionals, correctly OK +x.diff(something, someOtherThing ? undefined : "hour"); +``` + +## Use Union Types + +*Don't* write overloads that differ by type in only one argument position: + +```ts +/* WRONG */ +interface Moment { + utcOffset(): number; + utcOffset(b: number): Moment; + utcOffset(b: string): Moment; +} +``` + +*Do* use union types whenver possible: + +```ts +/* OK */ +interface Moment { + utcOffset(): number; + utcOffset(b: number|string): Moment; +} +``` + +Note that we didn't make `b` optional here because the return types of the signatures differ. + +*Why*: This is important for people who are "passing through" a value to your function: + +```ts +function fn(x: string): void; +function fn(x: number): void; +function fn(x: number|string) { + // When written with separate overloads, incorrectly an error + // When written with union types, correctly OK + return moment().utcOffset(x); +} +``` \ No newline at end of file diff --git a/pages/declaration files/Introduction.md b/pages/declaration files/Introduction.md new file mode 100644 index 000000000..de3cc423d --- /dev/null +++ b/pages/declaration files/Introduction.md @@ -0,0 +1,49 @@ +This guide is designed to teach you how to write a high-quality TypeScript Declaration File. + +In this guide, we'll assume basic familiarity with the TypeScript language. +If you haven't already, you should read the [TypeScript Handbook](https://www.typescriptlang.org/docs/handbook/basic-types.html) + to familiarize yourself with basic concepts, especially types and namespaces. + +# Sections + +The guide is broken down into the following sections. + +## Library Structures + +The [Library Structures](./Library Structures.md) guide helps you understand common library formats and how to write a correct declaration file for each format. +If you're editing an existing file, you probably don't need to read this section. +Authors of new declaration files must read this section to properly understand how the format of the library influences the writing of the declaration file. + +## "Do"s and "Don't"s + +Many common mistakes in declaration files can be easily avoided. +The [Do's and Don'ts](./Do's and Don'ts.md) section identifies common errors, + desscribes how to detect them, + and how to fix them. +Everyone should read this section to help themselves avoid common mistakes. + +## By Example + +Many times, we are faced with writing a declaration file when we only have examples of the underlying library to guide us. +The [By Example](./By Example.md) section shows many common API patterns and how to write declarations for each of them. +This guide is aimed at the TypeScript novice who may not yet be familiar with every language construct in TypeScript. + +## Deep Dive + +For seasoned authors interested in the underlying mechanics of how declaration files work, + the [Deep Dive](./Deep Dive.md) section explains many advanced concepts in declaration writing, + and shows how to leverage these concepts to create cleaner and more intuitive declaration files. + +## `/templates` + +In the `templates` directory, you'll find a number of declaration files that serve as a useful starting point + when writing a new file. +Refer to the documentation in [Library Structures](./Library Structures.md) to figure out which template file to use. + +## Publish to npm + +The [Publishing](./Publishing.md) section explains how to publish your declaration files to an npm package, and shows how to manage your dependent packages. + +## Find and Install Declaration Files + +For JavaScript library users, the [Consumption](./Consumption.md) section offers a few simple steps to locate and install corresponding declaration files. diff --git a/pages/declaration files/Library Structures.md b/pages/declaration files/Library Structures.md new file mode 100644 index 000000000..414e97fa5 --- /dev/null +++ b/pages/declaration files/Library Structures.md @@ -0,0 +1,364 @@ +# Overview + +Broadly speaking, the way you *structure* your declaration file depends on how the library is consumed. +There are many ways of offering a library for consumption in JavaScript, and you'll need to write your declaration file to match it. +This guide covers how to identify common library patterns, and how to write declaration files which correspond to that pattern. + +Each type of major library structuring pattern has a corresponding file in the [`templates`](./templates) directory. +You can start with these templates to help you get going faster. + +# Identifying Kinds of Libraries + +First, we'll review the kinds of libraries TypeScript declaration files can represent. +We'll briefly show how each kind of library is *used*, how it is *written*, and list some example libraries from the real world. + +Identifying the structure of a library is the first step in writing its declaration file. +We'll give hints on how to identify structure both based on its *usage* and its *code*. +Depending on the library's documentation and organization, one might be easier than the other. +We recommend using whichever is more comfortable to you. + +## Global Libraries + +A *global* library is one that can be accessed from the global scope (i.e. without using any form of `import`). +Many libraries simply expose one or more global variables for use. +For example, if you were using [jQuery](https://jquery.com/), the `$` variable can be used by simply referring to it: + +```ts +$(() => { console.log('hello!'); } ); +``` + +You'll usually see guidance in the documentation of a global library of how to use the library in an HTML script tag: + +```html + +``` + +Today, most popular globally-accessible libraries are actually written as UMD libraries (see below). +UMD library documentation is hard to distinguish from global library documentation. +Before writing a global declaration file, make sure the library isn't actually UMD. + +### Identifying a Global Library from Code + +Global library code is usually extremely simple. +A global "Hello, world" library might look like this: + +```js +function createGreeting(s) { + return "Hello, " + s; +} +``` + +or like this: + +```js +window.createGreeting = function(s) { + return "Hello, " + s; +} +``` + +When looking at the code of a global library, you'll usually see: + +* Top-level `var` statements or `function` declarations +* One or more assignments to `window.someName` +* Assumptions that DOM primitives like `document` or `window` exist + +You *won't* see: + +* Checks for, or usage of, module loaders like `require` or `define` +* CommonJS/Node.js-style imports of the form `var fs = require("fs");` +* Calls to `define(...)` +* Documentation describing how to `require` or import the library + +### Examples of Global Libraries + +Because it's usually easy to turn a global library into a UMD library, very few popular libraries are still written in the global style. +However, libraries that are small and require the DOM (or have *no* dependencies) may still be global. + +### Global Library Template + +The template file [`global.d.ts`](./templates/global.d.ts) defines an example library `myLib`. +Be sure to read the ["Preventing Name Conflicts" footnote](#preventing-name-conflicts). + +## Modular Libraries + +Some libraries only work in a module loader environment. +For example, because `express` only works in Node.js and must be loaded using the CommonJS `require` function. + +ECMAScript 2015 (also known as ES2015, ECMAScript 6, and ES6), CommonJS, and RequireJS have similar notions of *importing* a *module*. +In JavaScript CommonJS (Node.js), for example, you would write + +```ts +var fs = require("fs"); +``` + +In TypeScript or ES6, the `import` keyword serves the same purpose: + +```ts +import fs = require("fs"); +``` + +You'll typically see modular libraries include one of these lines in their documentation: + +```js +var someLib = require('someLib'); +``` + +or + +```ts +define(..., ['someLib'], function(someLib) { + +}); +``` + +As with global modules, you might see these examples in the documentation of a UMD module, so be sure to check the code or documentation. + +### Identifying a Module Library from Code + +Modular libraries will typically have at least some of the following: + +* Unconditional calls to `require` or `define` +* Declarations like `import * as a from 'b';` or `export c;` +* Assignments to `exports` or `module.exports` + +They will rarely have: + +* Assignments to properties of `window` or `global` + +### Examples of Modular Libraries + +Many popular Node.js libraries are in the module family, such as [`express`](http://expressjs.com/), [`gulp`](http://gulpjs.com/), and [`request`](https://github.com/request/request). + +## *UMD* + +A *UMD* module is one that can *either* be used as module (through an import), or as a global (when run in an environment without a module loader). +Many popular libraries, such as [Moment.js](http://momentjs.com/), are written this way. +For example, in Node.js or using RequireJS, you would write: + +```ts +import moment = require("moment"); +console.log(moment.format()); +``` + +whereas in a vanilla browser environment you would write: + +```ts +console.log(moment.format()); +``` + +### Identifying a UMD library + +[UMD modules](https://github.com/umdjs/umd) check for the existence of a module loader environment. +This is an easy-to-spot pattern that looks something like this: + +```js +(function (root, factory) { + if (typeof define === "function" && define.amd) { + define(["libName"], factory); + } else if (typeof module === "object" && module.exports) { + module.exports = factory(require("libName")); + } else { + root.returnExports = factory(root.libName); + } +}(this, function (b) { +``` + +If you see tests for `typeof define`, `typeof window`, or `typeof module` in the code of a library, especially at the top of the file, it's almost always a UMD library. + +Documentation for UMD libraries will also often demonstrate a "Using in Node.js" example showing `require`, + and a "Using in the browser" example showing using a `