As a superset of JavaScript, TypeScript inevitably references other third-party JavaScript libraries during the development process. While library classes and methods can be called by direct reference, TypeScript features such as type checking are not available. To solve this problem, we need to remove the function and method bodies of these libraries and keep only the export type declarations. Instead, we produce a declaration file that describes the JavaScript library and module information. By referring to the declaration file, you can borrow TypeScript features to use library files.

Declarative statement concept

If you want to use the third-party library jQuery, a common way to do this is to introduce jQuery through the

We usually get an element with id foo like this:

$('#foo');
// or
jQuery('#foo');
Copy the code

But in TS, the compiler doesn’t know what $or jQuery is:

jQuery('#foo');
// ERROR: Cannot find name 'jQuery'.
Copy the code

In this case, declare var is used to define the type:

declare var jQuery: (selector: string) = > any;
​
jQuery('#foo');
Copy the code

In the example above, Declare var does not actually define a variable. Instead, it defines the type of the jQuery global variable, which is used only for compile-time checks. The result of compiling:

jQuery('#foo');
Copy the code

Declaration file

Put declarations in a separate file (*.d.ts). This is the declaration file:

// src/jQuery.d.ts
declare var jQuery: (selector: string) = > any;
​
// src/index.ts
jQuery('#foo');
Copy the code

The declaration file is required.d.tsFor the suffix.

In general, ts parses all *. Ts files in a project, including files ending in.d.ts. So when you put jquery.d. ts into your project, all other *. Ts files will get the jQuery type definition.

The/path/to/project ├ ─ ─ the SRC | ├ ─ ─ but ts | └ ─ ─ jQuery. Which s └ ─ ─ tsconfig. JsonCopy the code

If you still can’t parse it, check the files, include, and exclude configurations in tsconfig.json to make sure it includes the jQuery.d.ts file.

Third Party Statement

It is recommended to use @types to centrally manage the declaration files of third-party libraries.

You can install the declaration module for @types with NPM, as shown in jQuery:

npm install @types/jquery --save-dev
Copy the code

Writing declaration

When a third-party library does not provide a declaration file, you need to write your own. The content and usage of the declaration file can vary in different scenarios.

The library can be used in the following scenarios:

  • Global variables: Passed<script>Tag to import third-party libraries and inject global variables
  • NPM package: Passedimport foo from 'foo'Import, which complies with ES6 module specifications
  • UMD library: Both can pass<script>Tags can be introduced, and can be passedimportThe import
  • Extend global variables directly: pass<script>Tags are introduced to change the structure of a global variable
  • Extending global variables in an NPM package or UMD library: Change the structure of a global variable after referencing an NPM package or UMD library
  • Module plug-in: Passed<script>importAfter import, change the structure of the other module

The global variable

When using global variable declaration files, no configuration is required if installed with NPM install @types/ XXX –save-dev. If you want to save the declaration file directly in the current project, it is recommended to put the rest of the source code in the SRC directory (or the corresponding source directory) :

The/path/to/project ├ ─ ─ the SRC | ├ ─ ─ but ts | └ ─ ─ jQuery. Which s └ ─ ─ tsconfig. JsonCopy the code

If not, check tsconfig.json for files, include, and exclude to make sure it includes the jQuery.d.ts file.

The declaration file for global variables has the following syntax:

  • declare varDeclaring a global variable
  • declare functionDeclaring global methods
  • declare classDeclaring a global class
  • declare enumDeclare a global enumeration type
  • declare namespaceDeclares a global object (with child properties)
  • interfacetypeDeclaring a global type

declare var

Declare var defines the type of a global variable. Declare let and declare const; let is no different from var:

// src/jQuery.d.ts
declare let jQuery: (selector: string) = > any;
// src/index.ts
jQuery('#foo');
// Use the jQuery type defined by Declare let to modify the global variable
jQuery = function(selector) {
    return document.querySelector(selector);
};
Copy the code

If we use const, we are not allowed to change the value of the global variable:

