编程知识 cdmana.com

Typescript declaration file details

brief introduction

The declaration file is based on .d.ts A file with a suffix , Developers write type declarations in declaration files ,TypeScript Type check according to the content of the declaration file .( Note that it is better not to have the same name in the same directory .ts Document and .d.ts, for example lib.ts and lib.d.ts, Otherwise, the module system cannot load the module only according to the file name )

Why do you need a declaration file ? We know TypeScript Type checking according to type declaration , But in some cases, there may be no type declaration :

  • Third party package , Because the third-party package is JavaScript grammar , Instead of TypeScript, There is no type .
  • Host environment extension , As some hybrid Environmental Science , stay window There are some... Under the variable bridge Interface , These interfaces have no type declarations .

If there is no type declaration , Using variables 、 Call function 、 When instantiating a class, you can't pass TypeScript Type check of .

The declaration document is aimed at these situations , The developer writes the type declaration of the third-party module in the declaration file / The type declaration of the host environment . Give Way TypeScript Type checking can be carried out normally .

besides , Declaration files can also be imported , Use the type exposed in it to define .

All in all , Declaration files are used in two ways :

  • Passed import Import , Use the type definitions and variable declarations exposed in it .
  • Associated with related modules , Type declarations for modules .

For the second usage , How do declaration files relate to related modules ?

For example, there is a third-party name "foo", that TypeScript Will be in node_modules/foo According to its package.json Of types and typing The declaration file found by the field search declaration file is used as the declaration file of the module ;TypeScript Will also be in node_modules/@types/foo/ Find the declaration file in the directory , If you can find it, it will be regarded as foo Module declaration file ;TypeScript We will also find in our project .d.ts file , If you encounter declare module 'foo' sentence , The declaration is used as foo Module declaration .

To sum up ,TypeScript The specified declaration file will be read in a specific directory .

  • In internal projects ,TypeScript Will read tsconfig.json File collection in , The declaration file in it will be processed .
  • Read node_modules Of each third party package in package.json Of types perhaps typing Specified file .
  • Read @types The declaration file of the package with the same name in the directory .

The code in the declaration file will not appear in the final compilation result , After compilation, the converted JavaScript Output the code to "outDir" Option in the specified directory , And the .ts Declaration of the value used in the module All output to "declarationDir" In the specified directory .

And in the .ts Declaration statement in file , It will be removed after compilation , Such as

declare let a: number;

export default a;
 Copy code 

Will be compiled as

"use strict";
exports.__esModule = true;
exports["default"] = a;
 Copy code 

TypeScript The compilation process will not only TypeScript Grammar translates into ES6/ES5, It will also be in the code .ts The type of value used in the file is output to the specified declaration file . If you need to implement a library project , This function is very useful , Because projects that use your library can use these declaration files directly , Instead of writing a declaration file for your library .

grammar

Content

TypeScript The declaration in creates one of the following three entities : Namespace , Type or value .

The namespace is eventually compiled into global variables , Therefore, we can also think that the declaration file actually creates two entities of type and value . That is, define a type or declare a value .

//  type   Interface 
interface Person {name: string;}

//  type   Type the alias 
type Fruit = {size: number};

//  value   Variable 
declare let a: number;

//  value   function 
declare function log(message: string): void;

//  value   class 
declare class Person {name: string;}

//  value   enumeration 
declare enum Color {Red, Green}

//  value   Namespace 
declare namespace person {let name: string;}
 Copy code 

We notice that types can be defined directly , But the declaration of value needs the help of declare keyword , This is because if not declare keyword , Value declaration and initialization are together , Such as

let a: number;

//  Compiled into 
var a;
 Copy code 

But the result of compilation is to remove all declaration statements , Keep the initialized part , The content in the declaration file is only for declaration , So it needs to pass declare To mark , This is just a declaration statement , Just remove it when compiling .

TypeScript It is also necessary to declare a value in the constraint declaration file declare, Otherwise, it will be considered that there is initialization content , To report a mistake .

