TypeScript was conceived as a superset to JavaScript that provides type-checking at design-time.
TypeScript code is made up of a declaration and definition
declare const map: { [ key: string ]: string };
const map = {
one: 1,
two: 2
};
class {
one: number;
two: number;
constructor(one = 1, two = 2) {
this.one = one;
this.two = two;
}
}
Classes are one part interface one part implementation
Declarations have zero impact on compiled TypeScript code.
The TypeScript compiler erases all typing information after building your project.
Sooner or later, your application will depend on other code that is:
Ambient declarations describe the public API of external libraries
interface JQueryStatic {
/**
* Accepts a string containing a CSS selector which is then used to match a set of elements.
*
* @param element A DOM element to wrap in a jQuery object.
*/
(element: string): JQuery;
};
interface JQuery {
click(callback: (event: any) => void): JQuery;
};
Modules that are loaded via a script tag or are loaded in the global space can be declared as a named variable
declare var jQuery: JQueryStatic;
when components are loaded (like AMD or Node.js) modules can be declared with the module id
declare module 'dojo/require' {
const require: dojo.RequirePlugin;
export = require;
}
Definitely Typed is the original typings repository
Typings is the popular CLI tool for working with ambient declaration repositories. It creates a typings.json file used to install and manage typings for your project.
@types publishes ambient declarations from Definitely Typed to npm
npm install @types/node --save
By default TypeScript includes all declarations in the @types directory.
You can also configure it yourself.
A reference to an declaration file can be declared in a special comment
///<reference path="jquery.d.ts"/>
Ambient declarations can be explicitly included in your project
{
"version": "2.1.0",
"compilerOptions": { },
"include": [
"./app/src/**/*.ts"
],
"files": [
"./node_modules/dojo-typings/dojo/1.11/index.d.ts",
"./node_modules/dojo-typings/dojo/1.11/modules.d.ts",
"./node_modules/dojo-typings/dijit/1.11/index.d.ts",
"./node_modules/dojo-typings/dijit/1.11/modules.d.ts",
"./node_modules/dojo-typings/custom/dgrid/1.1/dgrid.d.ts",
"./node_modules/dojo-typings/custom/dstore/1.1/dstore.d.ts"
]
}
TypeScript will now generate ambient declarations for your project with the declaration compiler option
{
"version": "2.1.0",
"compilerOptions": {
declaration: true,
declarationDir: "./dist/typings"
},
"include": [
"./app/src/**/*.ts"
]
}
Definitely Typed recommends writing a small implementation that exercises your declarations
Tests are an even better way to exercise declarations
For dojo typings, we converted our Intern tests to use ESM style imports and then built against our ambient declarations
export interface FixedEventedConstructor {
new (params?: Object): dojo.Evented;
}
export const FixedEvented: FixedEventedConstructor = <any> Evented;
export default class extends FixedEvented {
// ...
}
Evented's constructor was incorrectly declared so we fixed it in our project
// from old.version.d.ts
interface MyFactory {
builder() => any;
}
// add a missing method
interface MyFactory {
parser(value: any) => any;
}
Sometimes only typings for older versions of a library are available. When multiple versions of an interface are present TypeScript will attempt to merge them together.
interface Screen extends _WidgetBase {
set(name: 'model', value: Model): this;
get(name: 'value'): Data;
set(name: 'value', value: Data): this;
// holla-back methods
get(name: string): any;
set(name: string, value: any): this;
set(name: Object): this;
}
declare module 'dojo/_base/Deferred' {
type Deferred<T> = dojo._base.Deferred<T>;
const Deferred: dojo._base.DeferredConstructor;
export = Deferred;
}
TypeScript will merge declarations and interfaces with the same name and infer its usage like a class
import * as declare from 'dojo/_base/declare';
export default function (... mixins: Object[]): ClassDecorator {
return function (target: Function) {
return declare(mixins, target.prototype);
};
}
We wanted to use classes in TypeScript with the Dojo Toolkit
interface Screen extends _WidgetBase, _TemplatedMixin, _Container {
set(name: 'model', value: Model): this;
set(name: string, value: any): this;
set(name: Object): this;
}
@declare(_WidgetBase, _TemplatedMixin, _Container)
class Screen {
model?: Model;
}
The decorator extends the class, the interface declares the types it extends, and TypeScript merges it with the class interface