// src/jQuery.d.ts
declare const jQuery: (selector: string) = > any;
jQuery('#foo');
// The jQuery type defined with Declare const is not allowed to modify the global variable
jQuery = function(selector) {
    return document.querySelector(selector);
};
// ERROR: Cannot assign to 'jQuery' because it is a constant or a read-only property.
Copy the code

In general, global variables are non-modifiable constants, so you should use const in most cases.

Note that only the type can be defined in a declaration. Do not define the implementation in a declaration:

declare const jQuery = function(selector) {
    return document.querySelector(selector);
};
// ERROR: An implementation cannot be declared in ambient contexts.
Copy the code

declare function

Declare function Defines the type of a global function. JQuery = function (); jQuery = function ();

// src/jQuery.d.ts
declare function jQuery(selector: string) :any;
// src/index.ts
jQuery('#foo');
Copy the code

Function overloading is also supported in declarations of function types:

// src/jQuery.d.ts
declare function jQuery(selector: string) :any;
declare function jQuery(domReadyCallback: () => any) :any;
// src/index.ts
jQuery('#foo');
jQuery(function() {
    alert('Dom Ready! ');
});
Copy the code

declare class

When a global variable is a class, use Declare class to define its type:

// src/Animal.d.ts
declare class Animal {
    name: string;
    constructor(name: string);
    sayHi(): string;
}
// src/index.ts
let cat = new Animal('Tom');
Copy the code

Similarly, declare class statements can only be used to define types. They cannot be used to define implementations. For example, to define implementations of the sayHi method, an error will be reported:

// src/Animal.d.ts
declare class Animal {
    name: string;
    constructor(name: string);
    sayHi() {
        return `My name is The ${this.name}`;
    };
    // ERROR: An implementation cannot be declared in ambient contexts.
}
Copy the code

declare enum

An enumeration type defined with declare enum is also called an Ambient Enums. For example:

// src/Directions.d.ts
declare enum Directions {
    Up,
    Down,
    Left,
    Right
}
// src/index.ts
let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];
Copy the code

As with other global variable type declarations, declare enum defines only the type, not the value.

Directions. D. ts will only be used for compile-time checks, and the contents of the declaration file will be deleted in the compile-result. It compiles to:

var directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];
Copy the code

Directions is a global variable defined by a third-party library.

declare namespace

Namespace is a keyword created in the early days of TS to solve the problem of modularity.

For historical reasons, in the early days before ES6, TS provided a modular approach, using the module keyword to represent internal modules. However, since ES6 also used the module keyword later, TS replaced its module with namespace to be compatible with ES6 and changed its name to namespace.

With the wide application of ES6, it is no longer recommended to use namespace in TS. Instead, it is recommended to use the modular solution of ES6. Therefore, it is no longer necessary to learn how to use namespace.

Namespaces are obsolete, but in declare files, declare namespace is more common. It is used to indicate that the global variable is an object with many sub-properties.

If jQuery is a global variable and it is an object that provides a jQuery. Ajax method to call, then use Declare Namespace jQuery to declare the global variable with multiple child attributes.

// src/jQuery.d.ts
declare namespace jQuery {
    function ajax(url: string, settings? :any) :void;
}
// src/index.ts
jQuery.ajax('/api/get_something');
Copy the code

Note that instead of using Declare Function Ajax, you declare functions directly inside Declare Namespace. Similarly, we can use const, class, enum, and so on:

// src/jQuery.d.ts
declare namespace jQuery {
    function ajax(url: string, settings? :any) :void;
    const version: number;
    class Event {
        blur(eventType: EventType): void
    }
    enum EventType {
        CustomClick
    }
}
// src/index.ts
jQuery.ajax('/api/get_something');
console.log(jQuery.version);
const e = new jQuery.Event();
e.blur(jQuery.EventType.CustomClick);
Copy the code

Nested namespaces

If the object has a deep hierarchy, you need to use nested namespaces to declare the types of the deep attributes:

// src/jQuery.d.ts
declare namespace jQuery {
    function ajax(url: string, settings? :any) :void;
    namespace fn {
        function extend(object: any) :void; }}// src/index.ts