// foo.d.ts
let a: number = 1; // error TS1039: Initializers are not allowed in ambient contexts.
 Copy code 

declare It is also allowed to appear in .ts In file , But I don't usually do that ,.ts Use... Directly in the document let/const/function/class You can declare and initialize a variable . also .ts The file will also be removed after compilation declare The sentence of , So no need declare sentence .

Be careful ,declare Multiple variables with the same name will conflict

declare let foo: number; // error TS2451: Cannot redeclare block-scoped variable 'a'.

declare let foo: number; // error TS2451: Cannot redeclare block-scoped variable 'a'.
 Copy code 

Besides using declare Declare a value ,declare It can also be used to declare a module and a global plug-in , Both are used to declare third-party packages in specific scenarios .

declare module Used to type declare a third-party module , For example, there is a third-party package foo, No type declaration . We can implement a declaration file in our project to make TypeScript The module type can be identified :foo.d.ts

// foo.d.ts
declare module 'foo' {
    export let size: number;
}
 Copy code 

Then we can use :

import foo from 'foo';

console.log(foo.size);
 Copy code 

declare module In addition to being used to declare types to a module , It can also be used to implement the declaration of module plug-ins . This will be introduced in the following sections .

declare global It is used to declare the third-party package that extends the global , The following section introduces .

modularization

The module of grammar

The modular syntax and... Of the declaration file .ts Module is similar , Slightly different in some details ..ts The exported module (typescript The type will be determined according to the exported module ),.d.ts What is exported is the definition of the type and the declared value .

Declaration files can export types , You can also export the declaration of the value

// index.d.ts

//  Export value declaration 
export let a: number;

//  Export type 
export interface Person {
    name: string;
};
 Copy code 

Declaration file can introduce other declaration files , You can even introduce other .ts file ( because .ts Files may also export types )

// Person.d.ts
export default interface Person {name: string}

// index.d.ts
import Person from './person';

export let p: Person;
 Copy code 

If the declaration file is not exported , The default is globally accessible

// person.d.ts
interface Person {name: string}
declare let p: Person;

// index.ts
let p1: Person = {name: 'Sam'};
console.log(p);
 Copy code 

If you use module export Syntax (ESM/CommJS/UMD), It does not resolve to global ( Of course UMD It can still be accessed globally ).

// ESM

interface Person {name: string}

export let p: Person;

export default Person;
 Copy code 
// CommonJS
interface Person {name: string}

declare let p: Person;

export = p;
 Copy code 
// UMD
interface Person {name: string}

declare let p: Person;

export = p;
export as namespace p;
 Copy code 

Be careful :UMD package export as namespace Syntax can only appear in the declaration file .

Three slashes command

The three slash instruction in the declaration file , Used to control the compilation process .

The triple slash command can only be placed at the top of the file containing it .

If specified --noResove Compilation options , The precompile process ignores the triple slash instruction .

reference

reference Directive is used to indicate the dependency of the declaration file .

/// <reference path="..." /> Used to tell the compiler about other declaration files . Compiler preprocessing When will path The specified declaration file is added . The path is relative to the file itself . Reference a file that does not exist or reference itself , Will report a mistake .

/// <reference types="node" /> Used to tell the compiler that it depends on node_modules/@types/node/index.d.ts. If your project relies on @types Some declaration files in , Then this instruction will be automatically added to the declaration file after compilation , To indicate that the declaration file in your project depends on @types Relevant declaration documents in .

/// <reference no-default-lib="true"/>,

This involves two compilation options ,--noLib, After setting this compilation option , The compiler ignores the default library , The default library is installed TypeScript It was introduced automatically when , This file contains JavaScript Runtime ( Such as window) as well as DOM There are various common environmental declarations in . But if your project running environment is very different from the standard browser based Runtime Environment , You may need to exclude the default library , Once you eliminate the default lib.d.ts file , You can include a file with a similar name in the compilation context ,TypeScript The file will be extracted for type checking .

Another compilation option is --skipDefaultLibCheck This option causes the compiler to ignore the inclusion of /// <reference no-default-lib="true"/> Declaration file of the directive . You will notice that there will be this three slash instruction at the top of the default library , So if --skipDefaultLibCheck Compilation options , The default library will also be ignored .

amd-module

amd-module Related instructions are used to control packaging to amd Module compilation process

///<amd-module name='NamedModule'/> This instruction is used to tell the compiler to package as AMD Pass in the module name of the module ( The default is anonymous )

///<amd-module name='NamedModule'/>
export class C {
}
 Copy code 

The result is

define("NamedModule", ["require", "exports"], function (require, exports) {
    var C = (function () {
        function C() {
        }
        return C;
    })();
    exports.C = C;
});
 Copy code 

scene

Here we call our project code “ Internal projects ”, Third party modules introduced , Include npm Introduced and script Introduced , be called “ External modules ”.

1. Write a declaration file for the internal project in the internal project

In my own project , Write a declaration file for your module , For example, the type shared by multiple modules , You can write a declaration file . This scenario is usually unnecessary , Usually some .ts File export declaration , Other module reference declarations .

2. Write a declaration file to a third-party package

Writing declaration documents for third-party packages is divided into Write declaration files for third-party packages in internal projects and Write a declaration file to the external module in the external module .

Write declaration files for third-party packages in internal projects : If the third-party package does not TS Declaration file , In order to ensure that the third-party package can pass the type check , Also for the safe use of third-party packages , You need to write the declaration file of the third-party package in the internal project .

Write a declaration file to the external module in the external module : If you are the author of a third-party library , Whether you use TypeScript Development Library , Declaration files should be provided to use TypeScript Develop projects that make better use of your library , Then you need to write your statement file .

The syntax of the declaration file in both cases is similar , There are only differences in individual declaration syntax and file processing :

  1. When an internal project writes a declaration file to a third-party package , With .d.ts Just name it , And then in tsconfig.json Medium files and include The configuration can be included in the file , The declaration file of the external module needs to be packaged into the output directory , And in package.json Medium type Field specifies the location of the declaration file ; Or upload to @types/<moduleName> in , The user passes through npm install @types/<moduleName> Installation statement file .redux It's just tsconfig.json Specified in declarationDir by ./types,TypeScript Will package all the project declarations into this directory , The directory structure is the same as the source code , then redux All modules are exported at the entrance of the source code , therefore types There is also an entry declaration file under the directory index.d.ts, The exported module contains all the declarations ,redux stay package.json It is specified in types Field ( perhaps typings Field ) Declaration file for entry :./types/index.d.ts. In this way, the declaration file of the interface is automatically generated .
  2. When internal projects write declaration documents to third parties , If it's through npm Module introduction mode , Such as import moduleName from 'path'; You have to go through declare module '<moduleName>' Syntax to declare modules . The declaration files of external modules are normal type export syntax ( Such as export defaultexport = etc. ), If the declaration file is in @types in , The declaration file with the same name as the module will be declared as the type of the module ; If the declaration file is in a third-party package , then TypeScript The module takes it as the module declaration of the third-party package module , When users import and use this module ,TypeScript Type prompt and type check according to the corresponding declaration file .

According to the type of third-party package, it can be divided into several types

Third party Library of global variables

We know that if we don't use module export Syntax , The default declarations of the declaration file are global .

declare namespace person {
    let name: string
}
 Copy code 

perhaps

interface Person {
    name: string;
}

declare let person: Person;
 Copy code 

Use :

console.log(person.name);
 Copy code 

Modify the declaration of the third-party library of the module of the global variable

If a third-party package modifies a global module ( This third-party package is the plug-in of this global module ), The declaration file of this third-party package is based on the declaration of the global module , There are different ways to declare

If the global module uses a namespace declaration

declare namespace person {
    let name: string
}
 Copy code 

According to the declaration of namespace, the principle of merging , The plug-in module can declare

declare namespace person {
  	//  Expanded age attribute 
    let age: number;
}
 Copy code 

If the global module uses a global variable declaration

interface Person {
    name: string;
}