jQuery.ajax('/api/get_something');
jQuery.fn.extend({
    check: function() {
        return this.each(function() {
            this.checked = true; }); }});Copy the code

If fn is the only property in jQuery (there are no other properties or methods such as Ajax), you don’t need to nest a namespace:

// src/jQuery.d.ts
declare namespace jQuery.fn {
    function extend(object: any) :void;
}
// src/index.ts
jQuery.fn.extend({
    check: function() {
        return this.each(function() {
            this.checked = true; }); }});Copy the code

interfacetype

In the type declaration file, you can declare a global interface or type directly using interface or type:

// src/jQuery.d.ts
interfaceAjaxSettings { method? :'GET' | 'POST'data? :any;
}
declare namespace jQuery {
    function ajax(url: string, settings? : AjaxSettings) :void;
}
Copy the code

In this case, you can use this interface or type in other files as well:

// src/index.ts
let settings: AjaxSettings = {
    method: 'POST'.data: {
        name: 'foo'}}; jQuery.ajax('/api/post_something', settings);
Copy the code

Type is similar to interface.

Preventing name conflicts

The interface or type exposed in the outermost layer will act as a global type throughout the project, and the number of global variables or global types should be reduced as much as possible. So it’s best to put them in a namespace:

// src/jQuery.d.ts
declare namespace jQuery {
    interfaceAjaxSettings { method? :'GET' | 'POST'data? :any;
    }
    function ajax(url: string, settings? : AjaxSettings) :void;
}
Copy the code

Note that when using this interface, you should also prefix it with jQuery:

// src/index.ts
let settings: jQuery.AjaxSettings = {
    method: 'POST'.data: {
        name: 'foo'}}; jQuery.ajax('/api/post_something', settings);
Copy the code

A statement to merge

If jQuery is both a function that can be called directly to jQuery(‘#foo’) and an object that has child attributes like jquery.ajax () (which it is), then we can combine multiple declarations and they will merge without conflict:

// src/jQuery.d.ts
declare function jQuery(selector: string) :any;
declare namespace jQuery {
    function ajax(url: string, settings? :any) :void;
}
// src/index.ts
jQuery('#foo');
jQuery.ajax('/api/get_something');
Copy the code

NPM package

It is common to import an NPM package by importing foo from ‘foo’, which conforms to the ES6 module specification.

Before creating a declaration file for an NPM package, you need to see if its declaration file already exists. In general, NPM package declaration files may exist in two places:

  • Is bound to the NPM package. Judging by the fact thatpackage.jsonThere aretypesField, or there is oneindex.d.tsDeclaration file. This mode does not require additional packages to be installed and is the most recommended, so when you create your own NPM package, it is best to bind the declaration file to the NPM package as well.
  • Release to@typesIn the water. You need to try to install the corresponding@typesThe package knows if the declaration file exists, and the install command isnpm install @types/foo --save-dev. This pattern is usually due to the fact that the NPM package maintainer does not provide the declaration file, so someone else has to publish the declaration file to@types.

If you can’t find a corresponding declaration file either way, you’ll need to write your own declaration file for it. Since the module is imported through the import statement, the location of the declaration file is also limited. Generally, there are two solutions:

  • To create anode_modules/@types/foo/index.d.tsFile, storefooModule declaration file. This approach requires no additional configuration, howevernode_modulesThe directory is not stable, the code is not stored in the repository, there is no traceable version, there is a risk of accidental deletion, so this scheme is not recommended, usually only for temporary testing.
  • To create atypesDirectory for managing self-written declaration files, willfooIn the declaration filetypes/foo/index.d.tsIn the. This method needs to be configuredtsconfig.jsonIn thepathsbaseUrlField.

Directory structure:

/ path/to/project ├ ─ ─ the SRC | └ ─ ─ but ts ├ ─ ─ types | └ ─ ─ foo | └ ─ ─ the index, which s └ ─ ─ tsconfig. JsonCopy the code

Tsconfig. Json content:

{
    "compilerOptions": {
        "module": "commonjs"."baseUrl": ". /"."paths": {
            "*": ["types/*"]}}}Copy the code

After this configuration, when importing foo via import, you will also go to the types directory to find the corresponding module declaration file.

Note that there are many options for the Module configuration, which affect the import/export mode of the module. The commonJS option is used here and will be used by default throughout the rest of the tutorial.

The NPM package declaration file has the following syntax:

  • exportExport variables
  • export namespaceExport an object (with child properties)
  • export defaultES6 exports by default
  • export =Commonjs export module

export

The NPM package declaration file is very different from the global variable declaration file. In the NPM package declaration file, using Declare no longer declares a global variable, but only a local variable in the current file. These type declarations are applied only when the export export is used in the declaration file and then imported by the user import.

The syntax of export is similar to that of normal TS, except that the declaration file forbids the definition of an implementation:

// types/foo/index.d.tsexport const name: string;
export function getName() :string;
export class Animal {
    constructor(name: string);
    sayHi(): string;
}
export enum Directions {
    Up,
    Down,
    Left,
    Right
}
export interface Options {
    data: any;
}
Copy the code

The corresponding import and use modules should look like this:

// src/index.tsimport { name, getName, Animal, Directions, Options } from 'foo';
​
console.log(name);
let myName = getName();
let cat = new Animal('Tom');
let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];
let options: Options = {
    data: {
        name: 'foo'}};Copy the code