declare let person: Person;
 Copy code 

According to the declaration and merging principle of the interface , The plug-in module can declare

interface Person {
  	//  Expanded age attribute 
    age: number;
}
 Copy code 

The declaration method of the plug-in module of the above global module can be applied to the following scenarios :

  • Internal projects use plug-ins , But the plug-in does not have a declaration file , We can implement the declaration file ourselves in the internal project .
  • Write a declaration file for the plug-in module and publish it to @types.

If you are the author of the plug-in module , You want to reference the global module in the project and output the extended type to the declaration file , For other projects . It can be realized in this way

// plugin/index.ts

//  Note that this statement will make TypeScript Output the type declaration file 
declare global {
  	//  Suppose that the global module uses global variables to declare 
    interface Person {
        age: number
    }
}

console.log(person.age);

export {};
 Copy code 

Be careful ,declare global It can also be written in the declaration file , But add... To the tail export {} Or other module export statements , Otherwise, an error will be reported . in addition declare global Written in the declaration file , After compilation, it will not be output to the declaration file .

modify window

window The type is interface Window {...}, Declare in default library , If you want to expand window Variable ( As some hybrid Environmental Science ) It can be realized in this way

// window.d.ts

//  Declaration of merger 	
interface Window {
		bridge: {log(): void} 
}

//  perhaps 
declare global {
    interface Window {
        bridge: {log(): void} 
    }
}
 Copy code 

perhaps

// index.ts

declare global {
    interface Window {
        bridge: {log(): void} 
    }
}

window.bridge = {log() {}}

export {};
 Copy code 

ESM and CommonJS

For third parties ESM perhaps CommonJS Module write declaration file , Use ESM Export or CommonJS Module syntax can be exported , No matter what module form the third-party package is .

Look at the following example

interface Person {
    name: string;
}

declare let person: Person;
 
export = person;
//  You can also use export default person;
 Copy code 
import person from 'person';

console.log(person.name);
 Copy code 

The above declaration file is placed in node_modules/@types/person/index.d.ts in , Or put it in node_modules/person/package.json Of types perhaps typings Field .

If you declare in your own project , You should use declare module Realization

declare module 'person' {
    export let name: string;
}
 Copy code 

UMD

UMD modular , stay CommonJS Add... To the statement export as namespace ModuleName; Sentence can be used .

Look at the following ESM Example

// node_modules/@types/person/index.d.ts
interface Person {
    name: string;
}

declare let person: Person;

export default person;

export as namespace person;
 Copy code 

Can pass import Import to access

// src/index.ts
import person from 'person';

console.log(person.name);
 Copy code 

It can also be accessed globally

// src/index.ts

//  Note that if you use ESM export , Access first when using the global defalut attribute .
console.log(person.default.name);
 Copy code 

Here is CommonJS Example

// node_modules/@types/person/index.d.ts


interface Person {
    name: string;
}

declare let person: Person;

export default person;

export as namespace person;
 Copy code 

Can pass import Introduce access

// src/index.ts


import person from 'person';

console.log(person.name);
 Copy code 

It can also be accessed globally

// src/index.ts

console.log(person.name);
 Copy code 

Module plug in

We mentioned above ,declare module Not only can it be used to declare types to a third-party module , It can also be used to declare types for plug-in modules of third-party modules .

// types/moment-plugin/index.d.ts

//  If moment Defined as UMD, There is no need to introduce , Directly able to use 
import * as moment from 'moment';

declare module 'moment' {
    export function foo(): moment.CalendarKey;
}


// src/index.ts

import * as moment from 'moment';
import 'moment-plugin';

moment.foo();
 Copy code 

For example, as redux Of the plug-in redux-thunk The statement of extend-redux.d.ts, That's the statement

// node_modules/redux-thunk/extend-redux.d.ts

declare module 'redux' {
		// declaration code......
}
 Copy code 

版权声明
本文为[Hand cramp after brushing questions]所创,转载请带上原文链接,感谢
https://cdmana.com/2022/134/202205141312230445.html

Scroll to Top