A mixture ofdeclareexport

You can also use DECLARE to declare multiple variables and export them once. The above example can be rewritten as:

// types/foo/index.d.tsdeclare const name: string;
declare function getName() :string;
declare class Animal {
    constructor(name: string);
    sayHi(): string;
}
declare enum Directions {
    Up,
    Down,
    Left,
    Right
}
interface Options {
    data: any;
}
​
export { name, getName, Animal, Directions, Options };
Copy the code

Note that, similar to global variable declaration files, no declare is required before interface.

export namespace

Similar to declare namespace, export namespace is used to export an object that has child properties:

// types/foo/index.d.tsexport namespace foo {
    const name: string;
    namespace bar {
        function baz() :string; }}// src/index.tsimport { foo } from 'foo';
​
console.log(foo.name);
foo.bar.baz();
Copy the code

export default

In the ES6 module system, export Default is used to export a default value. The user can import the default value by importing foo from ‘foo’ instead of importing {foo} from ‘foo’.

In the type declaration file, export default is used to export the default type:

// types/foo/index.d.tsexport default function foo() :string;
// src/index.tsimport foo from 'foo';
​
foo();
Copy the code

Note that only function, class, and interface can be exported by default. Other variables need to be defined first and then exported by default:

// types/foo/index.d.tsexport default enum Directions {
// ERROR: Expression expected.
    Up,
    Down,
    Left,
    Right
}
Copy the code

Declare enum; export default enum; declare enum; export default;

// types/foo/index.d.tsdeclare enum Directions {
    Up,
    Down,
    Left,
    Right
}
​
export default Directions;
Copy the code

For this default export, it is common to place the export statement first in the declaration file:

// types/foo/index.d.tsexport default Directions;
​
declare enum Directions {
    Up,
    Down,
    Left,
    Right
}
Copy the code

export =

In the CommonJS specification, export a module as follows:

// Export as a whole
module.exports = foo;
// Single export
exports.bar = bar;
Copy the code

In TS, there are several ways to import such a module export. The first is const… = the require:

// Import the whole
const foo = require('foo');
// Single import
const bar = require('foo').bar;
Copy the code

The second way is to import… From, note that for the overall export, import * as is required:

// Import the whole
import * as foo from 'foo';
// Single import
import { bar } from 'foo';
Copy the code

The third way is to import… Require, this is how TS officially recommends it:

// Import the whole
import foo = require('foo');
// Single import
import bar = foo.bar;
Copy the code

If you want to write a type declaration for a library that uses the CommonJS specification, you need to use the export = syntax:

// types/foo/index.d.tsexport = foo;
​
declare function foo() :string;
declare namespace foo {
    const bar: number;
}
Copy the code

Note that export {bar} cannot be exported individually after export = is used in the example above. So by declaring merge, use declare namespace foo to merge bar into foo.

Specifically, export = can be used not only in declaration files, but also in normal TS files. In fact, import… Require and export = are new syntaxes created by TS for compatibility with AMD and CommonJS specifications, and are not recommended because they are not commonly used.

Since many third-party libraries are in the CommonJS specification, the declaration file has to use the export = syntax. However, it needs to be emphasized again that ES6 standard export Default and export are more recommended than export =.

UMD library

The UMD is called the Universal Module Definition. Libraries that can be imported via either the

export as namespace

Generally, when using export as Namespace, you can declare a declared variable as a global variable by adding an export as Namespace statement based on the NPM package declaration file, as shown in the following example:

// types/foo/index.d.tsexport as namespace foo;
export = foo;
​
declare function foo(): string;
declare namespace foo {
    const bar: number;
}
Copy the code

It can also be used with export Default:

// types/foo/index.d.tsexport as namespace foo;
export default foo;
​
declare function foo(): string;
declare namespace foo {
    const bar: number;
}
Copy the code

Extend global variables directly

If a third-party library extends a global variable, but the type of the global variable has not been updated, it will cause a TS compilation error. In this case, you need to extend the type of the global variable. For example, extend String:

interface String {
    prependHello(): string;
}
​
'foo'.prependHello();
Copy the code

By declaring merge, you can use interface String to add attributes or methods to a String.

You can also add a type declaration to an existing namespace using declare namespace:

// types/jquery-plugin/index.d.tsdeclare namespace JQuery {
    interface CustomOptions {
        bar: string; }}interface JQueryStatic {
    foo(options: JQuery.CustomOptions): string;
}
// src/index.ts
​
jQuery.foo({
    bar: ' '
});
Copy the code

Extend global variables in the NPM package or UMD library

As mentioned earlier, for an NPM package or UMD library declaration file, only type declarations exported by export can be imported. So for an NPM package or UMD library, if the library is imported to extend global variables, you need to use a different syntax to extend global variables in the declaration file: Declare Global.

declare global

Use Declare Global to extend the type of a global variable in the NPM package or UMD library declaration file:

// types/foo/index.d.tsdeclare global {
    interface String {
        prependHello(): string; }}export {};
// src/index.ts'bar'.prependHello();
Copy the code

Note that even though this declaration file does not need to export anything, it still needs to export an empty object to tell the compiler that this is a module declaration file, not a global variable declaration file.

Module plug-ins

Sometimes by importing a module plug-in, you can change the structure of another existing module. At this point, if the original module already has a type declaration file, but the plug-in module does not have a type declaration file, the type will be incomplete and the type of the plug-in part will be missing. Ts provides a syntax Declare Module, which can be used to extend the type of the original module.

declare module

If you want to extend an existing module, you need to refer to the existing module in the type declaration file and then use declare Module to extend the existing module:

// types/moment-plugin/index.d.tsimport * as moment from 'moment';
​
declare module 'moment' {
    export function foo() :moment.CalendarKey;
}
// src/index.tsimport * as moment from 'moment';
import 'moment-plugin';
​
moment.foo();
Copy the code

Declare Module can also be used to declare multiple module types in a file at once:

// types/foo-bar.d.tsdeclare module 'foo' {
    export interface Foo {
        foo: string; }}declare module 'bar' {
    export function bar() :string;
}
// src/index.tsimport { Foo } from 'foo';
import * as bar from 'bar';
​
let f: Foo;
bar.bar();
Copy the code

Declare dependencies in a file

One declaration file sometimes depends on the types in another declaration file. For example, in the previous Declare Module example, we imported moment into the declaration file and used the moment.CalendarKey type:

// types/moment-plugin/index.d.tsimport * as moment from 'moment';
​
declare module 'moment' {
    export function foo() :moment.CalendarKey;
}
Copy the code

In addition to the ability to import types from another declaration file via import from a declaration file, another syntax that can be used to import another declaration file is the triple slash directive.

Three slash instruction

Like namespaces, the slash directive is a syntax created by TS in earlier versions to describe dependencies between modules. With the widespread use of ES6, it is no longer recommended to use the slash instruction in TS to declare dependencies between modules.

But in the declaration file, it still has some use.

Like import in a declaration file, it can be used to import another declaration file. The difference with import is that we need to use the slash directive instead of import if and only if:

  • When we write a global variable declaration file
  • When we need to rely on a global variable declaration file

Write a global variable declaration file

These scenarios may sound awkward, but they are actually easy to understand — the import and export keywords are not allowed in global variable declaration files. Once it is present, it is treated as an NPM package or UMD library and is no longer a global variable declaration file. So when we write a global variable declaration file that references another library type, we must use the slash directive:

// types/jquery-plugin/index.d.ts/// <reference types="jquery" />declare function foo(options: JQuery.AjaxSettings) :string;
// src/index.ts
​
foo({});
Copy the code

/// Add the dependency on the jquery type in the XML format, so you can use the jquery. AjaxSettings type in the declaration file.

Note that the triple slash directive must be placed at the very top of the file; only one or more lines of comments are allowed before the triple slash directive.

A declaration file that relies on a global variable

In another scenario, when we need to rely on a global variable declaration file, we will have to use the slash directive to import global variables because they are not supported by import:

// types/node-plugin/index.d.ts/// <reference types="node" />export function foo(p: NodeJS.Process) :string;
// src/index.tsimport { foo } from 'node-plugin';
​
foo(global.process);
Copy the code

In the above example, the node type is indicated by the three slants, and then the nodejs.process type is used in the declaration file. Finally, when using foo, the node global variable process is passed in.

Since the imported node types are global variable types, there is no way to import them through import, so in this case, the only way to import them is through the slash directive.

In both cases, the use of the triple slash directive is necessary because of the need to write or rely on global variable declaration files. In other cases where the three-slash directive is not necessary, import is required.

Split declaration file

When the declaration files for global variables are too large, you can improve the maintainability of your code by splitting them into multiple files and then importing them one by one in an entry file. For example, the jQuery declaration file looks like this:

// node_modules/@types/jquery/index.d.ts/// <reference types="sizzle" />
/// <reference path="JQueryStatic.d.ts" />
/// <reference path="JQuery.d.ts" />
/// <reference path="misc.d.ts" />
/// <reference path="legacy.d.ts" />export = jQuery;
Copy the code

There are two different types and path directives used. Types is used to declare a dependency on another library, while PATH is used to declare a dependency on another file.

In the example above, sizzle is another library parallel to jquery, so you need to declare a dependency on it using types=”sizzle”. The other three slash directives split the jquery declarations into different files and then import them in the entry file using path=”foo”.

Other three slash instructions

There are other triple slash directives, such as ///
, ///
, etc., but they are deprecated syntax, so I won’t cover them here.

Automatically generate a declaration file

If the source code of the library itself is written by TS, you can also generate.d.ts declaration files by adding the declaration option when compiling TS to JS using a TSC script.

You can add –declaration (-d) on the command line, or you can add the declaration option in tsconfig.json. Tsconfig. json is used as an example:

{
    "compilerOptions": {
        "module": "commonjs"."outDir": "lib"."declaration": true,}}Copy the code

In the previous example, the outDir option is added to output the compilation result of the TS file to the lib directory. Then, the declaration option is added, set to true, indicating that the. D. ts declaration file is automatically generated by the TS file and is also output to the lib directory.

After running TSC, the directory structure looks like this:

/ path/to/project ├ ─ ─ lib | ├ ─ ─ bar | | ├ ─ ─ the index, which s | | └ ─ ─ index. The js | ├ ─ ─ the index, which s | └ ─ ─ index. The js ├ ─ ─ the SRC | ├ ─ ─ the bar | | | └ ─ ─ but ts └ ─ ─ index. The ts ├ ─ ─ package. The json └ ─ ─ tsconfig. JsonCopy the code

In this example, there are two ts files in the SRC directory, SRC /index.ts and SRC /bar/index.ts, which are compiled into the lib directory. Two declaration files lib/index.d.ts and lib/bar/index.d.ts are also generated. Their contents are:

// src/index.tsexport * from './bar';
​
export default function foo() {
    return 'foo';
}
// src/bar/index.tsexport function bar() {
    return 'bar';
}
// lib/index.d.tsexport * from './bar';
export default function foo() :string;
// lib/bar/index.d.tsexport declare function bar() :string;
Copy the code

As you can see, the automatically generated declaration file basically maintains the structure of the source code, and the implementation is removed to generate the corresponding type declaration.

When you use TSC to automatically generate declaration files, there is a.d.ts declaration file for each TS file. The advantage of this is that the user can get a type prompt not only when importing the default module using import foo from ‘foo’, but also when importing a submodule using import bar from ‘foo/lib/bar’.

In addition to the declaration option, there are several other options that are associated with automatically generating declaration files, which are listed here rather than demonstrated in detail:

  • declarationDirSet up to generate.d.tsFile directory
  • declarationMapFor each.d.tsFile, generate the corresponding.d.ts.mapSourcemap file
  • emitDeclarationOnlyOnly generate.d.tsFile, not generated.jsfile

Release statement

Once you’ve written the declaration file for a library, the next step is to publish it.

At this point, there are two options:

  • Bundle the declaration file with the source code/with the NPM package
  • Publish the declaration file to@typesBelow/post to @types Organization on NPM

Of the two options, the first is preferred. Keeping the declarative file with the source code eliminates the need for a separate declarative library to rely on, and also ensures that the declarative file version is consistent with the source code version.

Only when you are adding a type declaration file to someone else’s repository, but the original author is unwilling to incorporate the Pull request, you need to use the second option and publish the declaration file under @types.

Put the declaration file together with the source code

If the declaration file is automatically generated by TSC, then no additional configuration is required except that the compiled file is also published to NPM and the user can get the type prompt.

If the declaration file is manually written, one of the following conditions must be met for it to be correctly identified:

  • topackage.jsonIn thetypestypingsField specifies a type declaration file address
  • In the project root directory, write oneindex.d.tsfile
  • For entry files (package.jsonIn themainField), write a name with a different suffix.d.tsfile

The first is to specify a type declaration file address for the types or typings fields in package.json. Such as:

{
    "name": "foo"."version": "1.0.0"."main": "lib/index.js"."types": "foo.d.ts",}Copy the code

With types specified to foo.d.ts, when importing the library, it will look for foo.d.ts as the library’s type declaration file.

“Typings” is the same as “types”, just another way of writing it.

If types or typings are not specified, the index.d.ts file is looked for in the root directory as the type declaration file for the library.

If the index.d.ts file is not found, the entry file (specified by the main field in package.json) is looked for a.d.ts file with the same name and a different suffix.

For example, package.json looks like this:

{
    "name": "foo"."version": "1.0.0"."main": "lib/index.js"
}
Copy the code

The types or typings field in package.json is identified first. If it does not, the index.d.ts file is looked for. If it does not, then the lib/index.d.ts file is looked for. If lib/index.d.ts does not exist, it is considered a library that does not provide type declarations.

To support the import of submodules, such as import bar from ‘foo/lib/bar’, you need to write an additional type declaration file lib/bar.d.ts or lib/bar/index.d.ts, which is similar to the automatic generation of declaration files. There are multiple type declarations in a library.

Publish the declaration file to@types

If you are adding a type declaration file to someone else’s repository, but the original author is unwilling to incorporate the Pull request, you need to publish the declaration file under @types.

Unlike regular NPM modules, @types is managed uniformly by DefinitelyTyped. To publish the declaration file under @types, create a pull-request for DefinitelyTyped with the type declaration file, test code, tsconfig.json, etc.

Pull-requests need to meet their specifications and pass tests before they can be merged and then automatically published under @types.